Designing REST API’s the right way - Digital Solutions, IT Services & Consulting - Payoda

Designing REST API’s the right way

Modern web Client-server communication takes place with the help of APIs [Application Programming Interfaces] mostly. Business models/results of business logic are exposed to the clients through the apis. API’s should be well designed to support platform independence and service evaluation firstly.

There are few ways an API can be designed, REST [Representational State Transfer] is an Architectural style through which APIs can be well designed. Whether you are a pragmatic or idealistic programmer, it is always required to have the basic Architectural design with all the edge cases carefully considered for any API server.

REST APIs are Representations of resources in any of the well-known formats[such as JSON format] and transferred across to different systems in a distributed deployment environment. It is not a protocol like HTTP rather, an Architectural approach but REST can be used over HTTP.

Why REST API design is important:

These days the software systems are developed in a fast-paced manner which sometimes leads to repeated code, multiple APIs exposed for the same resource, Client/User interface doing the data manipulations, etc are happening. In order to avoid such concerns, there comes the importance of designing REST API’s in a proper manner, the design of REST APIs is a real important factor in any distributed system.

An architect/designer starts just with the business needs and the clients using the system in mind. Firstly, the responsibility of the user interface and the data storage system to be well understood before start designing, and the data models with respect to the business needs to be identified with the careful consideration of resources necessary.

Resources may or may not directly map with the data models, and also all the resources in the system are not necessarily exposed to the clients. Each resource should be identified with a unique identifier i.e the URL or endpoint. REST focuses on the components, connectors, and data rather than the implementations or protocol syntax.

REST API’s Architectural elements:

Data elements: REST transfers the representation of a resource in any existing data formats, based on the capabilities of the clients/user interface. Different data elements in REST architecture are as follows,

As we see the architectural elements, resources[the actual data] play the main role. The resources are modified/manipulated/provided using the HTTP request methods such as GET, POST, PUT, PATCH, DELETE, etc.

Let’s discuss few key considerations while designing the REST API:

Resource Identifier:

A resource identifier is nothing but the URL/path/endpoint through which a particular resource is accessed. It is always necessary to follow the right hierarchy and nouns for creating the URLs. For example, let’s think that we are designing for a shopping portal, the main resources for a shopping portal are products and consumer profiles. API design for products can take the form as below,

  1. List out the products

GET /products

2. List the details of the particular product [say product-Id as 12],

GET /products/12

3 Lists out the consumer comments for the product with product-Id 12

GET /products/12/comments

4. To see the particular comment for the product with product-Id 12

GET /products/12/comments/3

In the above examples, we could see the nouns are used and it is always in the plural. Whether it is listing many products or provides the details of a single product[GET /products/12] the literal should be plural. If you would have noted, the hierarchy is clearly defined, products have comments from the consumer, there can be multiple comments too. In the above example, you see point 4, identifies a particular comment for a specific product.

Do not use any action verbs for the API paths such as “GET /getAProduct”, it is a bad practice and there are no limits in creating such names and users [clients] may get confused about what could be the results of the particular API.

http://www.example.api.com/v1/products //Good

http://www.example.api.com/v1/create-product //Bad

Each of the HTTP request method itself has a specific action and that name means it [what action/operation it does] as below,

POST → Creates a resource represented by the details provided by the client, body of the request can have the particular resource properties that have to be created

GET -> Lists or get the resources/resource properties.

PUT -> Creates or replaces the existing resoby the server to the client details, the body of the request message can have the resource details to be created or replaced.

PATCH -> Partially updates or changes the resource-specific details, the body of the request message can have the details to be changed for the existing product.

DELETE -> removes the particular resource for the provided id.

Representations:

Representations mean that the form/format in which the resources are requested or the response is provided back. The body params, query parameters, or path parameters are the request representations. In the same way, the response body represents the resulting resources.

A POST API with body params in JSON format as below is how the resource is represented by the client.

POST-https://example.api.com/v1/products HTTP/1.1

Content-Type: application/json; charset=utf-8

Content-Length: 57

{“Id”:1,”Name”:”CoolPhone”,”Category”:”mobile phones”,”Price”:3000} // Body params

Similarly, A GET API with query parameters,

GET https://example.api.com/v1/products?price=3000

An example of path parameters are the specific product with a productId,

GET https://example.api.com/v1/products/{:id}

Metadata and actual data:

As so far we have seen the input/requests formats from the client end, now it is the response from the server for the corresponding requests received from the clients, The response format can be of the form which has both the high-level information about the resultant resource[metadata] and also the actual resource details[data].

Example response/results,

{

Metadata:{

// Information about the resulting data, such as count of data, length of data, format info about the data

},

Data:{

// actual data collections

}

}

Authentication and authorization:

This is a very important one in the fast-faced and open network world, it is the responsibility of the API server to make sure the data is secure, transmitted over the network safely, and reaches the right client without any modifications. There are threats to the data provided/shared over the network always. In order to identify the right client, it is required to have authentication and authorization in place for all of the API.

Authentication is identifying the client by means of specific identities of the client matching the server database information[For example, userID/username & password, API token, etc]. There can be one API such as “/login” which may be available publicly, which can authenticate the client and can provide back some specific authorization tokens/session token etc.

