Rest API best practices
Table of Contents
- What is REST
- REST is not HTTP
- Why Is API Design So Important
- Design first approach
- Best practices on URIs
- Best practices on HTTP Verbs
- Filter, sort and paginate
- Semantics of the HTTP Methods
Designing and building a REST API takes time, takes effort and most importantly takes the right skill. REST APIs should be easy to understand, well documented and follow standards so that integration is straightforward. This can be acheived only if we follow the best practices when designing a RESTful API. Lets look into the REST API best practices to design and build great APIs which are robust and reliable.
APIs are a generic concept that can be explained in many ways for example,
- An API is a web service which delivers resources via web technologies such as HTTP.
- APIs are interfaces used for system to system communications.
- APIs are used for building distributed software systems to enable loose coupling.
APIs are not exposed to end-users directly and do not offer a user interface instead it is developed as an interface between different systems. API consumers are the developers who uses your API to build client applications. So this means that the API should be simple and efficient for them to use.
What is REST?
Representational state transfer (REST) is an architectural style designed to take advantage of existing protocols like HTTP/HTTPS. REST defines a set of constraints to be used for creating APIs. When an API is designed based on the principles which adheres to the Representational state transfer architectural constraint it is called a RESTful API. For detailed post on what is REST take a look at my previous post.
REST is not HTTP
Its important to understand that REST is NOT HTTP. Most often REST and HTTP are considered same, which is not the case. REST is the way HTTP should be used. For example, REST dictates the usage of HTTP verbs for specific usage. But HTTP can be misused by incorreclty using HTTP verbs instead of the purpose its designed for. For example, misusing POST to delete a resource,
HTTP POST /blog/posts/?delete_id=100
Why Is API Design So Important?
API design is hard. You need to understand your targeted API consumer to design a well designed long term focussed API. Think of API as a product. Do you like or use a poorly designed product? ofcourse not! A well-designed API should support platform independence and evolution of the system without breaking it.
An API should be Platform independent which means that any client should be able to call the API, regardless of how the API is implemented internally. This requires using standard protocols and data formats.
The evolution of API and the client applications should be independent of each other. The web API should be able to evolve and add functionality independently from client applications. As the API evolves, existing client applications should continue to function without modification. All API functionality should be discoverable so that client applications can utilize it if necessary. Breaking changes should be as minimal as possible.
Design first approach
Design first approach or a spec driven development saves Time and Hassle. I cannot emphasize enough on the importance of contract first approach which saves tons of development time by knowing almost all the variables before starting the development. This process is useful for both the API developer and the consumer. The following process is API lifecycle when doing spec driven development.
- Use Design Patterns/ Code Reuse.
- Mock and get User Feedback.
- Make Necessary Changes.
- Start Coding to the Spec and don't deviate.
Use the API to generate the models not the other way around. You may want to adopt OpenAPI, RAML for your web APIs. Some points to consider:
The OpenAPI Specification and RAML comes with a set of opinionated guidelines on how a REST API should be designed. That has advantages for interoperability, but requires more care when designing your API to conform to the specification.
OpenAPI/RAML promotes a contract-first approach, rather than an implementation-first approach. Contract-first means you design the API contract (the interface) first and then write code that implements the contract.
Tools like Swagger and RAML design center can generate client libraries or documentation from API contracts.
The best designed API has only one version. You SHOULD consider versioning your API when:
- You have a backwards-incompatible platform change.
- Your API is no longer extendable — which is exactly what we are trying to avoid here
- Your spec is out of date.
Again, I cannot emphasize enough the importance of going into your API design with a long-term focus, and that means versioning becomes a last-resort when your API can no longer scale independently or meets your user needs.
Never release an API without a version.
- Make the version mandatory.
- Specify the version with a “v” prefix. Move it all the way to the left in the URL so that it has the highest scope (e.g., /v1/resourceName).
- Use a simple ordinal number. Don’t use the dot notation like v1.2, because it implies a granularity of versioning that doesn’t work well with APIs.Use v1, v2 etc.
- If there is a breaking change, maintain atleast one version which is backward compatible.
- Announce developers at least one cycle to react before obsoleting a version.
- Version in the header or in the URL? both are valid. If it changes the logic you write to handle the response, put it in the URL so you can see it easily. If it doesn’t change the logic for each response (like OAuth information), put it in the header.
Best practices on URIs
Lets look into the best practices for URI designing
- Keep the URL simple and intuitive as it makes consuming your API easy.
|Resource||POST (Create)||GET (Read)||PUT (Update)||DELETE (Delete)|
|/customers||New customer||List customers||Bulk update||Bulk delete|
|/customers/5||List customer id 5||Update id 5||Delete id 5|
Forward slash separator (/) must be used to indicate a hierarchical relationship
The forward slash (/) character is used in the path portion of the URI to indicate a hierarchical relationship between resources. For example: http://api.programmerspub.com/blog/general/what-is-rest-api
Do NOT use a trailing forward slash (/) in URIs.
A forward slash (/) at the end of the URI adds no value and its an unnecessary addition. REST APIs should not expect a trailing slash and should not include them in the links that they provide to clients. Many web components and frameworks will treat the following two URIs equally:
A REST API must design and communicate clean URIs.
Use hyphens (-) to improve the readability of URIs.
To make your URIs easy and readable, use the hyphen (-) character in long path segments. Anywhere you would use a space or hyphen in English, you should use a hyphen in a URI. For example: http://api.programmerspub.com/blog/general/what-is-rest-api
Underscores (_) should not be used in URIs.
Lowercase letters should be preferred in URI paths Lowercase letters are preferred in URI paths since capital letters can sometimes cause problems. For example:
URI 1 and 2 are considered to be identical but the URI 3 is not the same as the resource naming is capitalized which may cause confusion.
File extensions should not be included in URIs
A REST API should not include file extensions in URIs to indicate the format of a message’s entity body. Instead, they should rely on the media type, as communicated through the Content-Type header.
Use plural nouns to name collections
A URI identifying a collection should be named with a plural noun. A collection’s name should be chosen to reflect what it uniformly contains.
For example, the URI for a collection of customers http://api.programmerspub.com/customers/12/accounts/123
CRUD function names should NOT be used in URIs URIs should not be used to indicate that a CRUD function is performed. URIs should be used to uniquely identify resources. HTTP request methods should be used to indicate the action performed on a resource. For example, this API interaction design is preferred:
The following anti-patterns exemplify what not to do:
GET /deleteUser?id=1234 GET /deleteUser/1234 DELETE /deleteUser/1234 POST /users/1234/delete
URI Query Design - : The query component of a URI should be used to filter, sort and paginate collections
Best practices on HTTP Verbs
This may seem so simple but lot of RESTAPIs fall short of using the appropriate HTTP verbs for the intended use.
The HTTP verbs are major portion of “uniform interface” constraint and provide us the actions on resources. The primary or most-commonly-used HTTP verbs are GET, PUT, POST, and DELETE. These are similar to create,read, update, and delete (or CRUD) but they are not the same. The HTTP verb should always determine the action done on a resource. Do not ever use the action verbs and do something else on the server side.
The HTTP GET method is used to retrieve (or read) a representation of a resource. GET returns a representation of data in XML or JSON and a HTTP response code of 200(OK).
Examples of bad design:
GET /customers/get123 GET /customers/123/getOrders GET /customers/?delete=123
Examples of good design:
GET /customers/123 GET /customers/123/orders
GET requests are used for read-only purpose. And for this reason GET is considered SAFE and IDEMPOTENT.
PUT is another HTTP method used to update an existing resource and it is sometimes used to create a new resource at a specified URI. To create a new resource, using PUT we need to know the exact URI where the data needs to be put which means that the resource ID is chosen by the client instead of by the server. If incase there is data in the specified URI, the entire data is overwritten which is update.
But use PUT onlt to UPDATE a resource and use POST to create a new resource as a best practice.
Examples of bad design:
PUT /customers/updateCustomer/123 PUT /customers/123/putOrders/123
Examples of good design:
PUT /customers/123 PUT /customers/123/orders/546
The POST verb is most-often utilized for creation of new resources. In particular, it is used to create subordinate resources which means it creates a new resource under a collection of resources.
Examples of bad design:
Examples of good design:
POST is neither safe or idempotent.
If you are confused whether to use PUT or PATCH or POST for a particular action in your API check my previous blog post here.
|GET||HTTP request method used to retrieve a representation of a resource’s state.|
|PUT||HTTP request method used to insert a new resource or update a resource.|
|POST||HTTP request method used to create a new resource within a collection.|
|DELETE||HTTP request method used to delete a resource.|
An API is only as good as its documentation. The documentation should be easy to find and easy to understand. Most developers will check out the documentation before attempting any integration effort. The documentation should have complete examples of request and response.
Remember, your documentation should tell a story.
Filter, sort and paginate
If the API deals with collections (which API doesn't) always provide the option to filter, paginate and sort.
Use query parameters defined in URL for filtering a resource from server. For example, to filter based on all published posts
In the example above, state is the filter parameter
ASC and DESC sorting parameters can be passed in URL such as:
Returns posts sorted with descending order of update date time.
Semantics of the HTTP Methods
We’ve already discussed the possible HTTP methods you can use to modify resources: GET, POST, PUT, PATCH, and DELETE.
Still, many developers tend to abuse GET and POST, or PUT and PATCH. Often, we see developers use a POST request to retrieve data and use a PUT request which replaces the resource while they only wanted to update a single field for that resource.
Make sure to use the correct HTTP method to avoid confusion when using your RESTful API.
Understand the Semantics of the HTTP Methods
Definition of Idempotence: A HTTP methods is idempotent when we can safely execute the request over and over again and all requests lead to the same state.
GET Idempotent Read-only. GET never changes the state of the resource on the server-side. It must not have side-effects. Hence, the response can be cached safely. Examples: GET /employees - Lists all employees GET /employees/1 - Shows the details of the employee 1 PUT Idempotent! Can be used for both creating and updating Commonly used for updating (full updates). Example: PUT /employees/1 - updates employee 1 (uncommon: creates employee 1) To use PUT for creating, the client needs to know the whole URL (including the ID) upfront. That’s uncommon as the server usually generates the ID. So PUT for creating is typically used when there is only one element and the URL is unambiguous. Example: PUT /employees/1/avatar - creates or updates the avatar of employee 1. There is only one avatar for each employee. Always include the whole payload in the request. It’s all or nothing. PUT is not meant to be used for partial updates (see PATCH). POST Not idempotent! Used for creating Example: POST /employees creates a new employee. The new URL is delivered back to the client in the Location Header (e.g. Location: /employees/12). Multiple POST requests on /employees lead to many new different employees (that’s why POST is not idempotent). PATCH Idempotent Used for partial updates. Example: PATCH /employees/1 - updates employee 1 with the fields contained in the payload. The other fields of employee 1 are not changed. DELETE Idempotent Used for deletion. Example: DELETE /employees/1
Developers need to spend some time while designing REST APIs, as the API can make a service very easy to use or extremely complex. Additionally, the maturity of the APIs can be easily documented by using the Richardson Maturity Model.
These guidelines are compiled on my experience of development. I would love to know your views on the pointers mentioned above. Please leave a comment, and let me know!