Introduction
Rest API are a very common means of providing clients to communicate with servers over an http / https channel. This document lists out some guidelines that can help build a good rest service.
Committing to these practices can help build a good service in as little as 10 days.
Brief Overview of REST services
Representational State Transfer (REST) is an architectural style that specifies constraints, such as the uniform interface, that if applied to a web service induce desirable properties, such as performance, scalability, and modifiability that enable services to work best on the Web. In the REST architectural style, data and functionality are considered resources and are accessed using Uniform Resource Identifiers (URIs), typically links on the Web. The resources are acted upon by using a set of simple, welldefined operations. The communication channel established is typically considered stateless in this scenario.
The following principles encourage RESTful applications to be simple, lightweight, and fast:
- Resource identification through URI: A RESTful web service exposes a set of resources that identify the targets of the interaction with its clients. Resources are identified by URIs, which provide a global addressing space for resource and service discovery.
- Uniform interface: Resources are manipulated using a fixed set of four create, read, update, delete operations:
PUT
, GET
, POST
, and DELETE
. PUT
creates a new resource, which can be then deleted by using DELETE
. GET
retrieves the current state of a resource in some representation. POST
transfers a new state onto a resource. - Self-descriptive messages: Resources are decoupled from their representation so that their content can be accessed in a variety of formats, such as HTML, XML, plain text, PDF, JPEG, JSON, and others. Metadata about the resource is available and used, for example, to control caching, detect transmission errors, negotiate the appropriate representation format, and perform authentication or access control.
Designing a Good Rest API
Rest services are lightweight and easy to use. However, designing a good rest service can ensure seamless client integration with the service and provide a mechanism for a robust, scalable and secure means of communication.
The next section describes guidelines that can help in building a good REST API.
Day 1 - The Basics – Names and Actions
It is important to get the name of the service right while building a REST service. While this appears to be a simple step to follow, ensuring that the services are named correctly ensures clients can easily understand what a services does. Day 1 of building a REST API starts by ensuring the right nomenclature is being used.
Similar, REST services uses standard HTTP verbs like GET
, POST
, PUT
and DELETE
. Using these as per guidelines ensures clients call the service in the right manner.
Quote:
“Url is a sentence, resources are nouns and actions are verbs”.
The next subsections describe these in more detail.
Naming the Service
Name the services by using the name of the resource being accessed as a part of the service.
For example, if the service returns a piece of static data information, the service should be called:
\static\{id}
where static is the type of resource being accessed and {id} represents the actual id of the service.
It is not advised to use a verb in the name of the service.
For example, getStatic
or saveStatic
would not be the right names for accessing static data services.
For accessing multiple resources, the plural form of the resource can be used.
For e.g. /currencypairs/
or /persons/
The only place where it is recommended to use a verb is when the service performs an action that is not directly linked to a resource.
For e.g. /backup/
when a database needs to be backed up.
The right HTTP verb (action) is important in building a service.
The following guidelines should be followed:
- GET – Request data from resource without any side effects
- POST – Create a resource in a database
- PUT – Update or create a resource
- DELETE – Remove a resource
- PATCH – Update a partial resource (for example, one field in the resource)
There is no validation by HTTP if these are used incorrectly. However, using the right action ensures consistency across the service – both for service and for the clients consuming this service.
Day 2 - Response Codes and Error Handling
By default, both http 1.0 and http 1.1 provides a set of responses codes that are returned whenever a response is generated over http protocol. These are the standard responses returned when REST services are called. However, these are often not sufficient to provide the client with the correct responses. It is useful to provide a proper message along with the response.
Additionally, in the case of errors, it is important to log the stack trace of the error to ensure it can be debugged / examined when investigated. This information need not be passed back to the client. However a well-defined error message should be returned to the customer as a response.
Security related information or critical data that need not be shared with the client are specific cases of information that need not be logged back to the client.
Note: Standard HTTP response messages are described here.
Day 3 – Authentication, Authorization and Security
Authentication and authorization play key roles in enabling a good rest service. There are several ways to enable security and this section only introduces some ways to implement these items.
Authentication
Authenticating calling resources is a standard process used by all services. It can be done in several ways. Some common ways are listed below:
- In-house authentication – confirming authentication against a server database. Risky because username and password are sent over the wire.
- HMAC – hash-based message authentication where only hashed passwords are sent over the wire
- Oauth 2 – providing access to a server on behalf of a resource owner
These methods provide authentication, but do not guarantee all further requests to the server are secure. As a result, some form of token mechanism needs to be considered as an addition to authentication.
Authorization
An easy way to enable authorization is to enable principals to manage resources. Linking principals to resources will make sure only accessible resources are returned to each client.
Once authorized, even principal information can be added to a token.
Token Based Security
Tokens are generated when a login attempt succeeds. These tokens are then attached to the header in every request to the server.
In order to manage requests in a stateless environment, JWT (json web tokens) provide a smart and secure way of managing tokens.
JWT tokens consist of three parts including header, claims and signature providing a stateless way of achieving validation of each request.
JWT tokens ensure validity of tokens across different users, principals and manage expiration via claims.
More on Security
Authentication and Authorization by themselves are not sufficient to provide security in the real world. Several risks like sniffer attacks, man in the middle attacks, etc. threaten a rest service.
While detailed discussion of these topics is out of scope for this document, some items are mentioned here for reference:
- HTTPS – Implementing basic http is never enough on a server. HTTPS has to be enabled to secure a set of REST services and ensure security against Man in the middle attacks.
- Encrypting data – Encrypting important information (passwords, customer names) over the network will enable an additional layer of security. However, encryption can have a performance impact besides making it harder for clients to communicate with the server.
- Security tokens – Security tokens form an important part of any stateless communication setup to ensure every request to the service is valid and not created by a potential attacker.
- Input validations – Validating input can be handy when evaluating against XSS type of risks.
- CSRF – Protection against CSRF can be achieved through special tokens where part of the token is understood by the client and another by the server.
Each of these threats need to be mitigated to enable REST services are secure and robust to handle any type threats.
Day 4 – Using Sub-resources
Rest services suggest accessing resources to resources with the help of “sub-resourcing”.
For example, if we have a resource with the name ‘cars
’ denoting a list of all types of cars and another resource set ‘drivers
’, to get a list of drivers for a particular type of car, the following resource sub-resource combination can be used:
/cars/{carname}/drivers/{drivername}/
This avoids the overhead of having two different types of services while ensuring relationships are maintained between different sets of API.
Day 5 – HATEOS
With HATEOS (Hypermedia as an engine of application state), a client application can directly interact with servers through hypermedia. This ensures a REST client needs no prior knowledge of interfacing with the REST services.
Once the first URL is accessed, links to further resources can be generated dynamically. The major benefit of this is that it allows the servers to change the URI schema and there is no impact on the client. The client requires no changes even after links to the server are changed.
This is different from traditional WSDL systems where interface points are already known and changes to these interfaces would imply a necessary change on the client side.
The response from accessing a resource tells which action needs to be performed next.
In the example below, layout resource provides access to static currency resources within the response itself.
Day 6 – Versioning of API
With changing business functionality, it is expected REST API will change. However, as new APIs are created, it will become impossible for older clients to ensure they are up to date. REST API signatures will break existing functionality. As a result, existing customers will struggle to keep up with these changes.
API versioning ensures long term customer commitment.
There are several ways to address versioning. A few of these ways are listed below.
- URI Versioning – The version can be included in the URI of the rest service. This ensures system does not have to handle headers and is a cleaner way of maintaining versions. However, clients need to change their code to point to a newer version. It also invalidates REST since it is argued that REST uri’s indicate only the resource.
However, this is one of the most common REST versioning approaches presently used.
Example: .../static/v2/loadSchema - Accept Header – Modify the accept header to include the version. This approach ensures client need not update the uri whenever API versioning changes. This approach has a drawback, however, that clients and firewalls need to know the format of the header being accepted / used. This approach is hidden, making it hard for clients not knowing about this approach to use it.
Example: Accept: application/vnd.myapi.v2 + json - Custom Header – Http versions allow custom headers. These custom headers can be used to manage version numbers. However, just like Accept headers, client need to know about these headers. Firewalls may not allow these headers though to the network either.
Example: X-MyAPIVersionRequest-Header: 2 - URI Parameter – Adding the version as a parameter is an easy option to manage API versions. This is a commonly used and suffers with the same pros and cons as URI versioning. However, this has an additional advantage that if a version is not provided, there is an option to switch to the default version.
Example: .../static/v2/loadSchema?version=2
URI versioning using versions in the url and URL parameters are more commonly used approaches to manage versions.
Below, we will look at two use cases that will highlight some pros and cons of each approach.
USE CASE 1 – Ability to version specific resource branches. Clients are willing to upgrade and change code to call these specific branches.
- URI versioning option is suitable. There is clear distinction between older and newer version in API itself. Existing hyperlinks may break but if client is open to integration with newer version, this is ok.
- URI parameter is also an option. Here again, a parameter value defines the version. If no value is specified, default specification can be used.
REST itself does not recommend anything other than resource in the URI so this approach is a deviation to the standard.
However, these are the most common approaches to versioning (Amazon, Google and Netflix use them!)
USE CASE 2 – Ability to version specific resource branches in a hidden manner. Clients may not always want to specify versions.
- Accept and Custom header are suitable – if a version is specified in the header, a newer resource is fetched / updated.
- This is a neat approach with the uri not specifying the header
Both accept and custom headers are harder to test. Clients need to be aware of them.
Not all firewalls may allow customized headers.
Day 7 – Using HTTP Headers for Managing Serialization
To enable API to be used for future use, it is important that the REST API should be built to handle any type of data formats. Some of these formats may be available now and others may only be available in the future.
For example, content-type
could contain either of text/xml
or application/json
.
As a first step, the service should be able to switch between different formats provided in content-type
.
As a next step, the service should also be extensible to support other formats that may be used in future.
By ensuring that all this functionality is built into the service, it can ensure that that service can be adapted to changes in formats faster.
Day 8 – Support for Pagination, Filtering and Sorting
The service should inherently support pagination, filtering and sorting within the uri itself.
This can enable smother integration with a client / user interface expecting data from the service.
As far as possible, filtering, pagination and sorting should occur on the server and then data passed on to the client. Benefit of this will be reduced data sent over the network improving performance.
Pagination, sorting and filtering can also be done on the client side. This could slow client side down because all these would be working on a large dataset.
Day 9 – Overriding the HTTP Method
On several clients, proxies only allow POST
and GET
method. There is no support for PUT
and DELETE
.
As a result, REST API will fail to work on these clients.
In order to provide a solution for this problem, it is advisable to for the service to understand the X-HTTP-METHOD-OVERRIDE
header option.
The server pipeline receives this value and converts it into the appropriate REST method.
Day 10 – Caching
The main goal of caching is to never generate the same response twice. In a REST service, this can ensure better performance by enabling servers to handle more clients at the same time.
Caching
- Improves performance
- Reduces server load
The cache-control parameter in a request provides several parameters in the response. These parameters provide configurable options with respect to caching.
max-age
– time to live for a cached resource public
- allows every request to be cached private
- does not allow proxies to cache data must-revalidate
– revalidates cache when it is stale
Amongst all the HTTP methods, GET
is the most likely candidate for caching, simply because it requests for resources. POST
and PUT
are less likely candidates for caching as these update information on the server side.
Care needs to be taken to ensure caching is enabled differently for http 1.0 and http 1.1 separately as some parameters between these two protocols vary and are different.
To improve performance, caching can be done at the following four locations:
- Local cache – This type of caching is typically implemented at browser level.
- Proxy cache – This type of caching is implemented at the proxy at the front of the firewall.
- Application cache – This type of caching is typically implemented at application level.
- Gateway cache – This type of caching is typically implemented at the proxy on the server. The benefit of this over proxy cache is that several clients across the internet would have access to the same data.
Documentation
No API is complete without proper documentation. If a service is not documented properly, it will be hard for users to know what each service does and what they need to do to consume it.
Consumers of a service need to know several items within a service:
- They need to know which version to call whenever multiple versions are supported
- Each input parameter needs to be documented to ensure clients pass the right parameter
- Every response parameter needs a description
- Both normal and error messages should be returned to the client with proper descriptions
In general, tools like JAXRS analyser and ASCII doc can be used along with standard javadoc to create swagger like documentation. But this may not be sufficient as proper functional documentation needs to be provided by functional consultants / technical writers to enable clients to use APIs in the right manner.
Conclusion
A good REST service can be created keeping in mind a basic set of basic principles. This need not require many days of effort and if these practices are implemented from the beginning, a service can include many good practices in as few as 10 days.
Building a good REST framework initially can help clients easily interact with the server and ensure long term commitment in consuming these services.
In several places, API and services are used interchangeably.
REST stands for Representational State Transfer – wherever this acronym is used, it can be suitably replaced with its full form.
Both Rest and REST have been used across the document. For the purpose of this document, they mean the same thing.
History