Hello, dear readers!

Before you start reading this article, I would like to describe the purpose of its creation, and tell you what prompted me to write it.

Once, the need to design a server application in the REST style arose on one of our projects. Initially, it seemed to be a quite easy task for us to solve, and our own experience would be enough for its implementation.

But when the architecture development process and process of bringing the REST services to the common style had started, there were disputes and different points of view on the implementation of one or the other aspect in our team. Then we understood that we need to address Google and seek the assistance of the collective mind, study the proposed best practices that should be used when designing RESTful applications.

This article will be useful for those people who had already had some experience of web application development (and possibly with REST services), but needs to consolidate and standardize the gained knowledge.

Definition

Firstly, you need to decide what is REST. Wikipedia gives to this question the following answer. REST (Representational State Transfer) is an architectural style of interaction among distributed application components in the network. REST is a concerted set of constraints taken into account when designing a distributed hypermedia system.

In my own words, I would explain the concept of REST as “a set of recommendations, which unifies the interaction of client and server applications.”

In this article, I am trying to describe those “recommendations” that will help to design and create REST services in accordance with generally accepted practices.

Also, it is necessary to understand what REST service means. I would give the following definition to REST service: it’s a point of interaction of the client application to the server.” Speaking in Java terminologies – it’s a servlet to which the client sends a request.

Problematics

Before I begin to describe the rules, I would like to convey the idea that REST is not a standard, so there are no unified strict rules which should be adhered to. This means that there is still no full agreement on what solutions are the best to implement in a given situation. Very often there are the debates about what HTTP methods should be used and what HTTP code should be returned in any of given situations.
More details about REST problems can be read here.

Accordingly, the recommendations that I will describe below, may be taken into consideration, but shouldn’t be followed, if they could damage any other aspect of the project.

However, if you manage to follow all the recommendations, you’ll be able to design a very intuitive and maintainable system, which is pleasant to work with.

Service name

First, we need to choose a name for REST service. Under service name, I mean its path in the URI request. For example, http://my-site.by/api/rest/service/name. To select a name, we need to understand what “resources” mean in REST architecture.

Presentation of the resource

In REST terminology, everything can be a resource – HTML document, image, information about a particular user, etc. If the resource is an object, we can easily represent it using some standard format, for example, XML or JSON. Then the server can send the resource using the selected format, and the client will be able to work with the resource obtained from the server, using the same format.

Example of presentation “profile” resource in JSON format:

 {
     "id":1,
     "name":"Mahesh",
     "login":"manesh"
 }

REST does not impose explicit restrictions on the format that should be used to represent resources, but there are some rules to follow when designing a format that will be used to represent a resource:

  • The client and the server should “understand” and be able to work with the selected format.
  • The resource can be fully described using the selected format, regardless the complexity of the resource.
  • The format should provide the possibility to provide relations between resources.

Example of presentation of the “order” resource and its relations to the “profile” resource:

 {
    id: 11254,
    currency: "EUR",
    amount: 100,
    profile: {
        id: 11,
        uri: "http://MyService/Profiles/11"
   }
 }

As you can see, it is not necessary to completely duplicate the entire structure of the resource which another resource refers to. Instead, the clear links to another resource can be used.

Address to the resource

Each resource should be uniquely marked with the permanent identifier. “Continuous” means that the identifier is not changed during the data exchange, and even when the resource state is changed. If the other ID is assigned to the resource, the server should inform the client that the request was not successful, and provides a link to the new address. Every resource is uniquely defined by URL. This means that in fact URL is the primary key for the data unit. That is, for example, the second book from the bookshelf will look like /books/2, and 41 pages of this book – /books/2/pages/41. It turns out strictly specified format. And it does not matter in what format the data are located at the address /books/2/pages/41 – it can be HTML or a scanned copy as a jpeg file or a MS Word format document.

It’s recommended, when determining the name of the REST service, use the resource name in the plural. This approach allows you to add new REST services, only expanding the existing names. For example, service /books will return us a list of all the books, /books/3 returns information about the third book, and service /books/3/pages will return all the pages of the third book.

For services that perform some specific actions on the resource, there are two approaches to indicate the action: in the name of the service or in its parameters. For example, /books/3/clean, or /books/3?clean. I prefer the first option, because usually such services use the POST methods, which do not support the transfer of parameters in URl, that makes the service, in my opinion, not very readable. Using the definition of the action type in the name of the service, we make our service more scalable, since it does not depend on HTTP method type.

Also, it is not recommended to use names that include a few words describing the business component of the service (as it is recommended when naming java methods). For example, instead of /getAllCars it’s better to write method /cars. If the method cannot be described in any way in one word, it is necessary to apply a single style separators, I usually use ‘-‘, which is the most popular approach. For example, /cars/3/can-sold.

More details about the design of REST service names can be found in this article.

HTTP methods

