Best Practices for Designing a Modern REST API
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.
GET https://www.example.com/api/v1/products
Response:
200 OK
PUT https://www.example.com/api/v1/products/{product_id}
Response:
200 OK
POST https://www.example.com/api/v1/products
Response:
201 Created
DELETE https://www.example.com/api/v1/products/{product_id}
Response:
204 No Content
Also, use the following HTTP status codes when returning errors.
Resource not found on the server
404 Not Found
Authentication Failed. Incorrect username, password, or access token.
401 Unauthorized
Authorization Failed. Use this error when a user is properly authenticated but doesn't have the correct privileges to access a resource.
403 Forbidden
Bad Request. Errors from the client-side. For instance, data validation errors.
400 Bad Request
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.
Authentication Error
{ "errors" : { "http_status_code" : 401, "title" : "Invalid Login", "message" : "The username/password is incorrect." "custom_error_code" : 401.1 } }
Validation Error
{ "errors" : { "http_status_code" : 400, "title" : "Validation Error", "message" : "The 'product_name' field is required." "custom_error_code" : 400.1 } }
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 byproduct_name
in ascending order.GET https://www.example.com/api/v1/products?sort=retail_price,product_name
Sort data by
retail_price
and then byproduct_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 Cloud Compute server or Bare Metal.
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.