RESTful API 101

It is almost impossible for a backend engineer to be oblivious to RESTful API in the 21st century, as it is one of the most popular API types. We can mostly attribute its popularity to its scalability, flexibility, and simplicity, all of which are highly sought-after qualities in modern APIs. If you also find these qualities desirable, you should consider building APIs following the RESTful principles. In this article, I will explain what counts as a RESTful API and demonstrate how to design one from scratch.

What is RESTful API?

Let’s start with the acronym “REST” in RESTful API. “REST” stands for REpresentational State Transfer. It is a software architectural style with five constraints:

  1. Uniform Interface
  2. Stateless
  3. Cacheable
  4. Client-Server Separation
  5. Layered System.

RESTful APIs are essentially APIs that do not violate any of the above constraints. Let’s go over them one by one.

Uniform Interface

This constraint is probably the most relevant to backend engineers since it concerns API design. It stipulates that there should only be one uniform way to interact with a given server regardless of the device or application types of the client. The following are some guidelines for building such a uniform interface. A uniform interface ensures consistency and simplifies API development and usage.

  1. Resource-Based: The client has to specify in the URI which resource (object, data, or service) it seeks to access in each request.
  2. Manipulation of Resources Through Representations: To allow the client to manipulate (edit or delete) resources, the client should hold representations of resources that contain information necessary for resource modification or deletion.
  3. Self-Descriptive Messages: Each message (request or response) should contain enough information that the recipient needs to understand the message. This constraint applies to both the client and the server. The client should include the HTTP method in their request to indicate to the server what action they want to perform with their request. The response sent by the server should contain the content type and response status code so that the client knows how to interpret information in the response. The client and the server can also include other information in their messages if necessary.
  4. Hypermedia as the Engine of Application State (HATEOAS): Each response should include hypermedia. Hypermedia is information about the further requests the client can make after they receive the current response. The server uses it to inform the client what it can do next.

Stateless

This constraint forbids the server from keeping track of the states of the client. As a result, the client cannot rely on the server for state tracking and has to include all the information necessary to fulfill a request. The server treats each request as a standalone. Such statelessness frees the server from the chores of maintaining states, thus improving availability.

Cacheable

This constraint requires the server to specify in every response whether the response is cacheable and the duration that the client can cache it. Doing so helps us build an efficient caching system that can eliminate unnecessary communication between the client and the server, thus improving the overall performance and availability.

Client-Server Separation

This constraint illustrates the separation of duties between the client and the server in a RESTful API architecture. The client is only responsible for making requests to create, access, or manipulate resources. The server is only in charge of providing and managing resources in response to the client’s requests. Client-Server Separation allows the frontend and backend to evolve independently.

Layered System

There can be multiple layers in an application’s architecture between the client and server. These in-between layers usually play a complementary role to the core functionalities of the application. Some examples include proxies (for load balancing) and gateways (for protocol translation). The “Layered System” constraint restricts each layer from interacting with layers other than the ones right next to it. It ensures the structural simplicity of the application and prevents us from introducing unnecessary complexity into the inter-layer interactions.

How to Build RESTful APIs (Example)

Next, I will illustrate how to build RESTful APIs with a concrete example. I will make references to the constraints and guidelines brought up in the previous section. Please go back to the previous section if you have trouble remembering them.
Let’s say that you want to build a blogging site. In the first phase, you need to develop endpoints for performing the following actions:

  1. Creating a new post
  2. Retrieving all posts
  3. Retrieving a post by its ID
  4. Updating a post
  5. Removing a post

Can you guess how many URIs you need? Many of you might be tempted to answer five because, intuitively speaking, you need one URI for one action. However, we only need two URIs if we fully comply with the “Resource-Based” guideline. The URIs should represent resources instead of actions performed on resources. The two types of resources we have here are a list of posts and a single post. We can create the following two URIs to represent them respectively:

  1. List of posts: /api/v1/posts
  2. A single post: /api/v1/posts/

You might want to ask: How can we represent five different actions with only two URIs? The answer is simple: We can use the HTTP methods to represent the actions, and actions performed on the same resources can share the same URIs. Here are the HTTP methods that we need:

  1. GET: resource retrieval
  2. POST: resource creation
  3. PUT: resource update
  4. DELETE: resource removal

Now let’s look at how we should design our endpoints to support the above five operations.

  1. Creating a new post: [POST] /api/v1/posts
  2. Retrieving all posts: [GET] /api/v1/posts
  3. Retrieving a post by its ID: [GET] /api/v1/posts/
  4. Updating a post: [PUT] /api/v1/posts/
  5. Removing a post: [DELETE] /api/v1/posts/

Next, let’s move on to designing what each endpoint’s request and response should look like. We still need to bear in mind the guidelines of the Uniform Interface constraint. For this article, let’s focus on the third action.

Retrieving a post by its ID: [GET] /api/v1/posts/

To follow the “Manipulation of Resources through Representation” guideline, we need to ensure the client has all the necessary information to manipulate (edit or delete) a given post. To edit or delete a post, the client needs to specify the ID of the post so that the server can identify which post to manipulate. To edit a post, the client also needs to know which fields it can edit and the ID of the post. Let’s say that the client can edit the following fields:

  1. title
  2. body

Then the response data of [GET] /api/v1/posts/ should look like this:
Response:

We also need to have the client and server send Self-Descriptive messages to each other. With the “Self-Descriptive Message” guideline in mind, we can design the request and response for [GET] /api/v1/posts/ as follows:
Request:

In this request, the client has specified the action through the HTTP method (GET). The client has also pinpointed the target resource of the action by including the URI (/api/v1/posts/). Furthermore, the client has included the protocol it uses (HTTP/1.1) and the type of data it accepts (application/json).
Response:

In this response, the server has informed the client that the request has been successfully processed via the 200 response status code. It has also added the content type so the client knows how to interpret the response data.

We still need to conform to one more guideline to build a Uniform Interface: “Hypermedia as the Engine of Application State.” This guideline requires the server to include further actions the client can take. After receiving the response containing details of the post, the client can choose to edit or delete the post. Therefore, the server can add the requests for editing or deleting the post in the response data.
Response:

Now that we have built a uniform interface, let’s ensure our system complies with the “Cacheable” constraint. We can do this with the Cache-Control header. Assume the server allows the client to cache posts for up to 10 minutes (600 seconds). Then our final response should look like this:
Response:

Cache-Control: max-age=600 means the client can cache the data for 600 seconds at most.

So far, we have gone through the “Uniform Interface” and “Cacheable” constraints. It is difficult to illustrate the other constraints in this example since they concern system design, but please do bear them in mind when building RESTful APIs.

Summary

In this article, I have covered the definition of RESTful API and the five constraints that RESTful APIs cannot violate. I have also shown how to build RESTful APIs with an example. I hope this article has helped you understand RESTful APIs better.

Read More:

https://www.geeksforgeeks.org/rest-api-architectural-constraints/
https://learn.microsoft.com/en-us/azure/architecture/best-practices/api-design
https://restfulapi.net/hateoas/

おすすめ記事