Are you developing web application? OR building mobile app? what you commonly require is a backend providing the ability to save user data and provide meaningful insight. In addition to storage and retrieval of the data, a secured access control mechanism is required to ensure data integrity. There are multiple approaches to handle the requirement and the implementation varies based on the chosen programming language. Usually, the concepts used to solve this problem fall into following broad categories
- Proprietary Implementation (Servlets, MVC Controller, Remote Object etc.)
- SOAP services
- API development using REST
The intention of this article is to suggest some of the concepts that must be considered while designing REST API. Most of the time, my interactions with Developers and even some of the architects, led me to a realization that most people have limited or no knowledge of REST API design. Some refer merely exposing a data using either XML/JSON is REST, while some think just using HTTP methods is sufficient to build REST based API. But that’s not the entire truth.
URL Patterns and HTTP Methods
Commonly known as, Endpoint or Service URL, is significant aspect of the Service design. URL enables access to the functionality exposed by the application. Hence, URL must convey the meaningful purpose of its intended operation in REST services, possibly making symantec use of HTTP methods. For example, to create user profile, the URL may be http://localhost:8080/profile, while the HTTP method will be either POST or PUT. Similarly to remove the profile, the URL will be http://localhost:8080/profile/1010 (here 1010 indicates profile id) and HTTP method will be DELETE.
Based on the above samples, it can be said that the URL should consist of the noun indicating the Entity which will be affected/operated upon, followed by identifier(optional). If you need to pass any additional input, it can be passed either as query parameters (e.g. Search screen having multiple parameters) or use POST method if the parameter size will exceed the standard URL length.
Typically, most of the application requirements fall into the CRUD (Create, Read, Update and Delete) category. Hence, HTTP methods are mapped to the CRUD format as follows
|Create||POST / PUT|
|Update||POST / PUT|
The additional methods like OPTIONS and TRACE aren’t that suitable for CRUD. Additionally, In case of Creating new entity, it is always better to either use POST or PUT and not both. Similarly, in case of Update, use HTTP POST, if you are already using HTTP PUT for create. This will help developers to speak common vocabulary.
Headers offer the most convenient way to exchange information in addition to Query parameters or body. Some of the examples include, authentication tokens (JWT) or custom headers. For custom header, the name must be carefully chosen as it may collide with existing HTTP header or the server implementation you are using. It is always advisable to apply a prefix, to custom headers, which can help to distinguish it from standard headers.
For exchanging authentication tokens, one can choose a custom implementation or go for well established implementation like JSON Web Token (aka JWT). There are ports available for almost every language with wide variety of examples. The adherence to standards and capability to encrypt data before exchange, makes JWT an ideal candidate. JWT enables stateless implementation of services and hence reducing the load on the server.
HTTP Status Code
Every web developer, must be aware of HTTP Status Code 200/404/501. Code indicates the status of the HTTP request and hence enables better handling of the response from server. Consider, while invoking a service, if an authentication token is not supplied, a service can raise error with HTTP status code as 499 instead of default OK code as 200. Simply referring to the code can enable program to improve the error handling. With default OK code, program must always parse the body to know whether there was an error with the request or not. You can refer to additional articles at apigee, twitter.
Instead of making use of all the HTTP status codes, it is always advisable to create small subset based on the application needs. This will aid REST client to focus on small set, instead of writing monolithic switch/case statement.
Choosing appropriate format for exchanging messages is vital for REST service performance. Today there are wide variety of options available including XML, JSON, protobuf, thrift etc. Each of these format has tradeoffs in terms of readability, optimized size, encoding/decoding overhead etc. For example, XML is readable but not optimized and produces heavy messages. Most of the API developers prefer JSON as message format. If you are looking for more optimized solution, you can go for binary formats like protobuf or thrift.
Along with textual data, there is also a need to upload files e.g. profile picture, videos etc. While HTTP offers a mechanism to upload files along with text (using multipart/form-data), the approach looks little clumsy. In case if the file being uploaded is of large size, there are chances that the upload might fail. Such scenario may lead to inconsistencies, if data sent along with the request is transactional. Additionally, the optimum methods like chunked upload can be better applied if the request is dedicated for file upload. An interesting article about file uploads, can be seen here.
This is the most critical piece from integration perspective and also the most tedious task to perform. Keeping the documentation updated with the changing service versions, is always a daunting task. Historically, the service definition files are of little or no help for documentation and a separate document was always needed to define service structure along with invocation mechanism (e.g. JAX-RS wadl definition). With this need, a rise of frameworks like Swagger (aka Open API), RAML etc are doing some justice with Service documentation. Swagger presents nice GUI for service definition and also provides thin client to invoke services. The framework is yet to improve itself for few things.
Another aspect which can’t be overlooked, if you are really looking to squeeze every ounce of performance. With the rise in disperse clients needing access to data, services must get smarter and provide only relevant information. Each client may invoke same service, but may expect variations in data served, primarily in structure. Refer to the example of E-Commerce service exposing product details, may be accessed by a website viewed over Desktop and by a mobile client having limited real estate. Under such scenario, Service must make smart decision and serve optimized data tailored for each client. This definitely puts great emphasis on ability of underlying framework to validate and respond as per client. Unfortunately, there aren’t enough frameworks providing such capability and enterprises have preferred to build custom solution. One of the proposed solution to this problem is to make use of HEADER passing an information about device e.g. mobile/desktop/tablet etc.
Is there anything left, yes there is more and once you start digging in this area, there are many things that need to be taken care. Some of the areas include Role based documentation, which is must if you wish to build highly secure services (Swagger doesn’t offer this), another area is notifying long running tasks using mechanisms like WebHooks, instead of increasing connection/response timeout for client.
This was a small attempt to unearth some of the areas of API design that one must consider before laying out the design. If you have any queries with respect to API design, post comment below.