A Guide to Designing RESTful APIs
This article is originally published at https://www.learncsdesign.com
Uniform Resource Identifiers (URIs) are used by REST APIs to identify resources. Throughout this post, we’ll cover a set of API design rules molded from best practices consistent with the Web’s REST architecture style.
URI Format Rules
The following is the generic URI syntax described in RFC 3986:
URI = scheme “://” authority “/” path [“?” query] [“#” fragment]
Rule 1: Forward slash separator (/) must be used to indicate a hierarchical relationship
It must be used to indicate hierarchical relationships between resources.
For example:
https://api.medium.com/v1/users/learncsdesign/publications
Rule 2: Trailing forward-slash (/) should not be included in URIs
An ending forward-slash (/) in a URI path adds no semantic value and may cause confusion. REST APIs should not expect trailing slashes and should not include them in the links they provide to clients.
For example:
https://api.medium.com/v1/me
Rule 3: Hyphens (-) should be used to improve the readability of URIs
You can improve the readability of long path segments by including a hyphen (-) in URIs to make them easier to scan and interpret. In a URI, wherever space or hyphen would be used in English, you should use a hyphen.
For example:
https://api.razorpay.com/v1/payment-links
Rule 4: Underscores (_) should not be used in URIs
URIs are often underlined in-browser applications to indicate that they can be clicked. Underlining can either partially or completely obscure the underscore (_) character depending on the font of the application. In order to avoid this confusion, use hyphens (-) instead of underscores (_).
Rule 5: Lowercase letters should be preferred in URI paths
It is preferable to use lowercase letters in URI paths since capital letters can sometimes cause problems. RFC 3986 specifies that URIs are case-sensitive except for their scheme and host components.
For example:
- https://api.medium.com/v1/users/learncsdesign/publications
- https://API.MEDIUM.COM/v1/users/learncsdesign/publications
- https://api.medium.com/v1/Users/learncsdesign/publications
URI 1 and 2 are identical according to the URI format specification (RFC 3986), but URI 3 is not identical as U is capitalized in Users for URI 3, which may lead to confusion.
Rule 6: File extensions should not be included in URIs
The period (.) character is commonly used to separate the file name and extension portions of a URI on the Web. To indicate the format of a message’s entity-body, a REST API should not include artificial file extensions in URIs. To determine how to process the body’s content, they should rely on the media type, as provided by the Content-Type header.
HTTP clients use the Accept header to specify which response media types are acceptable. In turn, the server will send back a response that includes the Content-Type header that informs the client what type of media was returned.
Now, the Content-Type header could be on request and responses as well. But why? Consider POST or PUT requests. As part of those request types, the client is actually sending a bunch of data to the server, and the Content-Type header tells the server what the data is and how to parse it.
For example:
https://api.medium.com/v1/users/learncsdesign/publication.json, the Accept request header should be used instead of the file extension to indicate format preference.
URI Authority Design
Naming conventions that should be followed for the authority part of a REST API.
URI = scheme “://” authority “/” path [“?” query] [“#” fragment]
Rule 1: Consistent subdomain names should be used for APIs
An API’s top-level domain and first subdomain should identify the service owner. API should be added as a subdomain to the full domain name.
For example:
https://api.medium.com
Rule 2: Consistent subdomain should be used for the client developer portal
Many REST APIs come with a developer portal that provides documentation, forums, and self-service provisioning of secure API keys to help onboard new clients. By convention, a developer portal should have a subdomain labeled developer.
For example:
https://developer.android.com
Resource Modeling
URIs convey a REST API’s resource model, with each forward slash-separated path segment corresponding to a unique resource within the model.
For example,
this URI design: https://api.medium.com/v1/users/learncsdesign/publications indicates that each of these URIs should also identify an addressable resource:
https://api.medium.com/v1/users/learncsdesign
https://api.medium.com/v1/users
Rule 1: Resource Archetypes
As a starting point for modeling an API’s resources, we can look at some basic resource archetypes.
The following four archetypes describe a REST API’s resources:
- Document — The concept of a document resource is similar to that of an object instance or database record. Document resources can have child resources that represent subordinate concepts.
For example — https://api.book.com/v1/authors/sam-newman/books/building-microservices
Due to its ability to group many different resource types into one, a document is a logical candidate for a REST API’s root resource, called the docroot.
For example — https://api.book.com - Collection — Collection resources are directories of resources managed by the server. Clients can suggest new resources to be added to a collection. It is up to the collection to decide whether to create a new resource or not. Each collection resource decides what it will contain as well as the URIs of the resources it will contain.
For example — https://api.book.com/v1/authors/sam-newman/books - Store — A store is a repository of client-managed resources. A store resource allows an API client to add resources, retrieve them, and decide when to delete them. Stores don’t create new resources on their own; therefore, they don’t create new URIs. Rather, each stored resource has a unique URI created by the client when it was initially added to the store. For Example — A user with id 1234 adds a book document resource named building-microservices in his/her store of favorites.
PUT https://api.book.com/v1/users/1234/favorite-books/building-microservices - Controller — A controller resource represents a procedural concept. Controller resources are like functions, with parameters, return values, inputs, and outputs. The name of the controller is typically the last segment in a URI path, with no child resources following it in the hierarchy.
For example — send SMS to user with id 123
POST https://api.book.com/v1/users/123/sms-send
URI Path Design
Collection contains store and store stores document {collection}/{store}/{document}
URI path segments separated by forward-slashes (/) each represent a design opportunity. When path segments are assigned meaningful values, the hierarchy of a REST API’s resource model can be clearly understood.
Guidelines for constructing meaningful URI paths:
Rule 1: A singular noun for document names
URIs representing document resources should be named with a singular noun.
For example — The URI for a book document would have the singular form
https://api.book.com/v1/authors/sam-newman/books/{book-name}
Rule 2: A plural noun for collection names
A URI identifying a collection should use a plural noun. The name of a collection should reflect what it uniformly consists of.
For example — The URI for a collection of book documents uses the plural noun form of its contained resources
https://api.book.com/v1/authors/sam-newman/books
Rule 3: A plural noun for store names
The name of a URI identifying a store of resources should include a plural noun.
For example — The URI for a store of favorite books for the user with id 1234
https://api.book.com/v1/users/1234/favorite-books
Rule 4: A verb for controller names
URIs identifying controller resources should be accompanied by names indicating their actions.
For example — https://api.book.com/v1/authors/sam-newman/publish
Rule 5: Variable path segments may be substituted with identity-based values
URI path segments can have fixed names, which are chosen by the designer of the REST API. Other URI path segments are variable, meaning that they are automatically filled in with some identifier, which helps give the URI its uniqueness. Designers can clearly name both static and variable segments using the URI Template syntax. URI templates include variables that must be substituted before resolution.
For example — https://api.medium.com/v1/users/{userId}/publications
Rule 6: CRUD function names should not be used in URIs
A URI should not be used to indicate a CRUD operation has been performed. URIs should be used to identify resources, and their names should follow the rules outlined above. You should use HTTP request methods to indicate which CRUD function is being performed.
For example:
GET https://api.medium.com/v1/me
POST https://api.medium.com/v1/users/{authorId}/posts
URI Query Design
Remember from RFC 3986 that a URI’s optional query appears after the path but before the optional fragment.
URI = scheme “://” authority “/” path [“?” query] [“#” fragment]
The query contributes to the unique identification of a resource as part of its URI.
For example:
https://api.book.com/v1/authors/sam-newman/publications
https://api.book.com/v1/authors/sam-newman/publications?rank=newest
The URI with query parameter modifies the order of publications so that the newest publications are displayed at the top.
The query component of a URI contains a set of parameters that can be interpreted as variations or derivatives of the resource, which is hierarchically identified by the path component. While these two resources are not the same, they are closely related.
Rule 1: The query may be used to filter collections or stores
A URI’s query component makes it easy to supply search criteria to a collection or store.
For example
GET https://api.book.com/v1/users
GET https://api.book.com/v1/users?role=admin
The first URI response contains a listing of all users in the collections, whereas the second URI response contains a filtered list of all users with the role of admin in the collection.
Rule 2: The query may be used to filter collections or stores
PageSize and pageStartIndex parameters can be used by a REST API client to paginate collection results.
For example
GET https://api.book.com/v1/users?pageSize=10&pageStartIndex=100
Request Methods
HTTP methods have specific, well-defined semantics within the context of a REST API’s resource model.
- GET retrieves a representation of a resource’s state.
- HEAD retrieves the metadata associated with the state of resources.
- PUT should be used to add a new resource to a store or to update an existing one.
- DELETE removes a resource from its parent.
- POST method should be used to create a new resource within a collection and run controllers.
Rule 1: GET & POST must not be used to tunnel other request methods
Tunneling is any action that masks or misrepresents a message’s intent and undermines the protocol’s transparency. In an effort to accommodate clients with limited HTTP vocabulary, a REST API must not compromise its design by misusing HTTP request methods.
Rule 2: GET must be used to retrieve a representation of a resource
REST API clients retrieve the state of a resource using the GET method in a request message. The GET request message from a client may contain headers, but not the body.
Rule 3: PUT must be used to both insert and update a stored resource
A resource must be added to a store using the PUT method with a URI specified by the client. PUT must also be used to update or replace an existing resource. The PUT request message must include a representation of the resource the client wants to store.
Rule 4: POST must be used to create a new resource in a collection
In order to create a new resource within a collection, clients use POST. POST requests contain the state representation of new resources to be added to the server-owned collection in the body of the request.
Rule 5: POST must be used to execute controllers
The function-oriented controller resources are invoked by the client using the POST method. As inputs to the function of a controller resource, a POST request message may include both headers and bodies.
A POST request is considered unsafe and non-idempotent by HTTP, which means its outcome is unpredictable and not guaranteed to be repeatable without potentially undesirable side effects.
For example:
POST https://api.book.com/v1/users/123/sms-send
Rule 6: DELETE must be used to remove a resource from its parent
DELETE is used by a client to request the removal of a resource from its parent, which is often a collection or store. After a DELETE request has been processed for a resource, the resource can no longer be found by clients. Therefore, any future requests to retrieve the resource’s state representation using GET or HEAD should result in a 404 (“Not Found”) status from the API. For example:
DELETE https://api.razorpay.com/v1/invoices/inv-DAuFuwWYU3R9tg
Rule 7: OPTIONS should be used to retrieve metadata that describes a resource’s available interactions
The OPTIONS request method may be used by clients to retrieve resource metadata that includes an Allow header value.
For example: Allow: GET, PUT, DELETE
Response Status Codes
HTTP defines standard status codes for conveying the results of a client’s request. They are divided into five categories.
In this section, we will describe how and when to use the subset of codes that apply to REST API design.
Rule 1: 200 (“OK”) should be used to indicate success
It indicates that the REST API successfully completed whatever action the client requested and that no more specific code in the 2xx series is required. In contrast to the 204 status code, a 200 response must contain a response body.
Rule 2: 200 (“OK”) must not be used to communicate errors in the response body
Use the HTTP response status code appropriately, and the 200 response code should not be used to communicate errors in the response body.
Rule 3: 201 (“CREATED”) must be used to indicate successful resource creation
If a collection or store adds a new resource at the client’s request, a REST API will respond with the 201 status code. As a result of some controller action, a new resource may also be created, in which case 201 is also the right response.
Rule 4: 202 (“ACCEPTED”) must be used to indicate the successful start of an asynchronous action
The 202 response indicates that the client’s request will be handled asynchronously. According to this response status code, the client’s request appears valid, but may still experience problems once it’s processed. It is typically used for actions that take a long time to process. Only controller resources should send 202 responses.
Rule 5: 204 (“NO CONTENT”) should be used when the response body is intentionally empty
A 204 response code is typically returned in response to a PUT, POST, or DELETE request when the REST API declines to return any status message or representation within the body of the response message. 204 may also be sent as part of a GET request to indicate that the requested resource exists but does not have a state representation.
Rule 6: 301 (“MOVED PERMANENTLY”) should be used to relocate resources
The 301 status code indicates that the REST API’s resource model has been redesigned significantly and that a new permanent URI has been assigned to the client’s requested resource. In the response’s Location header, the REST API should specify the new URI.
Rule 7: 302 (“FOUND”) should not be used
The HTTP 1.1 standard introduced status codes 303 (“See Other”) and 307 (“Temporary Redirect”), which should be used instead of 302.
Rule 8: 303 (“SEE OTHER”) should be used to refer the client to a different URI
A 303 response indicates that a controller resource has finished its task, but instead of sending a potentially unwanted response body, it sends the client the URI of a response resource. REST APIs can send a reference to a resource using the 303 status code without forcing the client to download its state. Alternatively, the client may send a GET request to the value of the Location header.
Rule 9: 304 (“NOT MODIFIED”) should be used to preserve bandwidth
It is similar to 204 (“No Content”) in that the response body must be empty. A key difference is that 204 is used when there is nothing to send in the body, while 304 is used when there is state information associated with a resource, but the client already has a recent version of the representation.
Rule 10: 307 (“TEMPORARY REDIRECT”) should be used to tell clients to resubmit the request to another URI
The 307 response indicates that the REST API will not process the client’s request. The client should resubmit the request to the URI specified in the response message’s Location header.
Rule 11: 400 (“BAD REQUEST”) may be used to indicate nonspecific failure
When no other 4xx error code is appropriate, 400 serves as the generic client-side error status.
Rule 12: 401 (“UNAUTHORIZED”) must be used when there is a problem with the client’s credentials
A 401 error response indicates that the client accessed a protected resource without authorization. The client may have provided incorrect credentials or none at all.
Rule 13: 403 (“FORBIDDEN”) should be used to forbid access regardless of the authorization state
In a 403 response, the client’s request is formed correctly, but the REST API refuses to honor it. 403 responses enforce application-level permissions.
Rule 14: 404 (“NOT FOUND”) must be used when a client’s URI cannot be mapped to a resource
The 404 error status code indicates that the REST API was unable to map the client’s URI to a resource.
Rule 15: 405 (“METHOD NOT ALLOWED”) must be used when the HTTP method is not supported
The API responds with a 405 error to indicate that the client has attempted to use an HTTP method that is not supported by the resource.
Rule 16: 406 (“NOT ACCEPTABLE”) must be used when the requested media type cannot be served
According to the Accept request header, the 406 error message signals that the API cannot produce any of the client’s preferred media types.
Rule 17: 409 (“CONFLICT”) should be used to indicate a violation of resource state
An error 409 indicates that the client has attempted to change the REST API’s resources into an impossible or inconsistent state.
Rule 18: 412 (“PRECONDITION FAILED”) should be used to support conditional operations
According to the 412 error response, the client specified one or more preconditions in its request headers, effectively telling the REST API to only execute the request if certain conditions are met. The API responds with this status code instead of executing the request if those conditions were not met.
Rule 19: 415 (“UNSUPPORTED MEDIA TYPE”) must be used when the media type of a request’s payload cannot be processed
A 415 error response indicates that the API is unable to process the client’s supplied media type, as indicated by the Content-Type request header.
Rule 20: 500 (“INTERNAL SERVER ERROR”) should be used to indicate API malfunction
The generic REST API error code is 500. The majority of web frameworks use this response status code to respond to any request handler that raises an exception.
HTTP Headers
HTTP’s request and response messages may contain entity-headers that convey different types of metadata. REST API designers can use these rules to work with HTTP’s standard headers.
Rule 1: Content-Type must be used
Content-Type describes the type of data that can be found in the body of a request or response message. The value of this header is a special type of string called a media type. Clients and servers use this header’s value to determine how to process the sequence of bytes in a message body.
Rule 2: Content-Length must be used
The Content-Length header indicates the size of the entity-body in bytes. This header is important for two reasons. To start with, a client can determine if it has read the correct number of bytes from the connection. In addition, a client can make a HEAD request to determine the size of the entity-body without having to download it.
Rule 3: Last-Modified should be used in responses
Only response messages contain the Last-Modified header. This response header contains a timestamp that indicates when the representational state of the resource was last altered. This header may be used by clients and cache intermediaries to determine the freshness of their local copies of a resource’s state representation. When responding to GET requests, this header should always be included.
Rule 4: ETag should be used in responses
This value is an opaque string that identifies a specific “version” of the representational state contained in the response’s entity. The HTTP message’s payload is composed of a message’s headers and body. Entity tags may be any string value as long as they change with the resource representation. This header is always sent in response to GET requests. Clients can store the value of the ETag header for use in future GET requests, as the value of the conditional If-None-Match request header. The REST API can save time and bandwidth by not sending the representation again if it determines that the entity tag has not changed.
Rule 5: Stores must support conditional PUT requests
Store resources can be updated as well as inserted using the PUT method, so a REST API has a hard time determining the true intent of a client’s PUT request. Using HTTP headers, APIs are able to resolve any ambiguity that might arise. To express their intent, a REST API must rely on the client to include the If-Unmodified-Since and/or If-Match request headers. When the If-Unmodified-Since request header is specified, the API is requested to proceed with the operation only if, and only if, the resource’s state representation has not changed since the time specified by the header. If-Match contains an entity tag, which the client remembers from an earlier response’s ETag header. With the If-Match header, the request is conditional based on whether the header’s entity tag value matches the representational state’s current entity tag value, as stored or computed by the REST API.
Rule 6: Location must be used to specify the URI of a newly created resource
The Location response header contains the URL of a resource the client may find useful. When a resource is created within a collection or store, a REST API must include the Location header to identify the URI of the newly created resource.
Rule 7: Cache-Control, Expires, and Date response headers should be used to encourage caching
Among the most useful features of HTTP is caching. Include a Cache-Control header with a max-age value (in seconds) equal to the freshness duration when serving a representation. In order to support HTTP 1.0 caches, a REST API should include an Expires header with the expiration date-time. The value is the time when the API generated the representation, plus the lifetime of the freshness. REST APIs should also include a Date header with the time at which the API returned a response. By including this header, clients can compute the freshness lifetime as the difference between the values of the Expires and Date headers.
Rule 8: Cache-Control, Expires, and Pragma response headers may be used to discourage caching
If a REST API response should not be cached, add Cache-Control headers with the values no-cache and no-store. For compatibility with legacy HTTP 1.0 caches, you should also add Pragma: no-cache and Expires: 0 header values.
Rule 9: Caching should be encouraged
The no-cache directive prevents any cache from serving cached responses. APIs shouldn’t do this unless absolutely necessary. Instead of adding a no-cache directive, a small max-age value helps clients retrieve cached copies for at least a short time without affecting freshness significantly.
Rule 10: Expiration caching header should be used with 200 (“OK”) responses
In response to successful GET and HEAD requests, set expiration caching headers. Despite the fact that POST is cacheable, most caches do not cache this method. You don’t have to set expiration headers for other methods.
Rule 11: Expiration caching header may optionally be used with 3xx and 4xx responses
In addition to successful responses with the 200 (“OK”) response code, consider caching headers for 3xx and 4xx responses. It reduces the amount of redirecting and error-triggering loads on a REST API and is known as negative caching.
Rule 12: Custom HTTP headers must not be used to change the behavior of HTTP methods
Custom headers can be used for informational purposes only. Ensure that clients and servers do not fail when they cannot find expected custom headers. When the information you are conveying through a custom HTTP header is essential to the correct interpretation of the request or response, put that information in the body of the request or response or in the URI of the request. Do not use custom headers when conveying such information.
If you like the post, don’t forget to clap. If you’d like to connect, you can find me on LinkedIn.
References
REST API Design Rulebook — Mark H. Massé
RESTful Web APIs — Leonard Richardson & Mike Amundsen