Restful Api’s

Here’s my way, my influences, but feel free to follow any highway…

Naming

Resources are objects, things, so use a noun to specify a resource e.g clients and then use HTTP methods to define actions like get, modify, delete.

If you use verbs like send, get then you are falling into the Remote Procedure Call trap. RPC style endpoints are notorious for specifying the function in the URL e.g. /getClientNameById

Resource names should be plural as this is the most widely adopted approach:

/clients not /client /getClients /user/client

Unless there can only ever be one (0-1, exists or not) e.g. users/1/avatar.

HTTP Methods

Often call HTTP verbs are POST, GET, PUT, PATCH, DELETE and OPTIONS.

Considering the resource (Entity) life cycle can help you determine which HTTP methods to allow, in particular where it is stored, db, file and if it can be archived.

Patch & Put

  • Favor Patch for updating resources, use put when resources are replaced not updated e.g. binary documents.
  • Do not use Put to create new items.

    Be aware that PUT does a complete overwrite of the data,
    it is a request to replace a resource, PATCH is a request to
    update part or all of it i.e. a new version.

The difference between the PUT and PATCH requests is reflected in the way the server processes the enclosed entity to modify the resource identified by the Request-URI.

In a PUT request, the enclosed entity is considered to be a modified version of the resource stored on the origin server, and the client is requesting that the stored version be replaced. With PATCH, however, the enclosed entity contains a set of instructions describing how a resource currently residing on the origin server should be modified to produce a new version.

PATCH is less likely to have side affects when a resource is updated frequently because you only update the things that change, whereas PUT will update fields that have not changed with the values retrieved and if another request happens after the retrieval you will reset them. You can/should guard against this at the repository level by checking a resource version and invaliding the update.

Do not use ‘Put’ to create new items, use ‘Post’.

If barn 11 does not exist then PUT /barn/11 should return a 404 and message saying I couldn’t modify it because it doesn’t exist.

Options Verb

The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.

A pre-flight options request is triggered by some CORS requests, see the section on CORS for more details.

Response Codes

Always return a response code for each request.

HTTP/1.1 200 OK
Date: Sun, 18 Oct 2012 10:36:20 GMT
Server: Apache/2.2.14 (Win32)
Content-Length: 230
Connection: Closed
Content-Type: text/html; charset=iso-8859-1
Response Codes

If I did GET or an update, 200 is okay.

However, if I do POST or a PUT and something is created, use 201. Tell me explicitly that the new object was created.

If I do PUT or a PATCH and nothing’s modified, return 304 Not Modified.

If I send the wrong data or use the wrong format request, return 400 Bad Request.

If I haven’t logged in or I sent an invalid auth token, return 401 Not Authorised.

If I try to do something I’m not allowed to do, 403 Forbidden.

404 if the object never existed, or it’s not there. Technically, you want to use the status code for Gone namely, 410 if it once existed, but 404 is traditionally Not Found. DO NOT use this when a GET returns no rows/result, it’s still a 200.

405 represents Method Not Allowed. This goes beyond Forbidden 403 and says, “Hey, you can’t do this for example, delete an object. If you try a different method, that would work.”

415 corresponds to Unsupported Media Type, for example if I request XML but you only support JSON.

Formatting Content

I follow the conventions in the { json;api }  specification.

// Articles with fields title, body and author.
GET /articles?include=author&fields[articles]=title,body,author

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON:API paints my bikeshed!",
      "body": "The shortest article. Ever."
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  }]
}

The spec has matured overtime and specifies some of our important practices showing us how to format the negotiation between the client and server:

  • Relationships
  • Hypermedia
  • Pagination
  • Filtering
  • Sparse fields
  • Errors

Relationships (Related Resources)

Multiple related resources can be requested in a comma-separated list:

GET /articles/1?include=author,comments.author HTTP/1.1
Accept: application/vnd.api+json

In order to request resources related to other resources, a dot-separated path for each relationship name can be specified:

GET /articles/1?include=comments.author HTTP/1.1
Accept: application/vnd.api+json

To update a related resource include the resource as a relation in the PATCH e.g. request will update the author relationship of an article:

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "id": "1",
    "relationships": {
      "author": {
        "data": { "type": "people", "id": "1" }
      }
    }
  }
}

Hypermedia

Hypermedia As The Engine Of Application State.

Richardson Maturity Model
Richardson Maturity Model

The term “hypermedia” was coined back in 1965 by Ted Nelson, and over the years has dominated the technology industry. Hypermedia, in its most basic sense is an extension of hypertext – something you may recognise from HTML.

Hypertext is essentially text that is written in a structured format and contains relationships to other objects via links.

Hypermedia is just an extension of the term hypertext, hypermedia includes images, video, audio, text, and links.

