RESTful API design has become prevailing for contemporary web services. This article is not an introduction to RESTful API design, but a collection of important principles I learned in the past few years. Though I have good reasons for each point I made here, it is subjective. Please use your own discretion.
RESTful (Photo credit: Cait Stewart @flickr CC BY-SA 2.0)
1. HTTP Verb
The first step of RESTful API design is to comprehend HTTP verbs. Some API design choice may be subjective, but HTTP verb misuse would be embarrassing.
- GET: read resource (safe & idempotent)
- PUT: replace resource (idempotent)
- DELETE: delete resource (idempotent)
- POST: create new resource; also a catch-all verb
- PATCH: update partial resource
- HEAD: similar to GET but return HTTP header only (safe & idempotent)
- Other less frequently used verbs: refer to Wikipedia Hypertext Transfer Protocol: Request methods
Safe means the request will not alter the resource state of the server, and the response can be cached. Idempotent means the request will result in the same resource end state no matter the request is processed once or multiple times, therefore the client may safely retry. (However, the client does not necessarily receive the same response. For example, the client could receive a 404 upon the second DELETE request.)
PUT, POST and PATCH can be confusing. Here are the differences:
- PUT is usually for the replacement of a single resource / resource collection.
- POST is usually for new resource creation. It can also be used as a catch-all verb, such as a utility API request. (Utility API is different from regular resource operation. Example: validating a marketing promotion code.)
- PATCH is usually for partial resource update. (A few years ago, people might use POST in stead of PATCH due to limited PATCH support. This should be unnecessary now.)
2. URI Noun
In contrast to HTTP verbs, URIs are nouns. The URI consists of a prefix and an API endpoint. The prefix is optional, such as /api
or /api/v1
.
A few points about the API endpoint design:
- Use plural for regular resource, such as
/books
or/books/123
.- Some people prefer singular because
/book/123
seems better than/books/123
, but considering the file system folder names such as/Users
or/Documents
, using plural is totally fine. Plural keeps API endpoints consistent, so I prefer plural.
- Some people prefer singular because
- Use singular for singleton resource (i.e. only one instance is possible for the client). For example, the
user
inGET /user/subscriptions
of GitHub watching API is singular because it refers to the current authenticated user. - The resource hierarchy can be reflected on the API endpoint design when appropriate, e.g.
/books/123/chapters/2
. - Utility API is different from resource API; any reasonable endpoint choice would be fine. For example:
/search?q={keywords}
. - I recommend using lowercases for URI components, and separating words by either dashes
-
or underlines_
. (I prefer dashes.)
3. HTTP Status Code
The API response includes an HTTP status code. You should know the status codes well and choose the most proper one. Here are some commonly used codes:
- 2xx: Success
- 200 OK: the general code
- 201 Created: resource created successfully
- 202 Accepted: request accepted (and still being processed)
- 204 No Content: request completed successfully but nothing to return
- 3xx: Redirection
- 301 Moved Permanently: resource moved elsewhere
- 303 See Other: response can be found elsewhere (e.g. after the client has sent a POST request)
- 304 Not Modified: resource not modified (usually when the client sent a request with HTTP header If-Modified-Since or If-None-Match)
- 4xx: Client Error (Client should not retry)
- 400 Bad Request: the general code
- 401 Unauthorized: client not authenticated*
- 403 Forbidden: client not allowed for the request*
- 404 Not Found: resource not found
- 405 Method Not Allowed: the HTTP verb not supported for this API endpoint
- 406 Not Acceptable: the requested Content-Type not supported* (Accept header)
- 415 Unsupported Media Type: the Content-Type of the request body not supported* (Content-Type header)
- 5xx: Server Error (Client may reasonably retry)
- 500 Internal Server Error: ouch, engineers need to debug
- 501 Not Implemented: request not implemented at the moment (i.e. may be implemented someday)
- 502 Bad Gateway: bad response from the upstream server (usually only a gateway or proxy server would return 502)
- 503 Service Unavailable: service temporarily unavailable (i.e. may be available shortly)
- 504 Gateway Timeout: upstream server timeout (usually only a gateway or proxy server would return 504)
* Some status codes can be confusing. Here is the disambiguation:
- 401 vs. 403: 401 means the client has not been authenticated. (The use of “Unauthorized” by the HTTP spec is somewhat misleading.) 403 means the current client (usually authenticated) or all clients are not allowed for the request.
- 406 vs. 415: 406 means the requested Content-Type for the response (indicated by the request Accept header) is not supported by the server. 415 means the Content-Type of the request body (indicated by the request Content-Type header) is not supported by the server.
Note that these codes are intended for the API service layer, not for the business logic layer. For example, when the search result of /search?q=xyz
is empty, the server should return a 200 rather than 404, because the “/search
resource” exists and the request is fulfilled successfully.
4. HTTP Header
The HTTP request sent by the client may include some HTTP headers. For example:
- Accept: contains the Content-Types of the response that the client accepts (part of content negotiation)
- Authorization: contains authentication credentials
As to the HTTP headers of the API responses, there is no special requirement. Just follow the common practice. (For example: Content-Type, Content-Length, ETag, Cache-Control, etc.)
5. HTTP Body: JSON or XML
Since JSON is easier to process and widely supported, more and more people use JSON as the HTTP body format for their web services. However, supporting which one (or both) still depends on your project needs.
6. Miscellaneous Notes
- Like HTTP, the API should be stateless, which means a job unit should not consist of two or more APIs. (It brings up an interesting question: How does REST API support transactions? We may talk about that in another post.)
- The resources represented by the API are designed for the client/application need, and do not necessarily reflect the server-side storage format (e.g. database schema).
- Although HATEOAS (Hypermedia as the engine of application state) is part of the original REST architectural constraints, I don’t think it’s a must-have in practice.
- There is no special requirement for query parameters. Just keep the style consistent.
Conclusion
RESTful API design is a time-tested design style. It makes the API more consistent, maintainable and extensible, and integrates with the HTTP protocol well. Hope this article gives you a good glance at the RESTful API design.
May your API REST.
Chinese version of the article: 簡明RESTful API設計要點