Guide to REST Services

Introduction to REST

REST (or "Representationl State Transfer") is an architectural style for exposing services over standard HTTP/S protocols. It was first proposed in 2000 by Roy Fielding in his doctoral thesis titled Architectural Styles and the Design of Network-based Software Architectures.

REST Constraints

REST specifies the constraints to which a service must adhere in order to be considered "RESTful". RESTful services are simple, consistent, and work in harmony with existing Web infrastructure. In fact, the World Wide Web itself is the largest implementation of a RESTful architecture. The fundamental REST constraints are as follows:

ConstraintDescription
Client-serverA clear "separation of concerns" is provided by dividing responsibilities between clients and servers. This means that clients are not concerned with activities like storing resource data and servers are not responsible for maintaining user state or creating a user interface.
StatelessIn order to decrease coupling and improve scalability, servers should be "stateless". This means that session state is maintained on the client and no user-specific information is stored on the server. To that end, every request must contain all of the information required to satisfy the request.
CacheableTo improve scalability and performance, REST clients and network intermediaries are allowed to cache responses. To facilitate caching, REST responses should indicate whether or not they are cacheable and for how long.
Layered systemLike the Web at large, REST services support routing requests through network intermediaries during transit to the destination server. These intermediaries can add value such as load balancing or local caching in order to further improve scalability and performance.
Uniform interfaceA uniform interface specifies the standard by which REST clients and servers communicate. This allows each side to evolve independently and simplifies re-use since all REST services expose a consistent interface.
Code on DemandCode on demand is an optional constraint that dictates that it is permissible for the server to return business logic that will be executed on the client. Client-side JavaScript, Java applets, and Flash applications are examples of code on demand logic.

..........................

 

REST Principles

REST services are "resource oriented". This means that they perform work by manipulating uniquely identifiable resources in a prescribed fashion. What is a resource? A resource is a server-side entity that represents a concrete idea such as a customer or an order (the "nouns" in the system). Resources are typically mapped to a file or database table wherein information about the resource is persisted. A REST API exposes the ability to perform a small set of operations on these resources. The operations by which resources can be manipulated are prescribed by the HTTP methods POST, GET, PUT, and DELETE to support create, read, update, and delete actions (the "verbs" in the system) against specific resources. These actions are often referred to as "CRUD" operations (for create, read, update, delete).

The term "Representational State Transfer" indicates that resources are created, retrieved, and modified using representations of those resources. For example, a customer record in a database could be created by the client passing a "representation" of the customer in JSON or XML format. The JSON or XML message isn't the resource itself but a representation containing information sufficient to create the server-side resource. Roy Fielding described REST in this manner:

"Representational State Transfer is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through an application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use."

REST services are based on these fundamental principles:

  1. All resources are uniquely addressable via a URI (Uniform Resource Identifier).
  2. Resources are manipulated using standard HTTP methods (e.g., POST, GET, PUT, DELETE) and representations (e.g., JSON or XML).
  3. Messages are "self-descriptive" such that they specify the format of the payload as well as other metadata (e.g., cache instructions, verision information, etc.).
  4. Other than a few fixed entry points, resource locations (i.e., URIs) are provided dynamically in the body of returned representations. This concept facilitates what is known as a "connected" service since the client can "discover" all permissible actions by simply exercising the API (excluding the initial entry point).

These fundamental principles make REST services consistent in their interface and flexible in their implementation.

Designing a REST API

Typically, the first step in building a REST service is to design the API by which clients will access server-side resources. Essentially, there are four important elements in each REST service call that must be defined by the API author. These elements are as follows:

  • HTTP method
  • Resource URI
  • Request headers and body
  • Response status code and body

Let's take a closer look at each of these elements.

HTTP Method

The HTTP method indicates the type of action that should be taken on the resource specified in the URI. RESTful services typically use four methods that roughly equate the standard CRUD (create, read, update, delete) operations as follows:

HTTP MethodCRUD OperationSafeIdempotent
GETRead a resourceyesyes
POSTCreate a new resource whose URI is assigned by the servernono
PUTUpdate an existing resource or create a new resource whose URI is defined by the clientnoyes
DELETEDelete a resourcenoyes

The meaning behind the safe and idempotent columns are described below.

The GET method is used for calls that have no side effects on the server (any calls that have no side effects should use GET). An example of this would be when retrieving information about a book. No transaction is taking place so the book can be retrieved any number of times without altering state on the server. In REST terminology, calls that do not modify server state are referred to as safe. GET is the only safe HTTP method. A typical GET method call looks like this:

GET http://{host:port}/library/v1/books/0201709066

You may have noticed in the table above that the PUT and POST operations can both be used to create resources. This has been a cause of much confusion when creating REST services. Here is a hint from the book RESTful Web Services to help you decide when to use each method:

The difference between PUT and POST is this: the client uses PUT when it’s in charge of deciding which URI the new resource should have. The client uses POST when the server is in charge of deciding which URI the new resource should have.

Another way of stating this rule is to say that PUT should be used when creating a new URI and POST when calling an existing URI. So, you use PUT to create (as well as update) resources when the client controls the URI that references the resource. For example, the client can create a new book that is referenced by an ISBN number that it assigns in this manner:

PUT http://{host:port}/library/v1/books/0201709066

Notice that the client passed the ID 0201709066 (ISBN) by which this book will be referenced. This call creates a new book resource that can be accessed as shown in the GET method above. In this case, the PUT operation is appropriate because the client is responsible for specifying the URI that uniquely identifies the book. On the other hand, consider a service call where the client doesn't specify the URI:

POST http://{host:port}/library/v1/books/0201709066/reviews

This call creates a review for the specified book. In a case like this, the server will return the URI by which the review can be referenced in the Location header of the HTTP response. The client can then retrieve the review using a URI created by the server. The GET call would look something like this:

GET http://{host:port}/library/v1/books/0201709066/reviews/1

In this case, the new review resource is known as a subordinate resource. A subordinate resource is a resource that only exists in relation to some parent resource. In other words, a review cannot exist on its own without being attached to a book. POST is usually used for creating subordinate resources.

