Top 14 REST API Design Best Practices to Follow

Ragavi S PRagavi S P
9 min read

Before getting started with a REST API design, you need a Functional Specification. It sets out what the system does, not how it does it. It might include a few mockup screenshots and information flow diagrams, but it is essentially a high-level document. By contrast, the API design gets into the nitty-gritty of how it works.

A quick overview

The web has endless sites and pages advocating “API design”, “Best Practices” and so on. API design is about 80% commonsense and the rest, technical.

A web API exposes server data to client users and accepts requests back from them. Most consumer web traffic is data served to clients by request. In the opposite direction, clients supply login data, make purchases, fill out government forms, and the like.

On the server, information is typically stored in a database like MySQL. The client API encapsulates activities on the database such as downloading information, storing new client information, and changing client information.

The basic idea is to split the API by major data categories reflecting the main database tables. Although client API requests translate into database operations on the server, the API client does not know anything about underlying databases.

For example, a commercial retail site will have data about customers, products, manufacturers, and more. Visit a site like Amazon to get the idea.

In API terminology, the API target will be a URL like https://www.example.com and data categories will be described by endpoints such as /customers, /products, etc. A fully qualified URL endpoint will look like this: https://www.example.com/customers.

Separate endpoints may be required for more complex transactions, perhaps yielding a database view that is returned to the client in a JSON or XML file. Finally, a query may be added to the endpoint, filtering the output: https://www.example.com/customers/cust_details?yourname=”John Doe”&custNum=123456

Within each endpoint, you provide definitions, templates, and examples for the relevant REST calls: GET, POST, PUT, PATCH, DELETE, and others.

Other elements

Formal API documentation is an integral part of the API design. Without it, nothing makes sense.

The API design must also clearly specify:

HTTP requests input and output payload formats, typically JSON or XML when present. HTTP return codes and their meaning in the specific API context: Everyone knows that a 404 response means “Page not found” but what exactly was not found in the API context and what might be done to correct the error?

Elements of REST API design

A REST API is built around three players:

Client-side

On the client side, we have already met some of the commands. Very few client-side developers will issue these commands directly. You might test them in a sandbox using cURL but more likely, you will use a program library. The most ubiquitous client-side programming environment is JavaScript: The fetch() call handles HTTP/HTTPS requests. There are associated routines for setting up and parsing JSON files. A good starting resource for this is MDN web docs - JavaScript.

Some API suppliers provide dedicated libraries for client-side developers that effectively encapsulate the HTTP REST API calls. That can work well for commercial-style APIs we all know. It may be less effective for B2B APIs that require or return vast quantities of data.

Server-side

