The REST architecture — why you might have entirely missed it

This is not okay. This needs to stop now!

AB Satyaprakash
CodeX
9 min readMay 29, 2022

--

This blog isn’t supposed to be a rant, but start by facing the truth— a lot of the backend developers out there have completely ignored the core REST design principles and best practices.

Today I came across some scary endpoints like /getAllSubsections and /deleteContactOfUser 😱 in a Node.js project, which motivated me to write this blog and touch upon the basic principles of designing REST APIs.

Let’s begin by stating the obvious. A REST API is an API that conforms to the design principles of the REST or representational state transfer architectural style. REST provides a high level of flexibility, scalability, and freedom, which is why it has emerged as the most used API architecture today.

If you are unsure what an API is, maybe read this and then come back here.

REST APIs can be developed with any programming language, unlike SOAP or XML-RPC. The only requirement is that they align to the six REST design principles — also known as architectural constraints.

Concepts

Before we talk about these principles, though, let’s cut short the theory and try to discuss some concepts and understand things through some real-life examples.

Resources

A resource is an object with a type, associated data, relationships to other resources, and a set of methods that operate on it.

Typically anything which can be named can be considered a resource. For instance, consider the resources on Medium. If you take a look at Medium’s API, we see the following 4 resources —

Note that while designing your API, you are free to make decisions of your own regarding what should be allocated as a resource. This decision will affect the corresponding complexity and structure of your API.

Resource-based URLs

While designing the endpoints or URLs for your API, we need to ensure that they are well structured and derived from on the resources we have defined above.

HTTP methods

As discussed in the definition of a resource, it is associated with a set of methods that can operate on it. The most common HTTP verbs are GET, POST, PUT, PATCH, and DELETE.

An important example

Let’s assume that, hypothetically, Medium API endpoints looked like the ones below. The operations we are trying to do via the first 4 endpoints are creating, reading, updating, and deleting blogs.

The above endpoints are a mess because of the following reasons —

  1. The URLs are not well structured, and they contain a lot more than just the resource name (nouns).
  2. Making up endpoints such as /getAllSubsections (as I have mentioned in the introduction) is a nightmare since subsections are not well-defined resources.
  3. Adding verbs to URLs can quickly make managing things impossible. We now have 4 new endpoints when we could have had just one, aka /blogs. The number will only keep increasing as we start dealing with other resources.
  4. For complex or nested queries, such as getting all user publications or creating a post under a publication, the URLs will be huge. Imagine of a scenario when we need 3 or 4 levels of nesting!

So what are we missing here? Endpoints should contain only resources (nouns) and use HTTP methods for actions! Here is what the endpoints should look like after we fix them. You will later learn that this constraint is called a Uniform interface. Also, note that we use plural nouns to construct URLs from resources as a convention. So we make use of \blogs and not \blog.

For the nested case, let’s say if we want to get all publications for a particular user, we will use something like —

GET /users/{{userId}}/publications

Here userId is a unique identifier for a particular user. You can see that Medium indeed uses this endpoint for the same purpose!

Send data as JSON (usually)

JSON (JavaScript Object Notation) is a lightweight data-interchange format. Usually, when the client and server interact, any data exchanged should be in JSON format. Furthermore, data sent must follow some specifications such as JSend (most common), JSON: API, or OData JSON protocol.

A basic JSend-compliant response is as simple as this. You can read more here.

{
status : "success",
data : {
"post" : { "id" : 1, "title" : "A blog post", "body" : "Some useful content" }
}
}

Be Stateless

REST APIs must be stateless. All state is handled on the client-side. Each request must contain all the information necessary to process a specific request. The server should not have to remember previous requests.

For instance, let’s say that you are currently viewing all blogs on page number 6. If you want to read the blogs on page seven, then request below does not let

GET /blogs/nextPage  -- bad practice 

the server be stateless. This is because some data needs to be stored on the server-side to track what page was last sent in response to a previous request. On the other hand a request where we specify the page

GET /blogs/page/6    -- good practice

number (or id) does not need any state to be handled on server side and is considered a good practice.

Another concern that might arise in this context is caching. Using a cache on the server-side can help resolve queries much faster. However, one must notice that caching does not mean that the server is storing the app state. It’s merely copying the data for the previous requests and putting it in a memory that is fast and can be accessed in case of future requests.

Architectural constraints

Thus finally, let’s look at the six REST design principles. You will notice that, we have already covered several of the points here.

1. Uniform interface