When updating a resource, it is important to keep in mind that PUT replaces the entire resource. In other words, if the PUT request shown above is called a second time with a different book representation, the entire book with ID 0201709066 will be replaced with the new representation. In order to update just a portion of the resource using PUT (without overwriting the entire entity), you would need to define a subset of the resource that is known as a shadow resource. For example, if the book had a "checkedOut" shadow resource, we could use PUT to set this value by updating the resource at this URL:

PUT http://{host:port}/library/v1/books/0201709066/checkedOut

This new checked-out resource would allow the client to use GET to determine if the book is checked out (without having to retrieve the entire book resource) or use PUT to update just the checked-out property of the book rather than the entire book resource (since it is replacing the entire checkedOut resource). The PATCH method described in RFC 5789 is another way to perform partial updates on a resource. It is an unsafe method (similar to POST) that allows the client to specify instructions regarding how a resource should be modified. Unfortunately, PATCH has not been widely adopted by the REST community and is not well supported by REST frameworks, tools, and documentation.

Though PUT always replaces an entire resource, it is possible to use POST to replace only a portion of it. Though not generally recommended, some REST services support partial updates using an "overloaded POST" operation against a single resource (as opposed to a collection to which POST requests are normally directed). In this case, the body of the overloaded POST message would contain only the portion of the resource that should be modified. The service would be required to interpret the request body and update the resource appropriately. This approach is very similar to the solution proposed by the seldom used HTTP PATCH method.

Note that PUT is idempotent while POST is not. Idempotent means that the operation can be safely performed multiple times. Since subsequent PUT requests would simply replace the original resource with an identical representation, performing the same PUT operation multiple times does not change the server state after the first call (setting it to the same state with each call). On the other hand, POST is not idempotent because it creates a new resource with each call. Performing multiple identical POST operations would cause the server state to change with every execution (as a new resource is created each time).

Finally, the DELETE method is used to delete resources. Like PUT, DELETE operates on an entire resource and is idempotent. That is, the whole resource is deleted rather than just a part of it. A typical DELETE method call looks like this:

DELETE http://{host:port}/library/v1/books/0201709066

After deleting a resource, it is no longer available. In this case, issuing a GET request to this resource would return an HTTP status code of 404 (Not Found).

The most basic pattern in REST API design is the use of plural resource names to represent collections and singular names or numeric IDs to represent individual resources. The following table illustrates how the standard HTTP methods manipulate both of these resource types:

ResourcePOSTGETPUTDELETE
/booksCreate a new bookGet a list of booksUpdate all booksDelete all books
/books/{ISBN}UndefinedGet a bookUpdate a bookDelete a book

NOTE: In addition to GET, POST, PUT, and DELETE, The HTTP specification defines the OPTIONS, HEAD, TRACE, and CONNECT methods. The OPTIONS method is used to query which HTTP methods are supported by a particular resource (returned in the Allow header). The HEAD method returns the HTTP headers without any body (response meta-data only). The TRACE and CONNECT methods are not used.

Resource URI

The URIs by which resources are referenced are a central part of any REST API. One of the most fundamental REST principles is that URIs should represent nouns, not verbs. There are two good reasons for this. First, the actions that can be performed have already been defined by the HTTP protocol (GET, POST, PUT, DELETE). Since they are standard, these actions are well understood by REST clients. We should not define new actions by including verbs in the URI. Remember, REST services are designed to perform a small number of operations (i.e., HTTP methods) on a large number of resources. This makes any new API very easy to learn. Second, since HTTP requires the client to specify an action using a standard HTTP method in each call, it only makes sense that this action would be performed against a concrete resource (noun) as opposed to another action (verb). For example, consider the following URI:

GET http://{host:port}/library/v1/getBooks

In REST parlance, this URI is saying "get the getBooks resource". This is redundant and a bit confusing. A better way to express this idea is through a URI like this:

GET http://{host:port}/library/v1/books

This URI just says "get the books resource". The representation of the books resource would naturally be a list of books stored in the library.

Another common guideline is to use plural nouns for collections of items. For example, consider the following service calls:

HTTP MethodURIDescription
GEThttp://{host:port}/library/v1/booksGet a representation of a list of books
PUThttp://{host:port}/library/v1/books/0201709066Create a new book resource with ISBN 0201709066
GEThttp://{host:port}/library/v1/books/0201709066Get a representation of the book with ISBN 0201709066
DELETEhttp://{host:port}/library/v1/books/0201709066Delete the book resource with ISBN 0201709066
POST

http://{host:port}/library/v1/books/0201709066/reviews

Create a new book review whose URI will be returned in the response

Notice how the second GET operation references an individual book in the books collection. This conveys the idea that we are retrieving a single book from the collection of all books.

The URIs for REST services will typically grow in a hierarchical fashion. For example, imagine that our library service allowed patrons to attach a review to each book. To accommodate this use case, the URI for this API should first reference the book to which the review applies and then specify the review itself in this manner:

GET http://{host:port}/library/v1/books/0201709066/reviews/1

To get a list of all reviews pertaining to the book identified by ISBN 0201709066, we would reference the collection of reviews using a URI like this:

GET http://{host:port}/library/v1/books/0201709066/reviews

NOTE: There has been much discussion in the REST community regarding whether it is most proper to use of the term URI or URL. In most cases, this debate is not constructive and the two terms can be used interchangeably. Technically, a URL is just a specific type of URI. However, since the other URI types (URN and URC) are rarely used, URIs and URLs are almost always equivalent. URL is considered a more "classical" (i.e., historical) term while URI is more "contemporary" (see RFC 3305).  Further,RFC 3986 states the following:

Future specifications and related documentation should use the general term "URI", rather than the more restrictive terms URL and URN.

Therefore, this document recommends that the term URL be deprecated in favor of URI.

Request Headers and Body

For each REST API method, the API designer must document which HTTP headers are required as well as the expected format of the request body (if applicable). The most common request headers used in REST service calls are Content-Type and Accept which are both used for content negotiation. Content-Type indicates the format of the request body. This header is required for HTTP methods that create or modify resources (i.e., POST and PUT) but is not applicable to GET and DELETE requests since they do not include a payload. The Acceptheader tells the server the response format desired by the client. For example, if the client wishes to receive resource representations in XML format, it would include an Accept header with a value of application/xml.

