4 Best Practices of RESTful API You Should Know

4 Best Practices of RESTful API You Should Know

Sunny Sun Lv4

Use these tips to craft a solid RESTFUL design

REST is a widely-used architectural style for building APIs. It’s known for being intuitive, flexible, and easy to learn, making it a popular choice among developers.

However, despite its simplicity, REST can also be misconstrued and is not always fully understood by those who work with it. While most developers have likely had some experience with REST, few can truly consider themselves experts in its use.

In this article, we will discuss a few best practices of REST API:

Select the Right Granularity of the RESTful Endpoint

Granularity is a relative measurement of the extent of resources an API exposes. The fine-grained APIs expose a small number (or single) resources, and the coarse-grained API return information from many resources in a single request.

Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
# coarse-grained  
/v1/user/1/blog
{
“posts”: [],
“comments”: [],
“profile”: { “name”: “user 1}
}
# fine-grained
/v1/user/1/posts
/v1/user/1/comments
/v1/user/1/profile

Choosing the correct granular size is important. A too-fine-grained API will lead to chatty API calls, performance issues, complex architecture, and increased maintenance costs. In the above example, we need to make three requests: /user/{userID}/posts, /user/{userID}/comments, and /user/{userID}/profile to get information about the user blog. In contrast, a single call /user/{userID}/blog will provide the same amount of information.

Conversely, an overly coarse-grained API will be inflexible, have scalability issues, and is hard to reuse.

Coarse-grained API is preferred when performance is a priority. For Get request, using coarse-grained API make it possible to get all information required with one call. It means reduced load on a web server and also better bandwidth latency.

Typical use cases for fine-grained API include mobile apps and Post/Put requests.

There is no one-size-fits-all rule for API Granularity. The design choice is a balance between several often conflicting factors, including the following:

  • The size of the response payload
  • The number of API calls
  • Maintainability and reusability
  • Scalability
  • Business needs

In a real-world project, you will find the result is often a mix of fine-grained and coarse-grained API. The bottom line is that the decision is made to satisfy the business requirement and other technical considerations. A well-designed API will be granular enough to expose necessary details but not too granular to be too chatty to use.

Do NOT Build Your API Based on Database Tables

Restful API is designed around resources. A resource can be loosely linked to business entities instead of database tables.

It is easy to mirror the API endpoints based on internal database tables. For example, for the following data structure

1
2
3
4
5
6
7
8
9
10
11
Orders  
---------
id (integer)
customer_id (integer)
status (string)

OrderItems
---------
id (integer)
order_id (integer)
product_id (integer)

It might be tempting to build API based on the tables, such as

1
2
3
4
5
6
7
8
9
GET /orders  
POST /orders
GET /orders/{id}
...

GET /orders/{id}/items
POST /orders/{id}/items
GET /orders/{id}/items/{item_id}
...

But this approach has several issues. First, it exposes too much information about the underlying database structure. we create a tight coupling between the API interface and database implementation. It will only make the API hard to maintain. Thus we should not expose (leak)the internal database structure to the client.

Second, it requires clients to make multiple requests to retrieve all the information about an order (e.g. the order details and the order items). This can be inefficient and slow.

A better approach would be to design a more coarse-grained API that provides all the necessary information in a single request:

1
2
3
4
GET /orders?customer_id={customer_id}&status={status}  
POST /orders
GET /orders/{id}
...

How can we separate the domain concept from the underlying data storage when designing a REST API?

The key is to break away from a database-driven mindset and resist the temptation of quickly jumping into implementation details. Here are some tips for food for thought:

  • Think of the REST API from the business domain perspective instead of the database. Gather business requirements to establish the boundary context of the API. Ask lots of questions, like, why do we need the API? What are the new capabilities to be provided? Who are the users? What are the use cases?
  • Isolate the API interface from underlying implementation details. A good API design is a stable API that can last long while the implementation changes. For example, the database schema will evolve or be reimplemented with a different database. Those internal changes won’t affect the API interface if the API is designed with proper abstraction.
  • Single Responsibility Principle. A good REST API should focus on one business area and do it well. Creating an API that manages multiple responsibilities will make it unnecessarily complex and hard to maintain.

Consume HATEOAS Data To Make Your Client Smart

