Best Practices for Designing a Modern REST API

Author: Francis Ndungu

Last Updated: Thu, Jul 29, 2021
Best Practices Programming

Introduction

An Application Programming Interface (API) is middleware that allows applications to talk to each other. For instance, when you run a mobile application like Facebook, it uses an API to connect to remote data. APIs are the standard way to interchange data in the modern IT industry because they offer numerous benefits. For example, mobile, desktop, and web applications can share data fetched by an API. APIs also speed up the development of in-house software that integrates with third-party applications. In most cases, you'll develop an API using a server-side scripting language like PHP, Python, or Node.js.

This guide focuses on some best practices to consider when creating a developer-friendly API.

1. Use JSON Data Exchange Format

Accept and respond to API requests with the most common format for data exchange. While some legacy APIs still use Extensible Markup Language (XML)), the JavaScript Object Notation (JSON)) format is more compact, and most developers agree that it is both human and machine-readable as shown below.

{
  "data" : {
    "product_id" : 123,
    "product_name" : "SPORTS BIKE",
    "retail_price" : 2499.36,
    "remaining_stock" : 786
  }
}

The following code is the equivalent of the above JSON data in XML.

<data>
  <product_id>123</product_id>
  <product_name>SPORTS BIKE</product_name>
  <retail_price>2499.36</retail_price>
  <remaining_stock>786</remaining_stock>
</data>

As you can see, JSON is prettier than XML; therefore, consider it when developing new APIs. In addition, most programming languages like PHP offer different tools for parsing and formating JSON data, undoubtedly allowing faster data accessibility.

2. Use Nouns Instead of Verbs in URL Paths

When creating your API URL endpoints, consider using nouns instead of verbs. Then, accept the different HTTP request methods(GET, POST, PUT, and DELETE) to fulfill the different data operations. This makes the URLs shorter and neat. Also, it minimizes the total number of URLs needed to complete CRUD(Create, Read, Update and Delete) operations for a single resource.

To put this in a better perspective, consider the following API endpoints that utilize verbs in the URLs.

https://www.example.com/api/v1/create_product
https://www.example.com/api/v1/get_products
https://www.example.com/api/v1/update_product
https://www.example.com/api/v1/delete_product

You can neatly merge the above URLs to a single resource path as shown below.

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

Then, API consumers can use the different HTTP verbs to retrieve, submit, update, and delete data by executing the below requests.

  • Retrieve products:

    GET  https://www.example.com/api/v1/products
    
  • Create a product:

    POST https://www.example.com/api/v1/products
    
  • Update a product:

    PUT  https://www.example.com/api/v1/products/{product_id}
    
  • Delete a product:

    DELETE  https://www.example.com/api/v1/products/{product_id}
    

3. Respond with the Correct HTTP Status Codes

Consider using the different Hypertext Transfer Protocol (HTTP) status codes to issue responses to clients' requests as shown below.

  1. GET https://www.example.com/api/v1/products

    Response:

    200 OK
    
  2. PUT https://www.example.com/api/v1/products/{product_id}

    Response:

    200 OK
    
  3. POST https://www.example.com/api/v1/products

    Response:

    201 Created
    
  4. DELETE https://www.example.com/api/v1/products/{product_id}

    Response:

    204 No Content
    

Also, use the following HTTP status codes when returning errors.

  1. Resource not found on the server

    404 Not Found
    
  2. Authentication Failed. Incorrect username, password, or access token.

    401 Unauthorized
    
  3. Authorization Failed. Use this error when a user is properly authenticated but doesn't have the correct privileges to access a resource.

     403 Forbidden
    
  4. Bad Request. Errors from the client-side. For instance, data validation errors.

    400 Bad Request
    
  5. Server-related errors including database failure.

     500 Internal Server Error
    

Also, even after returning an HTTP status code when an error is encountered, you should inform the end-user of the exact reason why the problem occurred. For example, when returning errors in JSON format to the end-user, you may include: The HTTP status code. A custom error code. A short title. A detailed human-readable message that explains why the problem occurred.

Here are a few samples of error messages.

  1. Authentication Error

    {
        "errors" : {
        "http_status_code" : 401,
        "title" : "Invalid Login",
        "message" : "The username/password is incorrect."
        "custom_error_code" : 401.1
      }
    }
    
  2. Validation Error

    {
        "errors" : {
        "http_status_code" : 400,
        "title" : "Validation Error",
        "message" : "The 'product_name' field is required."
        "custom_error_code" : 400.1
      }
    }
    
  3. Authentication Error

    {
        "errors" : {
        "http_status_code" : 403,
        "title" : "Unauthorized",
        "message" : "You're not authorized to access this resource."
        "custom_error_code" : 403.1
      }
    }
    

4. Secure the API Endpoints

Unless you're creating a publicly available API that does not require users to log in, you must utilize some form of authentication and authorization in your API endpoints.

Authentication proves the identity of an API consumer and verifies usernames, passwords, and access tokens. On the other hand, authorization specifies the different privileges and rights assigned to an end-user.

