For many of us, designing a REST API can sometimes feel more like an art than a science. Some best practices for REST API design are implicit in the HTTP standard, while other pseudo-standard approaches have emerged over the past few years. Yet today, we must continue to seek out answers to a slew of questions, such as:
- When should URI path segments be named with plural nouns?
- Which request method should be used to update resource state?
- How do I map non-CRUD operations to my URIs?
- What is the appropriate HTTP response status code for a given scenario?
- How can I manage the versions of a resource’s state representations?
- How should I structure a hyperlink in JSON?
The forward slash (/) character is used in the path portion of the URI to indicate a hierarchical relationship between resources. For example:
http://api.canvas.restapi.org/shapes/polygons/quadrilaterals/squares
As the last character within a URI’s path, a forward slash (/) adds no semantic value and may cause confusion. REST APIs should not expect a trailing slash and should not include them in the links that they provide to clients.
To make your URIs easy for people to scan and interpret, use the hyphen (-) character to improve the readability of names 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.example.restapi.org/blogs/mark-masse/entries/this-is-my-first-post
On the Web, the period (.) character is commonly used to separate the file name and extension portions of a URI. A REST API should not include artificial 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, to determine how to process the body’s content.
The top-level domain and first subdomain names (e.g., soccer.restapi.org) of an API should identify its service owner. The full domain name of an API should add a sub- domain named api. For example:
http://api.soccer.restapi.org
Many REST APIs have an associated website, known as a developer portal, to help on- board new clients with documentation, forums, and self-service provisioning of secure API access keys. If an API provides a developer portal, by convention it should have a subdomain labeled developer. For example:
http://developer.soccer.restapi.org
The URI path conveys a REST API’s resource model, with each forward slash separated path segment corresponding to a unique resource within the model’s hierarchy. For example, this URI design:
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet
indicates that each of these URIs should also identify an addressable resource:
http://api.soccer.restapi.org/leagues/seattle/teams
http://api.soccer.restapi.org/leagues/seattle
http://api.soccer.restapi.org/leagues
http://api.soccer.restapi.org
Resource modeling is an exercise that establishes your API’s key concepts. This process is similar to the data modeling for a relational database schema or the classical modeling of an object-oriented system.
Before diving directly into the design of URI paths, it may be helpful to first think about the REST API’s resource model.
A document resource is a singular concept that is akin to an object instance or database record. A document’s state representation typically includes both fields with values and links to other related resources. With its fundamental field and link-based structure, the document type is the conceptual base archetype of the other resource archetypes. In other words, the three other resource archetypes can be viewed as specializations of the document archetype.
Each URI below identifies a document resource:
http://api.soccer.restapi.org/leagues/seattle
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/mike
A collection resource is a server-managed directory of resources. Clients may propose new resources to be added to a collection. However, it is up to the collection to choose to create a new resource, or not. A collection resource chooses what it wants to contain and also decides the URIs of each contained resource.
Each URI below identifies a collection resource:
http://api.soccer.restapi.org/leagues
http://api.soccer.restapi.org/leagues/seattle/teams
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players
A store is a client-managed resource repository. A store resource lets an API client put resources in, get them back out, and decide when to delete them. On their own, stores do not create new resources; therefore a store never generates new URIs. Instead, each stored resource has a URI that was chosen by a client when it was initially put into the store.
The example interaction below shows a user (with ID 1234) of a client program using a fictional Soccer REST API to insert a document resource named alonso in his or her store of favorites:
PUT /users/1234/favorites/alonso
A controller resource models a procedural concept. Controller resources are like exe- cutable functions, with parameters and return values; inputs and outputs.
Like a traditional web application’s use of HTML forms, a REST API relies on controller resources to perform application-specific actions that cannot be logically mapped to one of the standard methods (create, retrieve, update, and delete, also known as CRUD).
Controller names typically appear as the last segment in a URI path, with no child resources to follow them in the hierarchy. The example below shows a controller re- source that allows a client to resend an alert to a user:
POST /alerts/245743/resend
A URI representing a document resource should be named with a singular noun or noun phrase path segment.
For example, the URI for a single player document would have the singular form:
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/claudio
A URI identifying a collection should be named with a plural noun, or noun phrase, path segment. A collection’s name should be chosen to reflect what it uniformly con- tains.
For example, the URI for a collection of player documents uses the plural noun form of its contained resources:
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players
A URI identifying a store of resources should be named with a plural noun, or noun phrase, as its path segment. The URI for a store of music playlists may use the plural noun form as follows:
http://api.music.restapi.org/artists/mikemassedotcom/playlists
Like a computer program’s function, a URI identifying a controller resource should be named to indicate its action. For example:
http://api.college.restapi.org/students/morgan/register
http://api.example.restapi.org/lists/4324/dedupe
http://api.ognom.restapi.org/dbs/reindex
http://api.build.restapi.org/qa/nightly/runTestSuite
Some URI path segments are static; meaning they have fixed names that may be chosen by the REST API’s designer. Other URI path segments are variable, which means that they are automatically filled in with some identifier that may help provide the URI with its uniqueness. The URI Template syntax‡ allows designers to clearly name both the static and variable segments. A URI template includes variables that must be substituted before resolution. The URI template example below has three variables (leagueId, teamId, and playerId):
http://api.soccer.restapi.org/leagues/{leagueId}/teams/{teamId}/players/{playerId}
The substitution of a URI template’s variables may be done by a REST API or its clients. Each substitution may use a numeric or alphanumeric identifier, as shown in the ex- amples below:
http://api.soccer.restapi.org/leagues/seattle/teams/trebuchet/players/21
http://api.soccer.restapi.org/games/3fd65a60-cb8b-11e0-9572-0800200c9a66
URIs should not be used to indicate that a CRUD§ function is performed. URIs should be used to uniquely identify resources, and they should be named as described in the rules above. As discussed in “Request Methods” on page 23, HTTP request methods should be used to indicate which CRUD function is performed.
For example, this API interaction design is preferred:
DELETE /users/1234
The following anti-patterns exemplify what not to do:
GET /deleteUser?id=1234
GET /deleteUser/1234
DELETE /deleteUser/1234
POST /users/1234/delete
This section provides rules relating to the design of URI queries. Recall from RFC 3986 that a URI’s optional query comes after the path and before the optional fragment:
URI = scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]
As a component of a URI, the query contributes to the unique identification of a re- source. Consider the following example:
http://api.college.restapi.org/students/morgan/send-sms
(1)
http://api.college.restapi.org/students/morgan/send-sms?text=hello
(2)
(1) The URI of a controller resource that sends an sms message.
(2) The URI of a controller resource that sends an sms message with a text value of hello.
The query component of a URI contains a set of parameters to be interpreted as a variation or derivative of the resource that is hierarchically identified by the path com- ponent. So, while these two resources are not the same, they are very closely related.
The query component can provide clients with additional interaction capabilities such as ad hoc searching and filtering. Therefore, unlike the other elements of a URI, the query part may be transparent to a REST API’s client.
The entirety of a resource’s URI should be treated opaquely by basic network-based intermediaries such as HTTP caches. Caches must not vary their behavior based on the presence or absence of a query in a given URI. Specifically, response messages must not be excluded from caches based solely upon the presence of a query in the requested URI. As discussed later in Chapter 4, HTTP headers, not queries, must be used to direct a cache intermediary’s behavior.
A URI’s query component is a natural fit for supplying search criteria to a collection or store. Let’s take a look at an example:
GET /users
(1)
GET /users?role=admin
(2)
(1) The response message’s state representation contains a listing of all the users in the collection.
(2) The response message’s state representation contains a filtered list of all the users in the collection with a “role” value of admin.
A REST API client should use the query component to paginate collection and store results with the pageSize and pageStartIndex parameters. The pageSize parameter specifies the maximum number of contained elements to return in the response. The pageStartIndex parameter specifies the zero-based index of the first element to return in the response. For example:
GET /users?pageSize=25&pageStartIndex=50
When the complexity of a client’s pagination (or filtering) requirements exceeds the simple formatting capabilities of the query part, consider designing a special controller resource that partners with a collection or store. For example, the following controller may accept more complex inputs via a request’s entity body instead of the URI’s query part:
POST /users/search
This design allows for custom range types and special sort orders to be easily specified in the client request message body. However, as detailed in Chapter 4, care must be taken to ensure that the controller’s cacheable results are marked accordingly.