REST API Design Best Practices

Hey guys, in this article, we will discuss a few best practices for Restful API's design. This post belongs to my favorite Java Best Practices Series category. Before designing and developing Rest API, I suggest you read this article to develop a good Restful API.

What is REST?

The REST stands for REpresentational State Transfer.

Let's understand the meaning of each word in the REST acronym.

  •  State means data
  •  REpresentational means formats (such as XML, JSON, YAML, HTML, etc)
  •  Transfer means carrying data between consumer and provider using the HTTP protocol
Here are a few best practices to design a clean RESTful API.

1. Use Nouns for Resource Identification

The fundamental concept of a REST-based system is the resource. A resource is anything you want to expose to the outside world, through your application.

Example 1: Resources for Employee Management System:

- Employee

- Department

- Projects

- Task

- Address

Example 2: Resources for Student Management System:

- Student

- Teacher

- School

- Class

- Subject

A resource has an identifier, which is a URI that uniquely identifies that resource.

It is best practice to use nouns as resource identification.

For an easy understanding use this structure for every resource:
  • GET - /users - Returns a list of users
  • GET - users/100 - Returns a specific user
  • POST - /users - Create a new user
  • PUT - /users/ - Updates a specific user
  • DELETE - /users/711 - Deletes a specific user
Here is REST API design that I created before writing REST API. Refer to the URI that uniquely identifies the Employee resource:

Bad Practice: Do not use verbs like:

/getAllUsers
/getUserById
/createNewUser
/updateUser
/deleteUser

2. Use Plural Nouns to Name a Resource

When you have to develop the resource in REST API, just go with plural nouns. Don't mix up singular and plural, use plural nouns to name a resource.

For example:
Use /students instead of /student
Use /employees instead of /employee
Use /orders instead of /order
Use /users instead of /user
Use /customers instead of /customer

3. Use Proper HTTP Headers for Serialization Formats

Both client and server, need to know which format is used for the communication. The format has to be specified in the HTTP-Header.
Content-Type defines the request format.
Accept defines a list of acceptable response formats.
On the server-side, an incoming request may have an entity attached to it. To determine its type, the server uses the HTTP request header Content-Type. 

Some common examples of content types are “text/plain”, “application/xml”, “text/html”, “application/json”, “image/gif”, and “image/jpeg”.
Content-Type: application/json
Similarly, to determine what type of representation is desired on the client-side, an HTTP header ACCEPT is used. It will have one of the values mentioned for Content-Type above.
Accept: application/json

4. Get Method and Query Parameters Should Not Alter the State

Use PUT, POST, and DELETE methods instead of the GET method to alter the state. Do not use GET for state changes:
GET /users/711?activate or
GET /users/711/activate

5. Use Sub-Resources for Relations

If a relation can only exist within another resource, RESTful principles provide useful guidance.

In REST, the relationships are often modeled by a sub-resource. Use the following pattern for sub-resources.
GET /{resource}/{resource-id}/{sub-resource}

GET /{resource}/{resource-id}/{sub-resource}/{sub-resource-id}

POST /{resource}/{resource-id}/{sub-resource}
Example: 

GET  /{post}/{post-id}/{comments}

GET  /{post}/{post-id}/{comments}/{comment-id}

POST /{post}/{post-id}/{comments}

Use sub-resources child object cannot exist without its parent.

Few more examples for sub-resources:
GET /cars/711/drivers/ Returns a list of drivers for car 711
GET /cars/711/drivers/4 Returns driver #4 for car 711
These messages can be logically mapped to the /tickets endpoint as follows:
  • GET /tickets/12/messages - Retrieves a list of messages for ticket #12
  • GET /tickets/12/messages/5 - Retrieves message #5 for ticket #12
  • POST /tickets/12/messages - Creates a new message in ticket #12
  • PUT /tickets/12/messages/5 - Updates message #5 for ticket #12
  • PATCH /tickets/12/messages/5 - Partially updates message #5 for ticket #12
  • DELETE /tickets/12/messages/5 - Deletes message #5 for ticket #12

6. Use Proper HTTP Methods (Verbs)

Using the right HTTP methods (or verbs) when designing and consuming RESTful APIs is crucial. It ensures clarity, adheres to the principles of REST, and simplifies understanding for developers who interact with the API.

