DEPRECATED - see https://crukorg.github.io/engineering-guidebook/ for the latest guiderails and standards

Cancer Research UK Web API Standards

Guidelines

This document provides guidelines and examples for Cancer Research UK Web APIs, encouraging consistency, maintainability, and best practices across applications. Cancer Research UK APIs aim to balance a truly RESTful API interface with a positive developer experience (DX).

Note: This document is based on the White House Web API Standards

This document borrows heavily from:

Pragmatic REST

These guidelines aim to support a truly RESTful API. Here are a few exceptions:

RESTful URLs

General guidelines for RESTful URLs

  • A URL identifies a resource.
  • URLs should include nouns, not verbs.
  • Use plural nouns only for consistency (no singular nouns).
  • Use HTTP verbs (GET, POST, PUT, DELETE) to operate on the collections and elements.
  • You shouldn’t need to go deeper than resource/identifier/resource.
  • Put the version number at the base of your URL, for example http://example.com/v1/path/to/resource.
  • URL v. header:
    • If it changes the logic you write to handle the response, put it in the URL.
    • If it doesn’t change the logic for each response, like OAuth info, put it in the header.
  • Specify optional fields in a comma separated list.
  • Formats should be in the form of api/v2/resource/{id}.json

Good URL examples

Bad URL examples

HTTP Verbs

HTTP verbs, or methods, should be used in compliance with their definitions under the HTTP/1.1 standard. The action taken on the representation will be contextual to the media type being worked on and its current state. Here's an example of how HTTP verbs map to create, read, update, delete operations in a particular context:

HTTP METHOD POST GET PUT PATCH DELETE
CRUD OP CREATE READ UPDATE PARTIAL UPDATE DELETE
/dogs Create new dogs List dogs Bulk update Bulk update Delete all dogs
/dogs/1234 Error Show Bo If exists, update whole Bo; If not, error If exists, update Bo fields specified; If not, error Delete Bo

(Edited example from Web API Design, by Brian Mulloy, Apigee.)

Responses

  • No values in keys
  • No internal-specific names (e.g. "node" and "taxonomy term")
  • Metadata should only contain direct properties of the response set, not properties of the members of the response set

Good examples

No values in keys:

"tags": [
  {"id": "125", "name": "Environment"},
  {"id": "834", "name": "Water Quality"}
],

Bad examples

Values in keys:

"tags": [
  {"125": "Environment"},
  {"834": "Water Quality"}
],

Error handling

Error responses should include a common HTTP status code and a message for the end-user (when appropriate). For example:

{
  "status" : 400,
  "userMessage" : "This is a message that can be passed along to end-users, if needed."
}

Use the following simple, common response codes indicating success (2xx), failure due to client-side problem (4xx), or failure due to server-side problem (5xx):

  • 200 - OK. The action the client has requested has successfully been carried out.
  • 201 - Created. A new resource has successfully been created at the client's request.
  • 400 - Bad Request. The request can not or will not be processed due to something that is perceived to be a client error (e.g. malformed request body).
  • 404 - Not Found. The resource or path could not be found.
  • 409 - Conflict. The client has attempted to put a resource in to an impossible or inconsistent state (e.g. data in the request body that does not pass validation).
  • 500 - Internal Server Error. The server encountered an unexpected condition which prevented it from fulfilling the request.

Versions

  • Never release an API without a version number.
  • Versions should be integers, not decimal numbers, prefixed with ‘v’. For example:
    • Good: v1, v2, v3
    • Bad: v-1.1, v1.2, 1.3
  • Maintain APIs at least one version back.

Record limits

Information about record limits and total available count should also be included in the response. Example:

{
    "metadata": {
        "resultset": {
            "count": 227,
            "offset": 25,
            "limit": 25
        }
    },
    "results": []
}

Request & Response Examples

API Resources

GET /magazines

Example: http://example.cruk.org/api/v1/magazines.json

Response body:

{
    "metadata": {
        "resultset": {
            "count": 123,
            "offset": 0,
            "limit": 10
        }
    },
    "results": [
        {
            "id": "1234",
            "type": "magazine",
            "title": "Public Water Systems",
            "tags": [
                {"id": "125", "name": "Environment"},
                {"id": "834", "name": "Water Quality"}
            ],
            "created": "1231621302"
        },
        {
            "id": 2351,
            "type": "magazine",
            "title": "Public Schools",
            "tags": [
                {"id": "125", "name": "Elementary"},
                {"id": "834", "name": "Charter Schools"}
            ],
            "created": "126251302"
        }
        {
            "id": 2351,
            "type": "magazine",
            "title": "Public Schools",
            "tags": [
                {"id": "125", "name": "Pre-school"},
            ],
            "created": "126251302"
        }
    ]
}

GET /magazines/[id]

Example: http://example.cruk.org/api/v1/magazines/[id].json

Response body:

{
    "id": "1234",
    "type": "magazine",
    "title": "Public Water Systems",
    "tags": [
        {"id": "125", "name": "Environment"},
        {"id": "834", "name": "Water Quality"}
    ],
    "created": "1231621302"
}

POST /magazines/[id]/articles

Example: Create – POST http://example.cruk.org/api/v1/magazines/[id]/articles

Request body:

[
    {
        "title": "Raising Revenue",
        "author_first_name": "Jane",
        "author_last_name": "Smith",
        "author_email": "jane.smith@example.cruk.org",
        "year": "2012",
        "month": "August",
        "day": "18",
        "text": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam eget ante ut augue scelerisque ornare. Aliquam tempus rhoncus quam vel luctus. Sed scelerisque fermentum fringilla. Suspendisse tincidunt nisl a metus feugiat vitae vestibulum enim vulputate. Quisque vehicula dictum elit, vitae cursus libero auctor sed. Vestibulum fermentum elementum nunc. Proin aliquam erat in turpis vehicula sit amet tristique lorem blandit. Nam augue est, bibendum et ultrices non, interdum in est. Quisque gravida orci lobortis... "
    }
]

JSONP

JSONP is easiest explained with an example. Here's one from StackOverflow:

Say you're on domain abc.com, and you want to make a request to domain xyz.com. To do so, you need to cross domain boundaries, a no-no in most of browserland.

The one item that bypasses this limitation is <script> tags. When you use a script tag, the domain limitation is ignored, but under normal circumstances, you can't really DO anything with the results, the script just gets evaluated.

Enter JSONP. When you make your request to a server that is JSONP enabled, you pass a special parameter that tells the server a little bit about your page. That way, the server is able to nicely wrap up its response in a way that your page can handle.

For example, say the server expects a parameter called "callback" to enable its JSONP capabilities. Then your request would look like:

    http://www.xyz.com/sample.aspx?callback=mycallback

Without JSONP, this might return some basic javascript object, like so:

    { foo: 'bar' }

However, with JSONP, when the server receives the "callback" parameter, it wraps up the result a little differently, returning something like this:

    mycallback({ foo: 'bar' });

As you can see, it will now invoke the method you specified. So, in your page, you define the callback function:

    mycallback = function(data){
        alert(data.foo);
    };

http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about?answertab=votes#tab-top