HATEOAS (Hypermedia as the Engine of Application State) is an interface of REST API which provides navigable links to related endpoints in API response. It is a principle of REST that the API should be self-descriptive, and any consumer should be able to discover and communicate with the API using the links provided.

HATEOAS is an intuitive way of expressing relationships between current resources and others. Below is an example response for a user blog API endpoint /v1/user/1/blog. As you can see, the related links for posts and comments are embedded in them.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{  
“id”: 1,
“name”: “John Duggan’s Blog”,
“links”: [
{
“href”: “1/posts”,
“rel”: “posts”,
“type” : “GET”
},
{
“href”: “1/comments”,
“rel”: “comments”,
“type” : “GET”
}
]
}

With HATEOAS, the client can discover the next action it can carry forward. Compared with hard-coded URL links in the client, the HATEOAS links make the client code more dynamic and flexible.

From an architectural perspective, HATEOAS allows a loosely coupled system. When the client implements the dynamic links using HATEOAS, the server can change the URL without updating the client code.

In Martin Fowler’s article about the REST Richardson maturity model , REST API is considered level three when HATEOAS is implemented for discoverability. Unfortunately, although HATEOAS is considered to be an essential part of REST design, it is often forgotten in real-world projects.

Although not every REST API project will have a use case for HATEOAS, it can be a very powerful feature when used in the right place. Some good use cases I have worked on include the following:

  • Workflow App: HATEOAS links returned can represent the current state, i.e., the collection of links represents all the possible actions available for the next step. The benefit is that the server manages all the business logic, and the client becomes reactive.
  • Manage user permissions: HATEOAS links are a natural way to pass the permissions for a resource. For example, a client app will send a Get request in loading a new screen, and the links in the Get response represent the allowed actions for the current user. Thus, the client can react to the user permissions accordingly (show/hide the buttons, etc.).

Handling Errors With a Consistent Pattern

Handling error is as important as handling successful responses in a REST API. With solid error handling, developers will find it easier to troubleshoot, and the app will also be easier to maintain.

A reliable error-handling framework must have a global error handler to capture and process errors. Depending on the nature of the error, we should use an appropriate HTTP status code and map the error to a readable and informative error message. A global error handler will ensure an error is handled and returned with a consistent and concise error message.

The key has a standard error response structure for the project. The error response should contain at least “code” and “message” key-value pairs and be able to hold multiple errors. Below is a simple example:

1
2
3
4
5
6
7
8
9
10
11
12
{  
“errors”: [
{
“code”: “10003”,
“message”: “invalid address field”
},
{
“code”: “10004”,
“message”: “birthday field is requried”
}
]
}

Please note that the code and message field should not be the technical error code and message directly from the server. Since the response from API is exposed to clients, they need to be translated into user-friendly and readable messages, and you don’t want to leak unnecessary technical details.

Of course, you can add more properties according to your needs. Some common properties include the following:

  • severity: it can be error | warning | info
  • suberrorcode: a lower-level error code
  • traceId: id for identifying the source of the error
  • correclationId: an internal ID to trace in the log for a complex flow.
  • stacktraces: the active stack frames when an error occurs. It normally should be disabled in production. Be careful not to expose sensitive information when using this field.

A useful reference is RFC 7807 : problem details for HTTP APIs. It provides guidelines on how to carry machine-readable details of errors in an HTTP response.

The client can react to the error effectively with a consistent message structure of Error response. It can also make the life of developers easier.

Summary

REST APIs are relatively easy to start, especially when using well-established frameworks. You can create a simple “hello world” service with just a few steps. However, building a solid REST API is not always straightforward, as there are no strict rules to follow. Instead, creating a well-designed API requires a balance of technical considerations, constraints, and business needs. It’s not easy, but it’s worth it for the benefits it can bring.

I hope you can learn one thing or two from this article.

Happy coding!

  • Title: 4 Best Practices of RESTful API You Should Know
  • Author: Sunny Sun
  • Created at : 2022-11-28 00:00:00
  • Updated at : 2024-07-12 14:23:13
  • Link: http://coffeethinkcode.com/2022/11/28/4-best-practices-restful-api-you-should-know/
  • License: This work is licensed under CC BY-NC-SA 4.0.