Next we need to select the HTTP method that will use our REST service, since even having the same names, but different methods, REST services perform completely different actions.

4 basic HTTP methods are used in REST: GET, POST, PUT, DELETE. In most cases, each of the methods is used to perform the predefined for its action from CRUD (create, read, update, delete).

POST – create, GET – read, PUT – update, DELETE – delete.

IMPORTANT ADDITION: There are so-called REST Patterns, which are distinguished by binding HTTP methods with what they do. In particular, the different patterns consider POST and PUT in different way. However, PUT is designed to create, replace, or upgrade. For POST it is not determined (The POST operation is very generic, and no specific meaning can be attached to it). So sometimes POST and PUT can be interchanged. But in most cases, POST is used to create, and PUT for editing, and a bit later I’ll explain why.

Here are a few examples of the use of different methods to interact with the resources.

  • GET /books/ – gets a list of all the books. As a rule, this is a simplified list, i.e. containing only the identifier field and the object name, without the other data.
  • GET /books/{id} – receives the full information about the book.
  • POST /books/ – create a new book. Data is transmitted in the request body.
  • PUT /books/{id} – changes the data of the book with ID {id}, may replace them. Data is also transmitted in the request body.
  • OPTIONS /books – gets the list of supported operations for a specified resource (almost never used).
  • DELETE /books/{id}– deletes data with ID {id}.

Safety and idempotence

The knowledge about safety and idempotency of HTTP methods will help in selecting this method.

A safe request is a request which does not change the state of the application.

Idempotent request is a request which the effect of the multiple executions equal to the effect of the single execution.

Based on this table, GET request should not change the state of the resource to which is applied. PUT and DELETE requests can change the state of the resource, but they can be safely repeated if there is no certainty that a previous request has been performed. In principle, it is logical: if a request to remove or replace the specific resource is repeated several times, then the result is the removal or replacement of the resource. But POST request, as we can see from the table, is unsafe and not idempotent. It is not enough that it changes the state of the resource and its multiple repetition will produce the effect, regardless of the number of repetitions. Him meaning corresponds to the operation add a new item to the database: we perform a query X times, and in the database of added elements.

Besides that, it changes the resource state, the multiple repetition of it will produce the effect, depending on the number of repetitions. It corresponds to the operation of the addition of new elements in the database: the request was completed X times, and X elements were added to the database.

I also give an example of why GET requests should not change the state of a resource. GET requests can be cached, for example, at the proxy server level. In this case, the request can not even reach the application server, and the proxy server returns information from the cache as a response.

HTTP codes

HTTP standard describs over 70 status codes. Good practice is to use at least basic ones.

  • 200 – OK – the successful request. If the customer has requested any data, then these data are in the header and/or body of the message.
  • 201 – OK – a new resource was created as a result of the successful execution of the request.
  • 204 – OK – the resource was successfully deleted.
  • 304 – Not Modified – the client can use the data from the cache.
  • 400 – Bad Request – the request is invalid or could not be processed.
  • 401 – Unauthorized – the request requires user authentication.
  • 403 – Forbidden – the server understood the request, but refuses to handle it or access is denied.
  • 404 – Not found – the resource was not found.
  • 500 – Internal Server Error – API developers should try to avoid such errors. These errors must be caught in the global catch-block and be logged, but they should not be returned to the response.

The more extensive set of codes we will use, the clearer will be API, we create. However, we should bear in mind that some browsers processed codes differently. For example, some browsers having received 307 response code immediately performs redirect, and some allow you to handle this situation and to cancel the action. Before using one or another code, you need to fully understand how it will be processed on the client side!

More details about HTTP codes can be read here.

Headers

It is recommended when designing REST services to clearly point out headers where the data exchange format is indicated:

  • Content-Type – the request format;
  • Accept – the list of response formats.

Resource search parameters

To simplify the use of the services which are responsible for the return of any information, and in addition to make them more productive, the parameters for sorting, filtering, selecting fields and pagination should be used as request parameters.

Filtration

Use the unique request parameter for each field to implement filtering. This allows to limit the amount of output information, therefore optimizes the request processing time.

For example, to display all red books, you need to execute the following request:

GET /books?color=red

Sorting

Sorting is implemented like filtering. For example, to display all the books, descendingly sorted by year of publication and ascendingly sorted by name, it is needed to run the following request:

GET /books?sort=-year,+name

Pagination

In order to have the ability to load a list of resources that should be displayed on a certain page of the application, the pagination functionality should be provided in REST API. It is realized using parameters familiar from SQL, limit and offset. For example:

GET /books?offset=10&limit=5

Besides, it’s a good habit to output links to the previous, next, first and latest pages in the Link header. For example:

Link: <http://localhost/api/books?offset=15&limit=5>; rel=”next”,
<http://localhost/api/books?offset=50&limit=3>; rel=”last”,
<http://localhost/api/books?offset=0&limit=5>; rel=”first”,
<http://localhost/api/books?offset=5&limit=5>; rel=”prev”

