REST API Design

Resources

See also: HTTP Response Codes are defined in W3C RFC2616. jsonapi.org has info on standard ways of representing data.

The way APIs are designed is important because:

There is no real right or wrong way. Just avoid dogma and be pragmatic.

URIs

http://api/Customers
http://api/Invoices

Use identifiers to locate individual items in URIs. Doesn’t have to be internal key as long as it is unique.

http://api/Customers/123
http://api/Games/halo-3
http://api/Invoices/2014-01-01

HTTP Verbs

Resource GET POST PUT DELETE
/customers Get list Create item Update batch Error
/customers/123 Get item Error Update item Delete item

What to Return

Resource GET POST PUT DELETE
/customers List New item Status code Error code
/customers/123 Item Error code Updated item Status code

Status Codes

The HTTP specification defines the status codes. Below are some common ones:

Code Description
200 OK
201 Created
202 Accepted
302 Found
304 Not Modified
307 Temp Redirect
308 Perm Redirect
400 Bad Request
401 Not Authorized
403 Forbidden
404 Not Found
405 Method Not Allowed
409 Conflict
500 Internal Error

Limit the number of returned status codes to keep the API simple. Consider these as a bare minimum:

Code Description Real Story
200 OK It Worked
400 Bad Request Your Bad
500 Internal Error Our Bad

The following could be useful for clients:

Code Description
201 Created
304 Not Modified
401 Unauthorized
403 Forbidden
404 Not Found

Sample of Sensible Defaults:

Code Description
200 OK Successful request
204 No Content File contains no data
301 Moved Permanently The requested resource has permanently moved to another address
401 Not Authorized Athorization needed to access resource
403 Forbidden
404 Not Found The requested resource cannot be found
408 Request Timeout. The Request timed out
500 Server Error

Associations

Using URIs

http://api/Customers/123/Invoices http://api/Games/halo-3/Ratings
http://api/Invoices/2014-01-01/Payments

Returns single object or list of objects. The format should be the same as when loading associated objects as when loading them as main resources. A resource may include multiple associations:

http://api/Customers/123/Invoices http://api/Customers/123/Payments
http://api/Customers/123/Shipments

For more complex needs use query params

http://api/Customers/123/Invoices?date=2014-0101&type=credit

Formatting the Results

{
    "total": 120,
    "results": [
        {"id": 1,...}, ...
    ]
}

Response Formats

http://api/Customers?format=json http://api/Customers.json
http://api/Customers?format=json&callback=foo
Type MIME Type
JSON application/json
XML text/xml
JSONP application/javascript
RSS application/xml+rss
ATOM application/xml+atom

Entity Tags (ETags)

HTTP/1.1 200 OK
Content-Type:application/json; charset=utf-8
Date: 20-01-2014 16:46:52 GMT
ETag: "8934675928367085"
Content-Length: 1024
HTTP/1.1 200 OK
Content-Type:application/json; charset=utf-8
Date: 20-01-2014 16:46:52 GMT
ETag: W/"8934675928367085"
Content-Length: 1024
GET /api/customers/123 HTTP/1.1
Accept: application/json
Host: localhost:8863
If-None-Match: "8934675928367085"
HTTP/1.1 304 Not Modified
PUT /api/customers/123 HTTP/1.1
Accept: application/json
Host: localhost:8863
If-Match: "8934675928367085"
...

Paging

http://api/games/?page=2&pageSize=50

{
    "total": 1598,
    "next": "http://api/Invoices/?page=3",
    "prev": "http://api/Invoices/?page=1",
    "results": [...]
}

Partials

Non-Resource APIs

http:/&/api/calculateTax?state=GA&total=149.90

API Versioning

Versioning Styles

Versioning using the URI path

http://api.attracs.com/v1/Customers?type=Current&id=123
http://api.attracs.com/v2/CurrentCustomers/123

Versioning using Query Param

http://api.attracs.com/Customers
http://api.attracs.com/Customers?v=2.1

Versioning using Content Negotiation

Use custom MIME types in header. Standard indicates you should use “vnd.” (meaning vendor) as prefix to the MIME type

GET /Customers/123
HOST:http://api.attracs.com
Accept: application/vnd.amc.v1.customer

Accept: application/vnd.amc.v1.customer.json

Versioning using Custom Header

