RESTful API designing is a very important aspect of any growing application. In this article, I am going to document the key lessons that I learned about designing the RESTful APIs.
Almost all web/mobile applications can be divided into at least two different components.
- The Frontend component: whose responsibility is to give a responsive user experience.
- The backend component: This supports the front-end systems by providing the desired data.
I have been part of applications where we realized later in the project lifecycle that our APIs were not designed properly and hence were a bottleneck in catering to upcoming changes in business requirements. I recently went through an ebook -https://geemus.gitbooks.io/http-api-design/content/en/ which helped me learn the right way of designing RESTful APIs.
REST is an acronym for REpresentational State Transfer. REST provides standard methods that allow users to modify the current state of the application. These APIs use some kind of representation e.g. XML/JSON or even HTML to represent the current state. And this is essentially transferred from the server to the client (or vise Versa).
Designing any form of an API is an art and it takes a lot of effort to get it right. It might sound easy at first, but unless we give due attention to the design process, the API might end up becoming difficult to use or inconsistent. Some of the problems that arise later during projects are related to API versioning, internationalization, changing response type, etc.
The key lessons I learned from the book are:
Lesson 1: Separate concerns by distributing responsibilities in a standard way among various components of a RESTful API
Lesson 2: Choose the correct RESTful API Method
Lesson 3: Identifying the resources (object models) via API path keeps automation and documentation simpler
Lesson 4: Managing breaking changes in APIs by use of Versioning
Lesson 5: Developing cacheable APIs
Let me expand each of these points in detail.
Lesson 1: Separating concerns by distributing responsibilities
A RESTful API Request/Response has different parts, and each part has its own responsibility.
REST methods define the type of operation the API is about to perform
Protocol and server domain
Protocol and server are generally out of control of application developers. Protocols (Http/Https) are the most widely used for web applications.
Server domain is the IP address or public domain of the host
Path to resource
The path to resource signifies a virtual path where the resource can be accessed. The path uniquely identifies a resource or list of similar resources.
Request params are some meta-information that governs the behavior of the API. Such parameters are visible in the browser and can be cached (or saved by the user in favorites).
Headers are the preferred way of sharing meta-information about the API. There can be multiple types of headers, which govern various aspects of the API including response type and version.
Cookies are some information that needs to be stored with the clients for a longer duration. And might be required to maintain some state.
The body is the actual payload that an API can transfer. It contains business data.
Lesson 2: Choosing the correct RESTful Method
RESTful API comes with a defined set of methods that the developers can use. These methods have a standard definition, which although is widely known is easy to forget.
It is also one of the common interview questions to differentiate between various methods of a RESTful API.
Lesson 3: Identifying the resources via a path
The path to the resource must be thoughtfully decided. This is the location where the object virtually lies for the external users.
E.g. while designing an API for banks we need to define the following model objects
We also need to capture their relationships e.g.
- A user has a few accounts
- An account has some transactions
A list will always be plural. Hence to retrieve all the users of the system the resource path must be
A single user whose user id is known would be returned by
Id is generally a number internal to the system. Mostly, it doesn’t make sense to end-users. Hence it is a good idea to additionally provide APIs which define user-readable identifiers. E.g. a user can also be uniquely identified by the email, (or in Indian banking context UIDAI or PAN number) hence the following APIs should be developed as an addendum for getting a single user.
Children can be accessed via the parent’s path (unless the hierarchy becomes too long).
E.g. to access all the accounts linked to a user should be accessed via
One option to get all the transactions for an account could be
But the above path can get complex quite quickly. Hence it is a good idea to keep the hierarchy flat. A better API to uniquely get an account could be
And hence transactions for an account could be retrieved by
Lesson 4: Managing breaking changes in APIs by use of versioning
To cater to the changing market requirements, we continuously need to improve the APIs that we build. Sometimes this might include breaking changes as well. Hence it is important to maintain version as a different part of the API.
Versions of an API can be managed via any of the following approaches
- By using headers
- By using path param
- By using query param
By using headers
It is suggested to use the “Accepts” header along with the version number.
Accept: application/vnd.heroku+json; version=3
This is a proven strategy to use headers for versioning and is widely adopted by large enterprises including Github itself.
It allows a clean approach where a client can indicate what version of API is he looking for
By using path params
Another common strategy to share versions is via path params. E.g.
Although this approach is also quite common, this breaks the single responsibility principle. In this case, the path is being used for more than one reason.
By using query params
This strategy is generally used for static resources that are cached by the browser. Since the cached items might be coming from the cache which is governed by the path and query params, this approach allows for a workaround to refresh resources forcefully from the server instead of a cache.
Lesson 5: Developing Cacheable APIs
All the applications consist of Master data APIs or APIs which deliver static content. The response of these APIs is generally static and does not change often. Clients like browsers have the capability to cache the response on their end which helps in improving response time on consecutive requests.
Dealing with cache has its own challenges, and we need more control over the caching mechanism. Fortunately, there are different headers available using which a server can define caching configurations for the content that they are releasing.
This blog by Yorgos Fountis provides a good read on how browser caching works.
Apart from standard cache-control headers, we need to take care of ETag headers while developing APIs
ETag stands for entity tag. The ETag header is used to define the versions of the response. Modern servers like Apache and Nginx handle ETag automatically.
ETag header, when used along with other headers e.g. If-none-match can drastically reduce dependence on the network, hence improving performance while maintaining the accuracy of the system.
First Request :
Whenever a request for a resource is initiated by the browser, the server fetches the resource and associates an ETag header with it. The ETag header defines the version of the resource. The browser now caches the resource (based upon cache-control headers) along with it’s ETag version.
Second Request :
The browser sends a second request for the same resource, but this time it also tells the server about the version that it already has via (If-None-Match) header.
The server in return calculates the ETag of resource, if it is not modified, the server simply returns an empty body in response with Http status: 304.
Thus saving network bandwidth and processing. Browser, on receiving 304, serves the content from the cache.
But if the resource has been modified on the server, the server responds with the new resource, updated version in ETag and Http Response code as 200. The browser now refreshes its cache with the correct version.