One way of Authorization or access for the APIs can be based on the tokens supplied by the client [provided as a response in the login API] or any additional authorization header information added by the client. The client has to provide the same information/token while accessing the API.

API access limits:

If someone misuses the API or wanted to bring the server down by accident/maliciously, there should be no room for it. API’s should have limits for continuous access or exceeds the number of API requests from the same client. The user should be notified with 503 responses.

Time-consuming API’s:

There might be a few APIs that may take some time to process the data fetched from DB or manipulate and provide the results. In those cases, the API should respond back with the “202 Accepted” as an immediate response, and also it should provide the URI to fetch the results. Location header in the response can have the URI fetch the results at a later time. As shown in the below example, where the response to the API is “202 Accepted”, but there are no immediate results provided, instead, Location info has been provided to fetch the results bit later by the client.

HTTP/1.1 202 Accepted

Location: /api/v1/status/56789

Pagination and filter:

Performance is a crucial factor for any system dealing with growing numbers of clients and data volume. A growing number of clients can be handled by taking care of the scalability of the system probably by adding the additional/duplicating the resources etc. Let’s think from the second one, the data volume, when we deal with the reports or statistical data it is always the number of records will be huge. In such situations, it is really important that the API does not take so much time or it should not fetch a longer amount of data at a given time. So, it is necessary to use the Pagination techniques along with the filter options in the query. For example, if there is a need for a larger list of all products, then the client can send an offset and limit while the API is invoked as below,

GET https://example.api.com/v1/products?limit=30&offset=0

GET https://example.api.com/v1/products?limit=30&offset=31

Binary partial content:

Another factor that we need to consider while handling the binary types of data is responding with partial content. Let’s assume an API provides a larger image or files as a response, in those cases the receiving capacity of the client should also be considered, making chunks of binary data while providing the response will help. The client should provide an Accept-Ranges header in their GET request, this option will say to the server that the client can support partial content in reception. There is also another option in the HTTP method the “HEAD”, it is also similar to GET, etc, it helps the client know what could be the size of the image or file. Based on the HEAD API response, clients can decide how much of the content can be fetched at a time and how many times.

HEAD https://example.com/api/v1/products/12?fields=productImage HTTP/1.1

The response can be as below,

HTTP/1.1 200 OK

Accept-Ranges: bytes

Content-Type: image/jpeg

Content-Length: 4338

With the above response, the client can decide how much of data can be fetched by the first go as in below,

GET https://example.com/api/v1/products/12?fields=productImage HTTP/1.1

Range: bytes=0–1999

Here, the GET request fetches the first 2000 bytes which may be the receiving and processing capacity of the client. The GET response metadata can be as below,

HTTP/1.1 206 Partial Content

Accept-Ranges: bytes

Content-Type: image/jpeg

Content-Length: 2000

Content-Range: bytes 0–2000/4338

Upon receiving the first 2000 bytes, clients can issue another GET request with a range of 2000–4338 to get the complete image content.

Response codes:

The server APIs should send the right HTTP response code while providing back the responses to the client. HTTP response codes, on the whole, it has majorly 5 classes as below,

  1. 1XX => Response code used to convey informational messages such as processing continue etc. That means the server is still processing the request.

It is best to convey the responses with the right response codes so that it is understood correctly on the client end and it is also globally understood.

There may be some situations where the server has received the request from the client and processing of the request could take some time than usual, in those cases in order to avoid the timeout on the client end, it is a good practice to send “102” code as a response.

A few important and frequently used response codes are listed below,

Versioning:

Least priority but still an important one to support backward compatibility, suppose the design evolves to support different additional functionality in the system, we might end up adding a few more features in the same APIs. In such cases, the older client[previous versions] can continue to work without any issues till they upgrade to the latest version if there is a proper version in place. For supporting backward compatibility, consider versioning of the API. There are few ways,

  1. Versioning In the path parameters

E.g, GET http://example.com/api/v1/products

2. Versioning in the query parameters,

E.g, GET http://example.com/api/products?version=1]

3. Header versioning

E.g, GET http://example.com/api/products

Custom-Header: api-version=1

4. Media type versioning.

E.g, GET http://example.com/api/products

Accept: application/image.v1+json

Documentation:

Most of the time it is a contract between the User interface developer and the system developer on how the interfaces are defined, how the request and response are going to be. While the API is developed, it is a best practice to include documentation of the API, it also helps to understand the versioning differences. There are many tools to make API documentation, one such is swagger.io. From the swagger version 2, it is the open API standard that is followed. The Open API initiative is created to standardize the REST API across different vendors.

Hope this guide helps to design matured REST APIs and also in an industry-standard way.

References:

https://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm

https://docs.microsoft.com/en-us/azure/architecture/best-practices/api-design

https://mathieu.fenniak.net/the-api-checklist/

https://medium.com/hashmapinc/rest-good-practices-for-api-design-881439796dc9

https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api

https://florimond.dev/blog/articles/2018/08/restful-api-design-13-best-practices-to-make-your-users-happy/

Leave a Reply

Your email address will not be published. Required fields are marked *

one × 5 =