NOTE: If the server doesn't support the format of the body's request or the desired response, a 415 "Unsupported Media Type" error should be returned.

In addition to the HTTP headers, the expected format and content of the request body must be defined. This information specifies the format in which resource representations will be submitted to the server (e.g., JSON or XML) as well as the information contained in that representation (e.g., field names and values). In order to provide flexibility to the client, many REST services support multiple request formats such as JSON and XML. For example, here is a typical HTTP request that contains JSON formatted content in the body:

POST http://rest.service.com/library/v1/books/0201709066/reviews HTTP/1.1
Content-Type: application/json
Accept: application/json
{"author":"John Doe", "comments":"Very informative."}

The sample HTTP request above indicates that the client is creating a new review for book 0201709066 by performing a POST to the reviews resource. The Content-Type header indicates that the request body is formatted in JSON and the Accept header conveys that the client wishes to receive the response in JSON format. Similarly, here is the same request in XML format:

POST http://rest.service.com/library/v1/books/0201709066/reviews HTTP/1.1
Content-Type: application/xml
Accept: application/xml
<resource>
<author>John Doe</author>
<comments>Very informative.</comments>
</resource>

Though both the Content-Type and Accept headers are shown here, the Accept header is optional if its value is the same as Content-Type. In other words, the server should default to returning a response in the same format as the request. Of course, the Accept header should be included in all GET requests since a Content-Type header is not applicable in that case.

Response Status Code and Body

The response to a REST service call contains multiple items of interest to the client. First, a REST response always includes a status code that conveys the result of the requested operation. The available status codes are defined by the HTTP specification and are grouped into 5 ranges that each convey a general meaning. These five ranges are as follows:

RangeDescription
1xx (Meta)Used only in negotiations with HTTP server.
2xx (Successful)The operation was successful.
3xx (Redirection)The client must perform an additional operation to get what it wants.
4xx (Client-Side Error)There is a problem with the client's request.
5xx (Server-Side Error)An error occurred on the server that prevented it from servicing the request.

Here are some of the most common response status codes (codes in bold are the most common):