What happens on the server side is the key to everything and there are no hard and fast rules. This is the “80% commonsense”. The server must supply information or solicit user responses using the REST API commands as the base of the user interface. A server-side developer can do a substantial amount of design and testing using a local Node.js installation. Node.js allows you to set up a server on your development machine (https://localhost:<port_number>) for development and testing. See Node.js as a starting point.

Resources

Resources are entities that exist on the server, accessible through HTTP GET commands. Sometimes a resource is not a database object, but a backend procedure to be called that carries out the request with any client-supplied data. A resource may be created by a POST command. It may duplicate an existing resource. A PUT command will update an existing resource or create a new one if there is nothing to update To update a resource (say a single field in a database) without replacing it, use the PATCH command The DELETE command will delete a resource Importance of good API design

Importance of good API design

The good

A good API design will attract users; a poor API design will put them off and make them look elsewhere. If your API is part of a commercial offering, then that translates into profit or loss.

The bad

At the technical level, there are further considerations: Your API may look great, be easy to use and have everything going for it – except that it is painfully slow. The slow performance will also put off users translating into a loss of revenue.

The Ugly

And what happens if all else is good but the server-side programming style is a “spaghetti code”? Bug corrections are difficult and updates become a nightmare. Poor server-side coding will result in increased maintenance costs. There are many commercial offerings that assist with API design but again, there is no substitute for common sense.

API design best practices

Ensure that the API scales

The API must solve real-world challenges: Test it under load and with excessively long output.

Use an international design standard

The OpenAPI v3 spec is a good start. Look here, OpenAPI Specification and also here: Swagger Editor.

As simply as possible, but not any less

This is a style issue, but it can affect performance: When responding to a query, provide all the information required but no more. If you are required to provide three fields out of a large record, then a JSON file of all records containing those fields is overkill and even counterproductive. It will slow the response and may even cause a bottleneck situation. It may also lead to a buffer overflow on the client side.

Make use of REST

There are “devices” to bypass REST the most common being methods to maintain state. The most well-known are cookies. They have their place and most sites use them, typically to supply session user credentials for each request. Going outside the REST convention may even be a security risk.

Endpoint paths should be written with nouns rather than verbs

This endpoint, https://www.example.com/customers uses a noun - customers. The next kind of example, https://www.example.com/listingCustomers should be avoided. The only verbs in REST API are the GET, POST, PUT, PATCH, DELETE, etc commands.

Use HTTP methods for CRUD functions

The acronym CRUD stands for Create, READ, Update, and Delete. The corresponding HTTP methods should be used and not bypassed using “smart” programming tricks. You can access a remote database without going through HTTP; don’t it’s not platform agnostic.

Use with HTTP response status codes

The HTTP response status codes are documented here: HTTP response status codes.

HTTP response status codes indicate whether a specific HTTP request has been successfully completed. Responses are grouped into five classes:

  1. Informational responses (100 – 199)

  2. Successful responses (200 – 299)

  3. Redirection messages (300 – 399)

  4. Client error responses (400 – 499)

  5. Server error responses (500 – 599)

In many contexts, we will require a 200 response indicating success. 400 responses can often be corrected on the client side and the request resubmitted. 500 responses may require a ”Comeback later” action. An exception is 511, Network Authentication Required issued by a proxy controlling server access. It means that the client credentials were incorrect.

Add filtering, sorting, and pagination

In a GET query, all three are accomplished by adding parameters after the endpoint. There is no standard method and much depends on the backend developer. See REST API Design: Filtering, Sorting, and Pagination.

One interesting method of pagination is based on the following paradigm:

  1. With the GET query, supply a required page length in lines.

  2. The GET returns the first page of data and a special continuation URL, all in a JSON file.

  3. Do repeat GETs using the continuation URL until there is no further data.

Enforce security

Enforce the use of HTTPS for data transfer, rather than HTTP. This is a large topic well beyond the scope of a short blog. As a starting point, look here: Content Security Policy (CSP) here: Best practices for REST API security: Authentication and authorization, and here: REST API Security Essentials

From the last link, we have a quick checklist:

  1. Keep it Simple. Secure an API/System – just how secure it needs to be.

  2. Always Use HTTPS. Always use SSL.

  3. Use Password Hash. Always encrypt passwords. Never send them in clear text.

  4. Never expose sensitive information on URLs. Usernames, passwords, session tokens, and API keys should not appear in the URL, as this can be captured in web server logs, which makes them easily exploitable.

  5. Consider OAuth for authorization.

  6. Consider Adding Timestamp in each request. Do it in a custom HTTP header. Input Parameter Validation. We mentioned this above. Reject the request if parameter validation fails.

Add Cache Data

Caching stores copies of frequently accessed data. Caching response data can

Reduce bandwidth usage Reduce response latency Reduce load on servers Temporarily hide network failures

GET requests are automatically cached. PUT and DELETE are not. Caching can be controlled using cache control headers in a request. See a tutorial on caching here: Caching REST API Response.

  1. API Versioning

  2. Versioning is required for these scenarios:

  3. Bug correction Adding new features Full new release

  4. A typical versioning scheme is based on a three-digit code:

API Versioning

Versioning is required for these scenarios:

  1. Bug correction

  2. Adding new features

  3. Full new release

A typical versioning scheme is based on a three-digit code:

<Major-Release\>.<Minor-Release\>.<Maintenance-Build-Level\>

Bug correction must not make any changes to the API. It is transparent to the API user. It increments the Maintenance-Build-Level.

Adding new features may not change the existing API calls in any way. The API user is free to use them or not. The Minor-Release number is incremented.

A Major-Release may not necessarily be backward compatible. That requires that the previous release remain available at least during a known deprecation period.

All this leads to the issue of how the version number should be included in an API call so existing client code is not broken. Here is one approach to the subject: How to Version a REST API. Another succinct description is here: Four REST API Versioning Strategies.

Define the desired capability and how to obtain it while adhering to current standards

Usually, you would use the OpenAPI standard. You can use Swagger to prototype the API following the standard. Here is our first exposure to the need to carry out the design and implementation iteratively with defined project milestones to assess progress. This is an important project management issue outside the scope of an informal blog.

Consider building good client-side implementations rather than just network access

This is basically the difference between using a client-side SDK (Software Development Kit) and a raw cURL command line request. Most of the client-side programming environments supply such SDKs as built-in libraries or packages. JavaScript fetch() API does it, although it needs a fair amount of “setup” for a call. There are several commercial offerings that package or replace the fetch call to make life easier for the developer. See, for example, 10 Best JavaScript HTTP Request Libraries.

Focus on use-cases

This is a documentation issue. Many CCMSs (including Document360) provide a three-way split window with a brief ToC on the left, documentation text in the middle, and sample code on the left, an option with a choice of developer language. If possible, the sample code should provide non-trivial results.

Some API documentation tools provide a sandbox in which you can try real-life HTTP requests. Alternatively, the sandbox may have to be linked to the CCMS documentation tool.

Read more on https://document360.com/blog/api-design-best-practices/

0
Subscribe to my newsletter

Read articles from Ragavi S P directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ragavi S P
Ragavi S P