It is also recommended to return the total number of resources in the X-Total-Count header.

Selection of the resource fields

For more convenient use of the service, the ability to control the output format can be provided in order to save traffic. It’s implemented by providing the possibility of selecting the resource fields, which the REST service should return. For example, if you want to get only id and color of books, you should run the following request:

GET /books?fields=id,color

State storage

One of the limitations of RESTful services is that they should not store the state of the client from which requests are received.

The example of service that doesn’t store the state:
Request1: GET http://MyService/Persons/1 HTTP/1.1
Request2: GET http://MyService/Persons/2 HTTP/1.1

Each of these requests can be processed independently.

The example of service that stores the state:
Request1: GET http://MyService/Persons/1 HTTP/1.1
Request2: GET http://MyService/NextPerson HTTP/1.1

To process the second request, the server will need to “remember” id of the latest person who was requested by the client. I.e. the server should “remember” its current state, or the second request cannot be processed. When designing the service, you should avoid the need to store the state, as it has several advantages.

Advantages of the service, not storing the state:

  • service processes requests independently;
  • service architecture is simplified;
  • it does not require additional efforts to implement the services using HTTP protocol, which also does not store state.

Disadvantages of service, not storing the state:

  • the client should be responsible for the transfer of the necessary context to service.

Versioning

It is good practice to support REST API versioning. This will allow in future to easily extend API without requiring changes to clients that are already using them.

There are several approaches to implement versioning:

  • Using Accept header. In this case, API version is indicated in Accept – Accept:text/v2+json
  • Using URI. In this approach, API version is specified directly in URI – http://localhost/api/v2/books
  • Using a custom header. You can use your own header, which will be responsible only for the transfer of API version – API-Version:v2
  • Using a request. You can use the request parameter to pass API version – /books?v=2

Each of these methods has the right to exist, and each has its pros and cons. However, only you can decide which method for implementing versioning is suitable for your project.

Documentation

For convenient use of REST services, you need to create a good and understandable documentation. For these purposes you can use various tools, such as Mashape or Apiary, but I recommend to use Swagger.

Swagger is a technology that allows to document REST services. Swagger supports a variety of programming languages and frameworks. Plus, Swagger provides UI for viewing documents.

Archiving

To save traffic, it is recommended to use archiving, when transferring large data. This will reduce the request execution time. In most modern frameworks, this feature is enabled by default.

Caching

Also, to reduce database requests and increase the speed of REST services, it is recommended to use a caching mechanism. Caching can be configured both at the server and at the application level, depending on the situation.

Caching can be managed using the following HTTP headers:

  • Date – date and time of resource creation.
  • Last Modified – date and time of last modification of the resource on the server.
  • Cache-Control – HTTP 1.1 header is used to control caching.
  • Age – the time elapsed since the last reception of the resource, a header can be added by an intermediate (between the client and the server) component (eg a proxy server).

Recommendations for caching:

  • It is recommended to cache static resources such as images, css styles, javascript files.
  • It is not recommended to specify a large lifetime of the cache.
  • Dynamic content should be cached for a short time or not cached at all.

General recommendations

Type of resource representation

Although REST does not impose explicit restrictions on the format that should be used to represent resources, the most popular formats are XML and JSON.

There are many libraries in different programming languages for easy work with these formats. Despite this, it is recommended to use JSON to represent resources. It is more readable format, which is easier to operate compared with XML, and simpler to perform serialization/deserialization of objects in different programming languages.

However, sometimes to support some REST clients, the service needs to support the XML format. In this case, you can implement the support for both formats and indicate in the request parameter in which format the response should be.

The use of proven solutions for authorization.

Use a tested and proven scheme for authorization.

Don’t reinvent the wheel using md5 signatures of data and other things, trust the professionals in the field of data protection. Everything has been invented before: OAuth, OpenID, APIKeys.

Handling exceptions

If you have any error situations, it is necessary to output formatted and understandable information. Primarily, this refers to the code status in HTTP response. Services errors are often of two types:

  • 4xx – client error;
  • 5xx – server error.

In the case of client error, for example, validation failure of some request parameters in the response body it is recommended to transfer useful information about the error: a message, description, code (e.g. in JSON format). In the case of server error, it is not always possible to send additional information in response body, for example, in cases when the server is not available.

A bad tone is to output the entire stack trace of the exception. We recommend having its own code for each exception. Later, when outputting the error information, you can add a link to the documentation where this code will be a unique identifier.

For example:

  {
     "code" : 1234,
     "message" : "Something bad happened :(",
     "description" : "More details about the error here",
     “moreInfo”: “http:/localhost/api/v2/errors/1234”
  }

Useful links

https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
https://habrahabr.ru/post/144011
https://habrahabr.ru/post/144259