Status CodeDescription
200 (OK)The request was received and processed successfully.
201 (Created)Indicates that a new server resource has been created (in response to a POST or PUT request). The URI pointing to the new resource would be returned in the Location HTTP header.
202 (Accepted)The request was accepted and is being processed. Typically returned in response to long running asynchronous processes.
204 (No Content)The request was processed successfully but there is no content to return.
206 (Partial Content)The request was only partially satisfied. For example, the client may have requested all data covering a certain date range. If only a portion of the range is available, that portion may be returned in a 206 response.
300 (Multiple Choices)There are multiple options available and it is required for the client to select between them. For example, if the service hasn't established a default response format, it could respond with a 300 message indicating that the response can be returned in JSON or XML and the client must re-issue the request indicating which format is desired (in the Accept header).
301 (Moved Permanently)The URI for this service call has changed and all future calls should be made to the new location. Client should re-issue the same request to the new location. The new URI would be returned in the Location header.
304 (Not Modified)The resource hasn’t changed since it was last requested (based on the request's If-Modified-Since header or the value of an ETag in the If-None-Matches header) so the response will contain no body.
307 (Temporary Redirect)The requested resource has temporarily moved to the URI specified in the Location header but future calls should be made to the original location.
400 (Bad Request)The client's request was not properly formatted, was missing required information, or contained invalid information.
401 (Unauthorized)The client attempted to access a resource without providing the required credentials (authentication error).
403 (Forbidden)The client is authenticated but not allowed to perform the requested action (authorization error).
404 (Not Found)The requested resource does not exist.
405 (Method Not Allowed)The given HTTP method (GET, POST, PUT, DELETE, etc.) is not supported for the specified resource. The response should include an Allow header indicating the methods supported by that resource.
406 (Not Acceptable)The server does not support any of the formats specified by the request's Accept header.
409 (Conflict)Fulfilling the request would put the server into an invalid state.
410 (Gone)Indicates that the requested resource has been removed from service.
412 (Precondition Failed)The server wasn’t able to satisfy a precondition set by the client. For example, the body of a request indicates that account XYZ should be deleted if not active for the past 30 days. If the account is active, this code may be returned to indicate to the client that the account was not deleted because it is active.
415 (Unsupported Media Type)The server does not support the format of the request body as specified in the request's Content-Type header.
422 (Unprocessable Entity)The request was well-formed but was unable to be followed due to semantic errors. This error differs from a 403 in that this error involves the relationship between 2 or more fields rather than just a single field. A multi-field relationship may dictate that if one field has a value then the other field is required. For example, consider a resource that contains first name, last name, and company name. First name and last name may be optional as long as both are empty. However, if one is specified then the other becomes required.
500 (Internal Server Error)An error occurred on the server when attempting to satisfy the request.
503 (Service Unavailable)The request cannot be satisfied because a dependency on the server-side is unavailable. For example, the database went down or the authorization server is unavailable.
.............................................. 

In addition to the status code, responses that include a message body should also contain a Content-Type header. This header indicates the format of content contained in the body of the response (e.g., JSON, XML, image, etc.). To illustrate, here is a typical HTTP response (to a GET request) that uses the Content-Type header to indicate that the body of the response is in JSON format:

HTTP/1.1 200 OK
Date: Wed, 5 Sep 2012 06:25:24 GMT
Content-Type: application/json
{"isbn":"1146104553", "author":"Leo Tolstoy", "title":"War and Peace"}

In response to a GET request, the server returns a representation of the requested resource. Similar to the request body, the format and content of this representation is service-specific. The service consumer must refer to documentation provided by the service provider to determine how the body of the response should be interpreted.

Some REST service calls may not return anything in the body of the response. For example, in response to a POST request that creates a new resource, the service will typically just respond with a message indicating that the resource was created and the location of the new resource in a header as illustrated here:

HTTP/1.1 201 Created
Date: Thu, 5 Jun 2008 06:25:24 GMT
Location: http://rest.service.com/library/v1/books/1146104553

The response above indicates that a new book resource was successfully created and is available at the URI indicated by the Location header.

Resource Modeling

Resource modeling is the process of identifying resources and their relationships to each other. Each URI in a resource model should convey the resource hierarchy. For example, the forward slashes in the following URI are used to establish a hierarchical relationship between the specified resources:

http://api.nfl.com/v1/divisions/nfcWest/teams/arizona/players/11

This URI uniquely identifies player number 11 on the Arizona Cardinals team within in the NFC West division of the NFL. Given this URI, the following URIs should also represent addressable resources:

http://api.nfl.com/v1/divisions/nfcWest/teams/arizona/players
http://api.nfl.com/v1/divisions/nfcWest/teams/arizona
http://api.nfl.com/v1/divisions/nfcWest/teams
http://api.nfl.com/v1/divisions/nfcWest
http://api.nfl.com/v1/divisions

REST incorporates four useful patterns that should be applied when designing a resource model. The URIs above are modeled after REST's document (11, arizona, nfcWest), collection (players), and store (teams, divisions) patterns. The next section describes these patterns.

Resource Patterns

Resources exposed by a REST API should be modeled after one of the following resource patterns:

  • Document
  • Collection
  • Store
  • Controller

Each of these patterns, along with the non-RESTful RPC over HTTP service style, are described below.

Document

Document (a.k.a., entity) is the most basic pattern in a REST API. In essence, a document represents a single resource on the server (e.g., a file or a database record). The following URIs represent document resources:

http://rest.service.com/library/v1/books/0201709066
http://rest.service.com/library/v1/books/0201709066/reviews/1

Collection

The collection (a.k.a., list) pattern represents a group of document resources. The server may return a representation of the collection when presented with an HTTP GET request or add to the collection in response to an HTTP POST. The URI by which each resource can be addressed is assigned by the server and returned in the HTTP Location header. The following URI is an example of a collection resource:

http://rest.service.com/library/v1/books/0201709066/reviews

Store

The store pattern exposes a resource that serves as a repository where clients can store, retrieve, and delete resources. Unlike collections, the client assigns the URI for each new resource inserted into the store. The following URI is an example of a store:

http://rest.service.com/library/v1/books

Though this resource looks like a collection, it does not support the POST method. Instead, clients add books by performing a PUT against the store and including the book's ISBN number in the URI (thus creating a new document resource URI). The following PUT operation inserts or updates a resource and the DELETE action removes it from the store.

PUT http://rest.service.com/library/v1/books/0201709066
DELETE http://rest.service.com/library/v1/books/0201709066

Controller

Though not RESTful in the strictest sense, the controller pattern may sometimes offer the most intuitive solution to an API design dilemma. A controller is similar to a remote procedure call (RPC) because it attaches a new verb to an existing resource. Controller resources can be used to perform procedural operations that don't logically map to an HTTP method. Since they don't fit into the standard HTTP interface (using HTTP methods), controller resources should always be invoked using a POST request. Given that POST is semantically ambiguous, it is the only HTTP method that can be "overloaded" under certain circumstances to perform "non-CRUD" operations. The controller action (named "resend" in the following example) should be listed in query string using the "operation" name like this:

POST http://rest.service.com/library/v1/books/0201709066/overdueAlerts/1?operation=resend

Another way to implement the controller pattern is to list the action last in the URI path like this (not recommended):

POST http://rest.service.com/library/v1/books/0201709066/overdueAlerts/1/resend

From the URIs above, it is clear that an "overdue alert" with ID of "1" was previously added to a book resource's overdueAlerts collection. Using the controller pattern, the client is able to resend the same alert without having to create a new server-side resource.

The controller pattern is very similar to the "POX" style service (plain old XML over HTTP) except that it offers a little more structure. For example, a POX style service may create a generic service endpoint that is invoked like this:

POST http://rest.service.com/library/v1/BookService
Content-Type: application/xml
<resource>
<isbn>0201709066</isbn>
<overdueAlertId>1</overdueAlertId>
<action>resend</action>
</resource>

Since the functions performed by a POX service are not prescribed in the URI, it's possible that the BookService endpoint shown above could perform any book-related operation based on the message body. The controller pattern offers more structure by including the action in the URI (i.e., query string) and tying the custom operation more closely to the resource upon which it is acting. Notice how the controller pattern above allows the client to resend an overdue book alert without any message body at all. Additionally, controller resources maintain separate and distinct URIs for each action that doesn't logically map to a CRUD operation.

RPC over HTTP

Remote procedure call (RPC) over HTTP style services can be useful when performing actions or retrieving dynamically calculated values that are unrelated to persisted resources. This type of service is essentially the same as the POX (plain old XML over HTTP) style except that it isn't specific to XML (JSON payloads are also supported). Like the controller pattern, RPC and POX services violate some REST principles but, at times, may provide the most pragmatic solution. If an operation doesn't logically map to an HTTP verb and it can't naturally be associated with an existing resource, an RPC style API may be required. This service style serves as a sort of "catch-all" for any action that doesn't fit neatly into REST's resource-oriented HTTP view of the world. For example, consider a banking service that performs currency conversion. If a "currency" resource exists in the system then it may make sense to attach a new "convert" operation to the resource using the controller pattern like this:

POST http://api.bank.com/v1/currencies/euro/convert?to=dollars&amount=150

However, if currency is not a resource then it may be necessary to employ an RPC/POX style solution by defining a new "convertCurrency" action on the service root as shown here:

POST http://api.bank.com/v1/convertCurrency?from=euro&to=dollars&amount=150

Though we can add new verbs to the service root as shown above, some companies' standards mandate that a service endpoint be established for this type of RPC call and convention dictates that these endpoints use a "Service" suffix such as "CurrencyService" or "BankingService". Therefore, the recommended URI for a currency conversion service would look something like this:

POST http://api.bank.com/v1/BankingService?action=convertCurrency&from=euro&to=dollars&amount=150

Or, better yet, by specifying a more specific endpoint and including the verb in the URI, an RPC/controller hybrid can be created like this:

POST http://api.bank.com/v1/CurrencyService/convert?from=euro&to=dollars&amount=150

In this manner, a unique URI is defined for each RPC function. This hybrid approach is preferred since it is more consistent with the controller pattern.

REST API Guidelines

This section provides some basic guidelines to keep in mind and/or apply while designing and implementing REST services.

Designing with Nouns

Given that REST services are based on manipulating resources using standard HTTP methods, the REST service designer is required to think differently than when designing a remote procedure call (RPC) service. For example, consider that we have two resources named Customer and Magazine. In a traditional RPC service, we might call the "subscribe" method in order to subscribe a customer to a magazine. The client could invoke the service like this:

POST http://rpc.service.com/MagazineService/subscribe?customer=1234&magazineId=528233

Since HTTP does not define a subscribe action, this approach is not RESTful. Therefore, instead of calling a subscribe method, the REST service designer could create a new "subscription" resource that represents the link between a customer and a magazine. The client could then subscribe a customer to a magazine using a POST call that looks like this:

POST http://rest.service.com/customers/1234/subscriptions?magazineId=528233

Or a PUT like this (if the client dictates the ID in the URI):

PUT http://rest.service.com/customers/1234/subscriptions/528233

Notice how the RESTful approach removes the non-standard verb from the API call and replaces it with a resource that can be created using a standard HTTP method. Furthermore, this approach eliminates the need for an "unsubscribe" method since the subscription can be removed using a standard HTTP DELETE action against the new subscription resource. In a similar manner, if a user is required to login before accessing a REST resource, a standard HTTP POST could be used to create a "session" resource on the server rather than calling a procedural "login" method. When the user logs out, the client could use the HTTP DELETE method to remove the associated session resource or the PUT method to change its state to invalid.

Managing State through Links

In an earlier section, we discussed that one of the principles of REST is that responses should contain links to resources the client can call next. Providing the next URIs in response to previous requests is how a "connected" REST service is created. It allows clients to discover the resources and operations that are available as they exercise the service. This why REST stands for "Representational State Transfer". You can think of a properly designed REST service as a state machine. A resource starts in a given state (i.e., it has a specific representation). Clients can modify resource state by making calls to the REST service. After each state transition, the service indicates the next available states allowing for an orderly flow from one state to the next. The REST community commonly refers to this concept using the acronym HATEOAS which stands for "hypermedia as the engine of appliction state". To illustrate, consider the following request that retrieves a list of books:

GET http://rest.service.com/library/v2/books

The response to this request should include links to the referenced resources like this in XML:

<books href="/library/v1/books">
<book href="/library/v1/books/0201709066">
<author>Leo Tolstoy</author>
<title>War and Peace</title>
</book>
<book href="/library/v1/books/0486406512">
<author>Charles Dickens</author>
<title>A Tale of Two Cities</title>
</book>
<book href="/library/v1/books/161382310X">
<author>Herman Melville</author>
<title>Moby Dick</title>
</book>
</books>

And like this in JSON:

{"books": {"href":"/library/v1/books", ["book": {"href":"/library/v1/books/0201709066", "author":"Leo Tolstoy", "title":"War and Peace"}, "book": {"href":"/library/v1/books/0486406512", "author":"Charles Dickens", "title":"A Tale of Two Cities"}, "book": {"href":"/library/v1/books/161382310X", "author":"Herman Melville", "title":"Moby Dick"}]}}

Providing links dynamically as the client traverses a REST API is a powerful concept that makes it possible for REST clients to be constructed in an automated fashion without prior knowledge of every resource and operation exposed by the service.

Documenting a REST API

There are numerous styles and formats for documenting REST services. In SPI, it is recommended that REST APIs be documented using a format similar to the following:

Create Book Review

DescriptionAdds a new book review.
Version2
HTTP MethodPOST

URI

http://{host:port}/library/v2/books/{ISBN}/reviews

Request Headers

HTTP header names and values:

NameValueTypeRequiredDefault

Authorization

OAuth credentialsstringyes 

Content-Type

Format of request body. Valid values are:

  • application/json
  • application/xml
enumyes 
Accept

Desired format of the response body. Valid values are:

  • application/json
  • application/xml
enumnovalue of Content-Type header






Request Body

JSON or XML formatted string containing the following property names and values (XML root element "resource"):

NameValueTypeMaxRequired
review
Book reviewobject yes
    author
Author of reviewstring100yes
    comments
Review bodystring255yes
    rating
Author's rating (1 to 5)integer5yes
desiredPayment
Desired payment in exchange for reviewdecimal100.00no
bookCategory

Book category. Valid values include:

  • fiction
  • nonFiction
  • scienceFiction
enum no
tags
Tags for this reviewarray no
    tag
Tag that conveys categorical informationobject yes
        category
Categorystring50yes
Response Headers

HTTP header names and values:

NameValueTypeRequired

Content-Type

Format of response body. Valid values are:

  • application/json
  • application/xml
enumyes
LocationURI for the new book review resourcestringyes





Response Body

JSON or XML formatted string containing the following property names and values (XML root element "resource"):

NameValueTypeRequired
links
Links listarrayif successful
    link
Create a new resourceobjectyes
        rel
create-book-reviewstringyes
        href
/books/{ISBN}/reviewsstringyes
        method

POST

stringyes
    link
View this resourceobjectyes
        rel
view-book-reviewstringyes
        href
/books/{ISBN}/reviews/{id}stringyes
        method
GETstringyes
    link
Update this resourceobjectyes
        rel
update-book-reviewstringyes
        href
/books/{ISBN}/reviews/{id}stringyes
        method
PUTstringyes
    link
Delete this resourceobjectyes
        rel
delete-book-reviewstringyes
        href
/books/{ISBN}/reviews/{id}stringyes
        method
DELETEstringyes
    link
View API docsobjectno
        rel
create-book-review-docsstringyes
        href
Absolute URI to HTML documentation for this APIstringyes
        method
GETstringyes
errors
Collection of errorsarrayif error
    error
Error informationobjectyes
        code
Error codestringyes
        description
Error descriptionstringno
        fieldName
Field namestringno
        fieldValue
Field valuestringno
        moreInfo
URI providing more info about the error in HTML formatstringno
Response Codes

HTTP status codes returned in the header:

HTTP CodeDescription
201Review was successfully created.
400Bad request. Most likely caused by a missing or invalid parameter.
401Not authenticated.
403Not authorized.
Error Codes

Error codes returned in the body:

HTTP CodeError CodeDescription
400MISSING_REQUIRED_FIELDA required field was not included in the request message (see fieldName property).
400INVALID_FIELDA field in the request message contained an invalid value (see fieldName property).
403INSUFFICIENT_PRIVILEGESThis action requires additional privileges.
Notes 

Note that "curly braces" ("{" and "}") should be used to specify portions of a URI that the client must replace with actual values. Consider the following URI:

http://{host:port}/library/v2/books/{ISBN}/reviews/{reviewId}

The values in curly braces must be replaced with real values before a request can be submitted to the URI above.

Using the "Create Book Review" API documented above, a valid request using JSON would look like this:

POST http://api.library.com/v2/books/0201709066/reviews
API-Key: 2f9f8-34fjf-943jf-293nf
Content-Type: application/json
Accept: application/json
{"review": {"author":"John Doe", "comments":"Very informative.", "rating":4}, "desiredPayment":1.50}

And this is how it looks using XML:

POST http://api.library.com/v2/books/0201709066/reviews
API-Key: 2f9f8-34fjf-943jf-293nf
Content-Type: application/xml
Accept: application/xml
<resource>
<review>
<author>John Doe</author>
<comments>Very informative.</comments>
<rating>4</rating>
</review>
<desiredPayment>1.50</desiredPayment>
</resource>

Notice that the "Max" column in the documentation above can have different meanings based on the element type. For instance, the "Max" field for a string element indicates the maximum field length. On the other hand, the "Max" field for a numeric field represents the maximum numerical value.

REST API Recommendations

The following recommendations are intended to improve the consistency and reusability of REST services across SPI.

URI Design

URIs for REST services should be constructed according to the following recommendations.

Avoid using verbs in URIs

Except for rare cases when the controller pattern is deemed necessary, verbs should not be used in URIs (see "Designing with Nouns" section above).

Avoid underscores and hyphens in URIs

URIs and query strings should avoid the use of underscores and hyphens.

Use camel case to separate words in a URI

URIs and query strings should use camel case to separate words rather than underscores or hyphens.

Do not specify the resource format in the URI

Do not include artificial file extensions like *.xml or *.json to indicate the requested format of the response body. Rather, use the standard HTTP Accept header for this purpose.

Include version information in URIs

API version information should be included in the URI. This facilitates backwards compatibility even as new API versions are released. Version numbers should use incremental integers that are prefixed with a lower-case letter "v" like this:

http://rest.service.com/library/v1/books
http://rest.service.com/library/v2/locations

When upgrading to a new version of the API, simply update the URIs to the new version number.

Do not include a trailing slash character in URIs

URIs passed in the Location HTTP header or in the body of a response should not include a trailing slash.

Use singular nouns for documents and plural nouns for collections and stores

Documents, collections, and stores can be specified in a URI. Make it easy to distinguish between documents and the other resource types by using singular nouns for documents and plural nouns for collections and stores like this:

http://api.nfl.com/v1/divisions/nfcWest/teams/arizona/players/11

In this URI, documents are represented by singular names/IDs "nfcWest", "arizona", and "11" while collections and stores use the plural names "divisions", "teams", and "players".

Prefer concrete over abstract resource names

Use concrete resource names like "books" and "orders" rather than abstract names like "items" or "elements".

Use verbs or verb phrases for controller names

The controller pattern is used when an action doesn't naturally map to one of the standard HTTP verbs. Controller resources are used like remote procedure calls. For this reason, controller names should use a verb or verb phrase that describe the action they perform. Consider the following URIs:

POST http://api.bank.com/v1/checkImage/process
POST http://api.bank.com/v1/reindexDatabase

Pass filtering criteria in the query string

When filtering the values returned from a collection or store, include any filtering criteria in the query string portion of the URI. Consider this URI:

GET http://api.bank.com/v1/customers/48282/accounts?status=active

Use the query string to support partial responses

At times it is desirable to request only a portion of a resource. For instance, a mobile device might wish to get back only the customers name and address rather than a full customer record (for bandwidth or performance reasons). In this case, it would be advantageous for the client to be able to specify which fields it wants included in or excluded from the response. The "elements" query string parameter should be used for this type of filtering like this:

GET http://rest.service.com/library/v1/customers/1234?elements=firstName,lastName,address

Individual elements could also be excluded like this:

GET http://rest.service.com/library/v1/customers/1234?elements=!photo

or like this to exclude multiple elements:

GET http://rest.service.com/library/v1/customers/1234?elements=!(ssn,photo)

Note that the comma, exclamation point, and parentheses are used as special characters or delimiters in the URIs above. Those characters are considered "reserved" by the URI specification for cases like this. The specification indicates that reserved characters should NOT be percent-encoded when being used as a delimiter or special character. However, those same characters within a delimited value must be encoded so as not to conflict with the delimiter. This rule is stated in RFC 3986 as follows:

URIs include components and subcomponents that are delimited by characters in the "reserved" set. These characters are called "reserved" because they may (or may not) be defined as delimiters by the generic syntax, by each scheme-specific syntax, or by the implementation-specific syntax of a URI's dereferencing algorithm. If data for a URI component would conflict with a reserved character's purpose as a delimiter, then the conflicting data must be percent-encoded before the URI is formed.

In our case, we are defining the use of comma, exclamation point, and parentheses as an "implementation-specific syntax" so they should not be encoded when used in that manner.

Use the query string to support pagination

REST services often support server-side pagination of data in order to simplify client logic and reduce network traffic. For instance, a client may wish to receive a list of books available in a library but not all books at once. In this case, the client should be able to specify the starting index in the list of books and the number of books that should be returned. The query string should be used to pass this pagination information. Specifically, it is recommended that query parameters of "offset" and "limit" be used to indicate the starting record (zero-based) and the total number of records to return. Here's an example that should return the first 10 books from the library:

GET http://rest.service.com/library/v1/books?offset=0&limit=10

And this request returns books 100 through 149:

GET http://rest.service.com/library/v1/books?offset=100&limit=50

It is also recommended for APIs that support pagination to return the total number of records in the response body. This allows clients to create UIs that indicate where the returned subset of records fits into the entire collection (e.g., "page 5 of 63" or "items 50-59 of 625").

Prefer relative over absolute URIs

Relative URIs should be preferred when returning links within the response body. Absolute URIs include the host and port information whereas relative URIs contain only the path, query string, and fragment components. There are several reasons to prefer relative URIs including:

  • relative URIs tell the client that the same security credentials can be used to access the resource (since host/port do not change)
  • it may be difficult to determine the real virtual host name when constructing absolute URIs
  • absolute URIs may expose cross-site scripting issues

HTTP Methods

These recommendations in this section are specific to requests.

Use HTTP methods to operate on resources

Use the standard HTTP methods (POST, GET, PUT, DELETE) to manipulate resources whenever possible. The constructor pattern or POX style service should only be used when an action can't be naturally mapped to one of the HTTP verbs.

Use GET to retrieve representations of resources

Since GET is both safe and idempotent, it should always be used to retrieve representations of resources. Do not overload other methods to provide this functionality. Representations returned using other methods won't be cached.

Use PUT to create and update stored resources

PUT should be used whenever creating a new resource whose URI is assigned by the client (see store pattern in "Resource Patterns" section above). It should also be used whenever updating a collection or store resource.

Use POST to create resources in a collection

POST should be used for creating new resources in a collection (see collection pattern in "Resource Patterns" section above).

Use POST to execute controller actions

Since POST is the only HTTP method whose purpose isn't strictly prescribed, always use POST to execute a controller action.

Use DELETE to remove resources

DELETE should always be used to remove resources. Avoid using an overloaded POST for this purpose.

Support the OPTIONS method

The OPTIONS method is meant to convey the actions that can be performed on the specified resource. In response to an OPTIONS call to a particular resource, the server should return a comma-delimited list of HTTP methods that the resource supports in the Allow header. The response should contain no body. For example, a typical collection resource might return "Allow: GET,OPTIONS,POST" in response to an OPTIONS call.

HTTP Headers

These recommendations are specific to HTTP headers.

Content-Type header should always be used

An accurate Content-Type header should accompany every request and response that contain a message body. Supported text formats include "application/json", "application/xml", and "application/x-www-form-urlencoded".

Accept header should be used for content negotiation

The client should include an Accept header in order to indicate the desired response format. However, if the request includes a message body (such as a POST or PUT request) as well as aContent-Type header that describes the message format, then the Accept header is optional. If the Accept header is not included, the service's response should be in the same format as the client's request.

Pass resource URIs in response body in addition to the Location header

REST usually utilizes the HTTP Location response header to return the URI assigned to a newly created resource. However, some standards dictate that this URI be returned in a "links" collection within the response body. To remain consistent with standard REST practices, include the URI to the new resource in both the Location header and the response body.

Use appropriate HTTP status codes in response headers

Error conditions should be mapped to the standard HTTP status code that most closely describes the problem. Additional detail can then be included in the response body. Do not "tunnel" error conditions through a successful 200 "OK" response that contains error information only in the message body. API design should not violate REST principles simply to accommodate clients with a limited HTTP vocabulary (though a proxy service can be created for this purpose if necessary).

Include additional error detail in response body

Since HTTP status codes often cover a broad range of error conditions (e.g., "bad request"), more detailed error conditions should be returned in the body of the response. For example, the following response indicates that the 400 response was returned due to validation errors in two parameters:

HTTP/1.1 400 Bad Request
Content-Type: application/json
{"errors": [
"error": {"code":"MISSING_REQUIRED_FIELD", "description":"A required parameter is missing.", "fieldName":"author"},
"error": {"code":"INVALID_FIELD", "description":"A submitted value was invalid.", "fieldName":"rating", "fieldValue":"five"}
]}

It is also recommended that descriptive string literals be used for error codes rather than numbers. This convention removes one level of abstraction and improves readability of client code since comparisons will be made against human readable strings instead of cryptic numeric values.

Include an ETag header in GET responses to facilitate caching

The HTTP ETag response header (a.k.a., "entity tag") specifies a unique value that changes each time a resource is modified. Since responses to POST, PUT, and DELETE requests are not eligible for caching, only GET responses should include this header. It is recommended that the ETag header contain a numeric value indicating the exact time the resource was last modified (typically the number of milliseconds since January 1, 1970). A response including the ETag header looks like this:

HTTP/1.1 200 OK
Date: Wed, 5 Sep 2012 06:25:24 GMT
Content-Type: application/json
ETag: "1346847924572"
{"isbn":"1146104553", "author":"Leo Tolstoy", "title":"War and Peace"}

To cache this request, the client (or proxy server) can store the response along with the ETag value. The next time this resource is requested, the client can pass the ETag value in the If-None-Match request header to specify that a new representation should be returned only if the resource's ETag value has changed (indicating that the resource has changed since the response was cached). Here's an example request:

GET http://rest.service.com/library/v1/books/1146104553
Accept: application/json
If-None-Match: "1346847924572"

If the resource's current ETag value is different from the If-None-Match header then the resource is returned normally since it has changed since the client last retrieved it. Otherwise, the service should return an HTTP 304 (Not Modified) response without any body indicating that the client should use their locally cached version.

Note that the Last-ModifiedIf-Modified-Since, and Expires HTTP headers can also be employed to support caching. These headers include dates and times that indicate when a resource was last changed or when a cached version should be considered stale. Though these headers can be used to assist with caching, ETag is recommended since it avoids the complexities around working with specific times.

If the length of time until a resource is considered stale can be reasonably approximated, use the Cache-Control header with the max-age option like this:

HTTP/1.1 200 OK
Date: Wed, 5 Sep 2012 06:25:24 GMT
Content-Type: application/json
Cache-Control: max-age=3600, must-revalidate
{"isbn":"1146104553", "author":"Leo Tolstoy", "title":"War and Peace"}

The Cache-Control header shown above instructs the client that the response can be considered fresh (i.e., cached) for one hour after which a new representation must be retrieved from the server.

NOTE: If a resource is extremely time sensitive and should be considered stale immediately upon delivery, the response should include a Cache-Control header with the value of "no-cache". If no caching instructions are returned, all caching decisions are left to the client.

API Versioning

The recommendations in this section are specific to API versioning.

Versioning should be mandatory in every API

To allow APIs to evolve without adversely affecting existing clients, all APIs should include version information.

Versions should change infrequently

As long as they are not changed in a manner that could break existing clients, APIs should be able to evolve without changing the version. For example, adding new resources or supporting new operations for existing resources should not require a new version since these additions would not adversely affect existing clients. Similarly, adding optional parameters to existing APIs (as long as the API's behavior is not modified) would not require updating version information. In contrast, anytime a resource or operation is removed from service or a new required parameter is introduced, version information should be updated to indicate a change that breaks backwards compatibility. Integers are preferred for versioning because point releases imply frequent version changes.

Versions should be maintained for at least one cycle

At a minimum, the current version and the previous one should always be available. Another common rule of thumb is to maintain a version for at least 18 months. Beyond that, it is left to the judgement of the service provider as to how long older versions should be maintained. Versions should be documented as deprecated for at least one cycle before being made obsolete (i.e., removed from service).

Miscellaneous

This section contains miscellaneous recommendations that don't fit into another category.

APIs should be "symmetric"

An API is considered symmetric if it accepts and returns representations in the same format. For example, an API that accepts JSON should generally respond in that same format. A common exception to this rule is a REST service that accepts POSTed requests directly from an HTML form in "application/x-www-form-urlencoded" format. This service might choose to respond in a different format that supports hierarchical elements (e.g., JSON or XML). That said, a REST service that supports both JSON and XML should still honor the request'sContent-Type and Accept headers even when they are different.

Use a root element of "resource" in all XML representations

It can be challenging to document an API that returns both JSON and XML since XML requires a root element and JSON does not. For this reason, it is often simpler to use the same root element for all XML responses. A root element name of "resource" is recommended for all XML representations. The following XML illustrates this suggestion:

<resource>
<review>
<author>John Doe</author>
<comments>Very informative.</comments>
<rating>4</rating>
</review>
<desiredPayment>1.50</desiredPayment>
</resource>

Do not use anonymous JSON objects in resource representations

Use of anonymous JSON objects is very common in the JavaScript world. The following example demonstrates two anonymous objects inside of the "links" collection:

{"author":"Leo Tolstoy", "title":"War and Peace", "links": [
{"rel":"create-book-review", "href":"/books/0201709066/reviews", "method":"POST"},
{"rel":"view-book-review", "href":"/books/0201709066/reviews/1", "method":"GET"}
]}

Notice how the "links" collection contains two unnamed objects that each contain "rel", "href", and "method" properties. Since this format can't be represented in XML without adding an enclosing element for each link, objects in JSON arrays should be constructed in this manner:

{"author":"Leo Tolstoy", "title":"War and Peace", "links": [
"link": {"rel":"create-book-review", "href":"/books/0201709066/reviews", "method":"POST"},
"link": {"rel":"view-book-review", "href":"/books/0201709066/reviews/1", "method":"GET"}
]}

Though not technically required, notice how applying the object name of "link" to each item in the array allows this JSON representation to be unambiguously expressed in XML like this:

<resource>
<author>Leo Tolstoy</author>
<title>War and Peace</title>
<links>
<link>
<rel>create-book-review</rel>
<href>/books/0201709066/reviews</href>
<method>POST</method>
</link>
<link>
<rel>view-book-review</rel>
<href>/books/0201709066/reviews/1</href>
<method>GET</method>
</link>
</links>
</resource>

Responses should include links operations that can be performed on the current resource

REST services should return links to resources and operations that are likely to be of interest to a client in the current state. For example, consider the following request that creates a new book review:

POST http://rest.service.com/library/v2/books/0201709066/reviews

The response to this request should include links that indicate how the new resource can be manipulated as shown here in JSON:

{"links": [
"link": {"rel":"create-book-review", "href":"/books/0201709066/reviews", "method":"POST"},
"link": {"rel":"view-book-review", "href":"/books/0201709066/reviews/1", "method":"GET"},
"link": {"rel":"update-book-review", "href":"/books/0201709066/reviews/1", "method":"PUT"},
"link": {"rel":"delete-book-review", "href":"/books/0201709066/reviews/1", "method":"DELETE"}
]}

Meta-Data Locations

Meta-data that is specific to a particular resource should be specified in the query string. Examples of this type of meta-data include filtering, operation, and pagination information. Meta-data that represents cross-cutting concerns such as security or tracking should be included in the HTTP header. This type of meta-data is more "universal" and not specific to a single resource. Examples of meta-data that should be included in the HTTP header include authentication, tracking, and locale information.

Avoid redundant resources

Though each URI cannot reference more than one resource, it is possible to have more than one URI point to the same resource. This practice should be avoided since it adds complexity to the API without any additional value.

Use warning headers as necessary

HTTP defines Warning headers that can be utilized by the server to convey information to the client that is not evident from the status code or response body. Multiple warning headers are allowed with warning precedence established by their order within the headers list (with the top warning being the most critical). More information about HTTP warnings is available in RFC 2616. Warnings should include a numeric warning ID followed by a description in human readable form. Here is an example:

HTTP/1.1 200 OK
Date: Wed, 5 Sep 2012 06:25:24 GMT
Content-Type: application/json
Warning: 15 Account balance retrieved from cache

Document ID rules

The numeric ID assigned to document resources should meet the following requirements:

  • Globally unique OR unique within its parent resource
  • Should not be sensitive or private (may be publicly displayed in the URI)
  • May be sequential or unique random
  • Persistent so that clients can store the ID and reliably reference it later

Collection resource representations should include document links

Performing a GET request on a collection resource should return summary information for the documents in the collection. This summary information should always include the URI to each document returned. Consider the following example:

<books href="/library/v1/books">
<book href="/library/v1/books/0201709066">
<author>Leo Tolstoy</author>
<title>War and Peace</title>
</book>
<book href="/library/v1/books/0486406512">
<author>Charles Dickens</author>
<title>A Tale of Two Cities</title>
</book>
<book href="/library/v1/books/161382310X">
<author>Herman Melville</author>
<title>Moby Dick</title>
</book>
</books>
Comments