January 18, 2018
REST APIs: A recommended approach
There is no such thing as best approach when it comes to designing APIs because of their subjective nature. Every organisation implements them as per their own requirements. Having said that, there are still some recommended ways in which we can make APIs more user friendly and easier to implement them. We here, at Bonzai, deal with a ton of APIs to power our Workflow, and we try to standardise them as much as possible.
Below are few points which we follow at Bonzai when designing our REST APIs :
Determining the objective
The first and the foremost thing is to determine the objective of the API I.e you have to start by asking yourself the question that, what are you going to achieve by the implementation of this API? The answer to this very question would help you separating your API into logical resources. Now these resources are manipulated using HTTP requests where these requests use any of the following annotation : GET, POST, PUT, PATCH or DELETE - depending on the objective of the resource. One should be very clear about these annotations in terms of their functionality. It is important because one would not know that s/he should use POST, PUT, DELETE instead of GET to alter the state of a resource unless they know what these methods do.
Naming of the resource
To a non programmer it may seem that the hardest part of a programmer’s job is to implement the complex logics and algos, but in reality for them naming a variable or a method is the harder. Having said that, we try our best to keep naming as logical and readable as possible. The widely used approach regarding the naming of resources is: Use nouns instead of verbs. In our case, for example, we would retrieve information of an Ad by using a resource named /ads/{adId} instead of /getAds/{adId}. Not that I have used plural for my resource Ad which makes it all the more readable i.e it says from all the collection of Ad give me the info of the with id = {adId}. : This may seem objectionable to some who are aware of ‘sub-resources’ because if you use sub-resources (another good and recommended approach) then our above example would become something like /ads/.
Using Sub-resources
It is always a good idea to club your similar resources under a group. For example, in our case we can have multiple Ads under a campaign, and a brand can have multiple campaigns in it. So for such a resource structure grouping would come handy. How we are using sub-resource to address such structure is: /brands/{brandId}/campaigns/{campaignId}/ads/{adId}. In plain English this would translate roughly to : From all available brands fetch the one with {brandId} - From all the available campaigns under this {brandId} fetch the one with {campaignId} - From all the ads available under this campaign fetch the ad with {adId}. Quite intuitive, isn’t it?
Specifying serialisation formats
We know that APIs provide an interface to the client to communicate with the server (where are our resources are hosted). In order to facilitate this communication both client and server has to use a serialisation format (or simply data format). There are many such formats available such as JSON, XML, protobuf, YAML etc. We prefer JSON which is arguably the most famous serialisation format among all the other. As a best practice this serialisation format should be specified in the HTTP header. Content-type defines the request format and Accept defines the list of acceptable response formats.
Specifying behavioural indicators in the API requests
There may be scenarios where a client may tell the server to give it a specific response by providing indicative parameters in the request. For example : If client wants the list of Ads in a sorted manner then it can specify a parameter named as order and give this parameter a value which would be interpreted by the server and the response would be manipulated accordingly (value could be ascending or descending). Another such indicative parameter could be sortBy and it can take values such as lastUpdated, Id, names etc. A client may also need to filter the response of the API, this can be specified in the filter. Pagination can also be achieved by specifying an indicative parameter like limit and offset.
Versioning APIs
As they change is the only constant and this also applies to our REST APIs. So how do we manage these ever changing (or evolving) APIs? Answer is by versioning them. It often is the case that there is a single server powering multiple clients such as Web, Mobile (IOS, Android), Tablets etc and if we change an API response then all the clients has to be in sync with this change and that can be a challenging task sometimes. In order to keep supporting all the client we should introduced the changes by versioning the APIs. One common approach is to specify a letter “v” in the URL like server_end_point/api/v1.
Documentation
We all can agree on the importance of documentation and an API is as good as its documentation. We should always do proper documentation of all our APIs. There are a lot of tools available to do API documentation and we use Swagger to achieve the same. Although we are still in process of introducing this tool into our legacy services but all our new services are using this tool and it is quite handy. It provides interactive documentation of APIs which helps client to understand them better before starting their implementations.
Handling Errors
If it has to then an API should always fail by giving a proper error message to the client. It should always try not to give generic error messages and leave the client to guess it on its own by beating around the bush. This precisely is the reason why we have our custom error codes which convey a clear message. So along with an HTTP status code a server should have its own error payload. Below are the most common HTTP codes and their meanings:
- 1.x.x - Informational
- 2.x.x - Success
- 3.x.x - Redirection
- 4.x.x - Client Error
- 5.x.x - Server Error
Please visit https://httpstatuses.com/ for the exhaustive list of the HTTP statuses and their elaborative meanings.