In a REST API, this means that your API is able to function similarly to a web page, providing the user with guidance on what type of content they can retrieve, or what actions they can perform, as well as the appropriate links to do so.

This in page guidance via links means that your clients do not need to remember much, they can just request a resource and check the response to see how to work with information provided, take appropriate actions, or access related information.

A good example of this is a client reading your site news only needs a single endpoint https://<site>.com/news.

The response would included all of the related articles and actions which you can change daily without coupling the client to news articles in any way.

Hypermedia can be express as links in a JSON API response:

{
  "data": [{
    "type": "clients",
    "id": "1",
    "attributes": {
      "name": "John"
    },
    "links": {
      "self": "http://example.com/clients/1"
	  "accounts": "http://example.com/clients/1/accounts"	
    },
    "relationships": {
      "accounts": {
        "links": {
            { 
               "name"  : "self",
               "method": "get",
               "href"  : "http://example.com/client/1/relationships/accounts"
            },
            { 
               "name"  : "related",
               "method": "get",
               "href"  : "http://example.com/client/1/accounts"
            },
            { 
               "name"  : "next",
               "method": "get",
               "href"  : "http://example.com/client/1/accounts"
            },
            { 
               "name"  : "prev",
               "method": "get",
               "href"  : "http://example.com/client/1/accounts"
            },
            { 
               "name"  : "close",
               "method": "patch",
               "href"  : "http://example.com/client/1/accounts"
            },
          "next" : "http://example.com/clients/1/accounts?page=3&page_size=2",
          "prev" : "http://example.com/clients/1/accounts?page=1&page_size=2",
          "close": "http://example.com/clients/1/accounts/close"
        },
        "data": [
          { "type": "accounts", "id": "5" },
          { "type": "accounts", "id": "12" },
          { "type": "accounts", "id": "13" }
        ]
      }
    }
  }],
  "included": [{
    "type": "acounts",
    "id": "12",
    "attributes": {
      "type": "Investment"
    }
    "links": {
        { 
           "name"  : "close",
           "method": "patch",
           "href"  : "http://example.com/accounts/12"
        },
        { 
           "name"  : "update",
           "method": "patch",
           "href"  : "http://example.com/accounts/12"
        },
    }
  }]
}

The action links description the content that can be retrieved and the actions that can be performed by user in a response to their request.

This is powerful as it gives the server flexibility to change without breaking the interface with the client.

Use the Accept and Content-Type Headers

We tend to think, “I’m going to build a JSON REST API and it’s going to be awesome.” It works great, until you get that million?dollar customer who needs XML. Then you have to go back and refactor the entire API for this customer. That’s why you should build your API from the start with the ability to add content types in the future.

Give yourself the ability to support multiple specifications without worrying about breaking backward compatibility.

Incoming request may have an entity attached to it. To determine it’s type, server uses the HTTP request header Content-Type. Common content types are:

  • application/json
  • application/xml
  • text/plain
  • text/html
  • image/gif
  • image/jpeg

Similarly, to determine what type of representation is desired at client side, HTTP header ACCEPT is used. It will have one of the values as mentioned for Content-Type above.

Sparse Fields

Sparse fields are key to creating Api’s that can be used by many clients.

If we do not support sparse fields we will force all clients to get the full set of data which will grow we add more functionality, similar to large objects graphs created in our monolith applications.

Use a fieldsTYPE parameter to return only specific fields in the response on a per-type basis.

GET /articles?include=author&fields[articles]=title,body 

Here we want articles objects to have fields title, body and author only.

HTTP/1.1 200 OK
Content-Type: application/vnd.api+json

{
  "data": [{
    "type": "articles",
    "id": "1",
    "attributes": {
      "title": "JSON:API paints my bikeshed!",
      "body": "The shortest article. Ever."
    },
    "relationships": {
      "author": {
        "data": {"id": "42", "type": "people"}
      }
    }
  }]
}

Timeouts

The client is in a better position to tell the server how long it wants to wait before timing out, so generally allow the client to override the default timeout period.

?Timeout=3000

Caching

ETag (entity tag) response header provides a mechanism to cache unchanged resources.

It’s value is an identifier which represents a specific version of the resource. Here’s an example ETag header:

ETag: "version1"

See ETags

Warning

Designing an API – that’s the most difficult part. That’s why you need to spend your time there and say, “Let’s get the design right, and everything else will follow.”

It only takes one tiny little thing, just one mistake in your API that goes to production, to screw things up.

Just like Facebook: they have this issue in production, but it’s in production now and they can’t change it.

Back Burner

  • Filtering, Sorting & Grouping
  • Descriptive Error Messages
  • Automate end-to-end functional testing
  • Cross Origin Resource Sharing.
  • Accelerate functional testing from your CI/CD pipelines
  • Tests to generate realistic load scenarios and security attacks
  • Remove dependencies during testing and development
  • Auto-gen & publish documentation.

Links