You should implement a clear policy for granting and denying access to the different resources in your API endpoints. In a more advanced scenario, you might consider using roles and permissions to the group and assign privileges to the API users.

5. Allow Users to Page API Data

Your API should return a subset of data to save on bandwidth and avoid overwhelming the server. For example, if you're offering a dropshipping endpoint that returns thousands of products, allow your API consumers to specify the number of products they want to retrieve on each page by specifying a per_page URL parameter. Also, make sure the users can navigate between the pages using a page parameter.

For instance, the URL below specifies a page size off 250 products, starting from page 1.

GET  https://www.example.com/api/v1/products?page=1&per_page=250

Similarly, to navigate to pages 2 and 3, your API consumers can use the URLs below.

GET  https://www.example.com/api/v1/products?page=2&per_page=250
GET  https://www.example.com/api/v1/products?page=3&per_page=250

API consumers with constrained resources like mobile users may specify a small page size to keep network traffic in check by using the following endpoints with a page size of 10 products.

GET  https://www.example.com/api/v1/products?page=1&per_page=10
GET  https://www.example.com/api/v1/products?page=2&per_page=10
GET  https://www.example.com/api/v1/products?page=3&per_page=10

Since you're most likely to return data from a database, you can achieve the paging functionality using the SQL LIMIT clause.

In addition, it's a good practice to include a meta object that displays paging information, including the current page number(page), page size(per_page), and total records(count) returned from a requested resource as shown below.

{
  "data" : {
    "product_id" : 123,
    "product_name" : "SPORTS BIKE",
    "retail_price" : 2499.36,
    "remaining_stock" : 786
  },

  ...

"meta" : {
    "page" : 1,
    "per_page" : 3,
    "total_pages" : 5,
    "count" : 15
  }

}

6. Accept Sorting Parameters in API Endpoints

Allow your API consumers to specify sorting parameters in the URL to sort data in ascending order. On the other hand, a minus sign - can be prefixed to the field name to sort data in descending order. Here are a few examples of how sorting URLs should look like.

  • Sort data by product_name in ascending order.

    GET  https://www.example.com/api/v1/products?sort=product_name
    
  • Sort data by product_name in descending order.

    GET  https://www.example.com/api/v1/products?sort=-product_name
    
  • Sort data by retail_price in ascending order.

    GET  https://www.example.com/api/v1/products?sort=retail_price
    
  • Sort data by retail_price in descending order.

    GET  https://www.example.com/api/v1/products?sort=-retail_price
    
  • Sort data by retail_price and then by product_name in ascending order.

    GET  https://www.example.com/api/v1/products?sort=retail_price,product_name
    
  • Sort data by retail_price and then by product_name in descending order.

    GET  https://www.example.com/api/v1/products?sort=-retail_price,-product_name
    

7. Allow Users to Filter and Search API Data

Just like sorting data, your API should provide a way to filter data using URL parameters. For instance, to search an item by its name, API consumers can make the following request.

GET  https://www.example.com/api/v1/products?search=sample_keyword

You can also allow users to specify keywords on multiple field names, as shown below.

GET  https://www.example.com/api/v1/products?category_id=7,is_active=Y

Also, sometimes, the end-users may not want to retrieve all the field names. In that case, allow them to specify the columns they want to retrieve using the field parameter as shown below.

GET  https://www.example.com/api/v1/products?fields=product_name,category_name,retail_price
GET  https://www.example.com/api/v1/products?fields=product_name
GET  https://www.example.com/api/v1/products?fields=category_name

8. Rate Limit your API Endpoints

To avoid abuse your server resources, rate-limit your API. For example, you can specify the total number of requests that a single user can make in an hour, a day, or a month.

Any account that exceeds that limit should fail with the following HTTP status code.

429 Too Many Requests

Rate-limiting your API improves its availability and eliminates any chances of Denial-of-Service(DOS) attacks. You can use different technologies to achieve this, including Redis, which works pretty well.

9. Version Your API

When developing your API, you'll make different changes even after releasing it to the public to improve it and offer more features. You may communicate these changes via email to the end-users, but your must version your API to avoid breaking the client's applications after each update.

After a new release, always support at least one previous version and deprecate it once you've transitioned all consumers. To support this feature, include a version identifier in your API URLs, as shown below.

  • Version 1:

    https://www.example.com/api/v1
    
  • Version 2:**

    https://www.example.com/api/v2
    
  • Version 3:**

    https://www.example.com/api/v3
    

10. Host Your API in the Cloud

You can either deploy your API on a virtual private server, a bare metal server or on a dedicated cloud server. These platforms ensure guaranteed RAM and CPU while giving you complete control to manage your server and install custom applications to make your APIs run faster.

Conclusion

This guide describes the best practices that you should follow when creating a modern REST API. Consider reading the following resources to code and implement most of the API functionalities discussed in this guide.

Want to contribute?

You could earn up to $600 by adding new articles