GET /Customers/123
HOST:http://api.attracs.com
x-AMC-Version: 2.1

x-AMC-Version: 2014-01-01

It’s hard to generally say which style is best. Content Negotiation and Custom Header styles are popular but adds some complexity. Versioning with URI components are still more common. These are typically easier to implement but can add more technical debt.

The API should support versioning from the first release.

Resource Versioning

Securing the API

You need to secure the API if:

Threats:

Securing the server infrastructure is another topic

Secure In-Transit (protecting data when it goes across the wire). Using SSL is typically worth the expense

Securing the API consists of Cross-Origin Security and Authorization/Authentication.

Cross Domain Security

Should you allow calls from separate domains? If internal, probably not. If public facing you might need to support cross-domain.

Two Approaches:

JSONP
function updateUser(data) {
    //use Data
}


GET /api/games?callback=updateGames HTTP/1.1
Accept: application/javascript
Host: localhost:8863

-> updateUser({total=1, results=[...]});

The data sent back from the server will be the same. It will just be wrapped in a function call.

CORS

Authentication

Who is calling the API?

Authentication: Validate a set of credentials to identify an entity (whether virtual or actual).

Authorization: Verification that an entity has right to access a resource or action.

API Keys

User Security/Authenticating Users

If your system has users, how do you verify which user is calling the API?

OAuth

For allowing developer to act as a user in your system but allows you to keep control of accepting credentials

Then trust a 3rd party with a token that represents the API developer. The developer will never receive the user credentials.

Hypermedia

Hypermedia is links for APIs helping developers to know how to use the API Links helps APIs become self-describing Links become the application state

Links

What kinds of links to provide:

{
    totalResults: 1000,
    links: [
        {"href": "/api/v1/games?page=1", "rel": "prevPage" },
        {"href": "/api/v1/games?page=3", "rel": "nextPage" },
        {"href": "/api/v1/games", "rel": "insert" }
    ],
    "results": []
}
{
    links: [
        {"href": "/api/v1/games/2", "rel": "self" },
        {"href": "/api/v1/games/2/rating", "rel": "rating" }
    ],
    "id": 2,
    "name": "Final Fantasy XIII",
    ...
}

Profile Media Types

GET /api/order/123
HOST: http://..
Accept: appliaction/json;profile=http://api.attracs.com/orders

Hypermedia Standards

The standards are emerging None are final

Based on content types with profile media type Content type defines data formatting Profile media type defines the structure of the data Keeps the format and structure/version separated

HAL

content type application/hal+json
content type application/hal+xml
content type application/hal+json;profile=http://api.attracs.com/orders

Resources contains state and links plus embedded resources containing state and links

{
    "_links":{
    "self": {"href": "/games"},
    "next": {"href": "/games?page=2"},
    "find": {"href": "/games{?query}", "templated": true},
    "totalCount": 100,
    "_embedded": {
        "results": [
            {
                "_links": {
                    "self": {"href": "/games/123"},
                    ...
                },
                "price": 30.00,
                "currency": "USD",
                "name": "Halo 3"
            },
            ...
        ]
    }
}

Template Links: Used when URI includes a template (e.g. RFC6570)

Collection + JSON

content type application/vnd.collection+json
content type application/vnd.collection+json;profile=http://foo.com/bar
{
    "collection": {
        "version": "1.0",
        "href": "http://api.attracs.com/Orders",
        "links": [
            { "rel": "feed", "href": "http://api.attracs.com/Orders/rss" }
        ],
        "items": [
            {
                "href": "http://api.attracs.com/Orders/1",
                "data": [
                    { "Id": 1, "Date": "2014-01-01"},
                    ...
                ],
                "links": [
                    {
                        "rel": "customer",
                        "href": "http://api.attracs.com/Orders/1/customer",
                        "prompt": "Show Customer"
                    },
                    ...
                ]
            }
        ]
    }
}

It also includes queries and templates for UI building:

{
    "collection": {
        ...
        "queries": [
            {
                "rel": "search",
                "href": "http://api.attracs.com/Orders/search",
                "prompt": "Find Order",
                "data": [
                    {"name": "search", "value": ""}
                ]
            }
        ],
        "template": {
            "data": [
                {"name": "id", "value": "", "prompt": "Order Id"},
                {"name": "date", "value": "", "prompt": "Order Date"},
                ...
            ]
        }
    }
}