All API requests for the same resource should look the same, no matter where the request comes from. The REST API should ensure that the same piece of data, such as the name or email address of a user, belongs to only one uniform resource identifier (URI). Resources shouldn’t be too large but should contain every piece of information that the client might need.

2. Client-server decoupling

In REST API design, client and server applications must be completely independent of each other. The only information the client application should know is the URI of the requested resource; it can’t interact with the server application in any other ways. Similarly, a server application shouldn’t modify the client application other than passing it to the requested data via HTTP.

3. Statelessness

REST APIs are stateless, meaning that each request needs to include all the information necessary for processing it. In other words, REST APIs do not require any server-side sessions. Server applications aren’t allowed to store any data related to a client request.

4. Cacheability

When possible, resources should be cacheable on the client or server side. Server responses also need to contain information about whether caching is allowed for the delivered resource. The goal is to improve performance on the client side, while increasing scalability on the server side.

5. Layered system architecture

In REST APIs, the calls and responses go through different layers. As a rule of thumb, don’t assume that the client and server applications connect directly to each other.

There may be a number of different intermediaries in the communication loop, each with a specific responsibility. A good design ensures that layers are isolated, don’t communicate with other layers except for the ones beneath them.

6. Code on demand (optional)

REST APIs usually send static resources, but in certain cases, responses can also contain executable code (such as Java applets). In these cases, the code should only run on-demand.

Other best practices

We have already discusses the following best practices in the above paragraphs — accept and respond with JSON, use nouns instead of verbs in endpoint paths, name collections with plural nouns, nesting resources for hierarchical objects, and cache data to improve performance.

There are several other things you can take care of. These are things that do not violate the principles as such but are essential for a good API design.

Handle errors gracefully and return standard error codes

To eliminate confusion for API users when an error occurs, we should handle errors gracefully and return HTTP response codes that indicate what kind of error occurred. Some common error HTTP status codes can be found here.

Error codes need to have messages accompanied with them so that the maintainers have enough information to troubleshoot the issue, but attackers can’t use the error content to carry our attacks like stealing information or bringing down the system.

Allow filtering, sorting, and pagination

The databases behind a REST API can get very large. Sometimes, there’s so much data that it shouldn’t be returned all at once because it’s way too slow or will bring down our systems. Therefore, we need ways to filter items.

We also need ways to paginate data so that we only return a few results at a time. We don’t want to tie up resources for too long by trying to get all the requested data at once. Sorting helps fetch ordered data as per requirement.

Good security practices

Most communication between client and server should be private since we often send and receive private information. Therefore, using SSL/TLS for security is a must.

A SSL certificate isn’t too difficult to load onto a server and the cost is free or very low. There’s no reason not to make our REST APIs communicate over secure channels instead of in the open.

Versioning our APIs

We should have different versions of API if we’re making any changes to them that may break clients. The versioning can be done according to semantic version (for example, 2.0.6 to indicate major version 2 and the sixth patch) like most apps do nowadays. You can read more about this here.

This way, we can gradually phase out old endpoints instead of forcing everyone to move to the new API at the same time. The v1 endpoint can stay active for people who don’t want to change, while the v2, with its shiny new features, can serve those who are ready to upgrade. This is especially important if our API is public. We should version them so that we won’t break third party apps that use our APIs.

Versioning is usually done with /v1/, /v2/, etc. added at the start of the API path.

Conclusion (TL;DR)

So ready to design REST APIs like a Pro? 🙃

Let’s quickly summarise the entire article in the next three paragraphs.

The most important takeaway for designing high-quality REST APIs is to have consistency by following web standards and conventions. JSON, SSL/TLS, and HTTP status codes are all standard building blocks of the modern web.

Performance is also an important consideration. We can increase it by not returning too much data at once. Also, we can use caching so that we don’t have to query for data all the time.

Paths of endpoints should be consistent. We use nouns only since the HTTP methods indicate the action we want to take. Paths of nested resources should come after the path of the parent resource. They should tell us what we’re getting or manipulating without the need to read extra documentation to understand what it’s doing.

If you have read this and found it useful, you might also like —

Feel free to reach out to me for any questions or suggestions that you might have. You can follow me on these social media platforms — LinkedIn, YouTube, Twitter, Instagram for more updated content!

As always! Happy Hacking! 😇

--

--

AB Satyaprakash
CodeX
Writer for

Engineering @Oracle | Applications Intern @Oracle | MLSA | MLH | Developer | Competitive Programmer | Mathematics and Computing @IIT Guwahati