1. GET 

Purpose: Retrieve data. 
  • It should only fetch data and should have no other side effects. 
  • Data retrieval should be idempotent, meaning multiple identical requests should have the same effect as a single request. 
Example: Retrieve a list of books.
GET /api/books

2. POST 

Purpose: Create a new resource. 
  • Used when you want to submit data to be processed to a specified resource. 
  • After a successful POST request, a 201 Created status code should be returned if a new resource was created. 
Example: Add a new book.
POST /api/books

3. PUT 

Purpose: Update an existing resource or create it if it doesn't exist. 
  • When used for updates, it should typically update the resource entirely with the provided data.
  • Idempotent: Multiple identical requests should have the same effect. 
Example: Update details of a specific book with ID 123.
PUT /api/books/123

4. PATCH 

Purpose: Apply partial modifications to a resource. 
  • Instead of updating an entire resource, PATCH applies a partial update. 
  • Not guaranteed to be idempotent, though it can be. 
Example: Update the title of a specific book with ID 123.
PATCH /api/books/123

5. DELETE 

Purpose: Remove a specific resource. 
  • It requests the server to remove a resource.
  • Idempotent: After deleting a resource, making the same DELETE request again should return the same result, typically 404 Not Found. 
Example: Delete a book with ID 123.
DELETE /api/books/123

7. HTTP Response Status Codes

When the client raises a request to the server through an API, the client should know the feedback, whether it failed, passed or the request was wrong. HTTP status codes are a bunch of standardized codes which has various explanations in various scenarios. The server should always return the right status code.
Some of the frequently used status codes in this class are as follows:
  • 200 OK: This code indicates that the request is successful and the response content is returned to the client as appropriate.
  • 201 Created: This code indicates that the request is successful and a new resource is created.
  • 400 Bad Request: This code indicates that the server failed to process the request because of the malformed syntax in the request. The client can try again after correcting the request.
  • 401 Unauthorized: This code indicates that authentication is required for the resource. The client can try again with appropriate authentication.
  • 403 Forbidden: This code indicates that the server is refusing to respond to the request even if the request is valid. The reason will be listed in the body content if the request is not a HEAD method.
  • 404 Not Found: This code indicates that the requested resource is not found at the location specified in the request.
  • 500 Internal Server Error: This code indicates a generic error message, and it tells that an unexpected error occurred on the server and that the request cannot be fulfilled.

8. Field Name Casing Convention

You can follow any casing convention, but make sure it is consistent across the application. If the request body or response type is JSON then please follow camelCase to maintain consistency.
The below example uses camelCase as JSON field name:
	{
		"firstName": "Ramesh",
		"lastName": "Fadatare",
		"id": 100,
		"userName": "Ramesh Fadatare",
		"email": "[email protected]"
	}

9. Searching, Sorting, Filtering, and Pagination

For searching, sorting, filtering, and pagination no need to create a new REST API, you can support these operations in the existing GET REST API, just append the query params with the GET REST API.
For example:
  • Sorting In case, the client wants to get the sorted list of companies, the GET /companiesendpoint should accept multiple sort params in the query. E.g GET /companies?sort=rank_asc would sort the companies by their rank in ascending order.
  • Filtering For filtering the dataset, we can pass various options through query params. E.g GET /companies?category=banking&location=india would filter the companies list data with the company category of Banking and where the location is India. 
  • Searching When searching for the company name in the companies list the API endpoint should be GET /companies?search=Digital .
  • Pagination When the dataset is too large, we divide the data set into smaller chunks, which helps in improving the performance and is easier to handle the response. Eg. GET /companies?page=23 means get the list of companies on the 23rd page.

10. Restful API Versioning

API versioning is the practice of transparently managing changes to your API.

Here are the 4 ways of versioning a REST API.

1. Versioning through URI Path

2. Versioning through query parameters

3. Versioning through custom headers

4. Versioning through content negotiation

Versioning through the URI Path strategy is widely used to version the REST API.

To version the REST APIs, just include the version number in the URI path.

Examples:

http://www.example.com/api/1/products

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

http://www.example.com/api/v2/products

http://www.example.com/api/v1/posts

http://www.example.com/api/v1/employees

Read more about versioning REST APIs at https://www.javaguides.net/2021/04/rest-apis-versioning.html

Comments