Your API isn’t RESTful — And That’s Good

Trevor Reed
9 min readMar 31, 2016

--

We have to stop using the terms REST and RESTful to describe APIs that aren’t RESTful. It’s hurting our APIs and confusing the purpose and definition of REST.

By Dwight Sipler from Stow, MA, USA (Bad, Tired GillieUploaded by Jacopo Werther) [CC BY 2.0 (http://creativecommons.org/licenses/by/2.0)], via Wikimedia Commons

If you understand why many Web APIs don’t qualify as RESTful, feel free to skip this first section.

The Background

At the turn of the millennium, a Ph.D. candidate by the name of Roy T. Fielding submitted his dissertation entitled Architectural Styles and
the Design of Network-based Software Architectures
. In chapter 5 of that dissertation, he introduces an architectural style he calls Representational State Transfer (or REST), and outlines the constraints necessary for a system to qualify as being RESTful (matching the architectural style of REST). A description of these constraints is available on Wikipedia.

Around the same time (1999), another term came into being — one of those buzz words that business-types like to throw around to make their ideas sound more relevant and innovative: Web 2.0. One of the ideas behind this buzz word (and it’s more technical sibling term, The Semantic Web) was the idea of a web of data consumable by machines.

This idea lead to, among other things, Web Services. A number of specifications for implementing web services popped up, including SOAP and XML-RPC. These protocols were a powerful means of exposing data and services to client applications, but they tended to be complicated to implement and had performance issues. The World Wide Web was posed for the advent of a simpler way of providing applications with a programming interface into this consumable web of data. Enter Web APIs and REST.

It was at this point in history that a highly sophisticated architectural style for distributed hypermedia systems collided with the raw force of a popular idea.

What most people wanted was a simple, standard alternative to protocols like SOAP that provided tightly-coupled APIs between their server and client applications. Some of the ideas behind REST seemed to fit the need well, but not all of them. So, people took what they needed and disregarded the rest (no pun intended). They didn’t, however, disregard the term REST; in fact, they borrowed it for this new Web API concept — never mind that REST already had a very specific meaning and definition.

Despite the efforts of Roy T. Fielding to the contrary, the Internet (as it is apt to do) hijacked the term REST and ran with it. The terms REST, RESTful, REST API, etc., have been inaccurately applied to all kinds of Web API implementations. This misuse of a previously defined term has further confused both the actual concept of REST, and best practices for how to implement a tightly-coupled Web API.

The Problem

Many developers, trying to build a simple API between their server and client applications, stumble upon or are directed to REST as a sort of collection of best practices for building APIs. If they understand REST correctly, they realize that it is not quite what they expected; regardless of their understanding, they realize that it is more than what they need. So they implement a “sort of RESTful” API, uncertain of their implementation because they don’t have the benefit of a strong body of standards or best practices for their (very common) use case.

The REST use case is one that requires decoupling between the server and client. The client only needs to understand how to navigate the hypermedia provided by the server; it can then use that hypermedia to transition to any of the available application states. The most common example of this use case is the World Wide Web, where the browser is the client, the URL represents the application state, and the user decides how to navigate the hypermedia (by clicking on links and such).

Many Web APIs are, in contrast and by design, tightly coupled to their clients and lacking hypermedia of any kind, thus violating a principle of REST. The use of hypermedia to discover API endpoints is unnecessary because API endpoints are known (via documentation or otherwise) at the time the client is being developed, and are hardcoded into the client.

The Solution

What we need is to formally define and standardize this common use case of tightly-coupled Web APIs, and we need to name this concept anew in order to end the confusion. This will allow the community to build up that strong body of standards and best practices independent of the concept and confusion of “REST”.

RESOURCEful APIs: A New Name, An Old Concept

To this end, I have gathered some of the best practices for building these tightly-coupled Web APIs, and submit them to the community, as a starting point, under the term RESOURCE or RESOURCEful APIs.

RESOURCE APIs: An API is considered RESOURCEful when it meets the following criteria:

  1. The API must use HTTP as its transport layer.
  2. All API endpoints (URLs) should be formed as a noun and represent a resource, collection of resources, or property of a resource.
  3. Resources that primarily or solely belong to a collection should be accessible at a URL that is prefixed by the collection’s URL. Properties or sub-collections that primarily or solely belong to a resource should be accessible at a URL prefixed by the resource’s URL.
  4. API version and resource identification parameters should be in the URL path. All other parameters should be in the query string or headers.
  5. API calls that truly do represent an action should be represented as a task queue (if asynchronous) or an agent (if synchronous) — note that these are both still nouns. The HTTP POST method should be used to trigger the desired action of the queue or agent.
  6. All HTTP methods implemented should be used for their defined purpose according to the HTTP Method spec, and not for any other purpose.
  7. All HTTP methods implemented should respect the principles of Safe and Idempotent. (Safe refers to not changing the state of the server; idempotent refers to not changing the state of the server upon subsequent requests after the first request of a given resource — e.g deleting a resource the second time has no additional consequences.)
  8. HTTP status codes should be used in all responses according to their defined purpose. 200 is always an acceptable response for a successful request, even if a more specific status code is available.
  9. The body of a request or response should represent a resource, collection of resources, or property of a resource.
  10. The format of a request body or a response body should be designated by the Content-Type header.
  11. If the API does not support the format of the request body, it should return a status of 415 (Unsupported Media Type).
  12. The format of a response body should, if possible, be in the format requested by the Accept header. If the API does not support the requested response format, the API should return a status of 406 (Not Acceptable).

I also recommend these best practices

  1. Make URLs lowercase
  2. Create a means for clients that don’t support certain HTTP methods or status codes to use fallbacks instead (e.g. JSONP requires that all responses have a 200 status code)
  3. Always support the JSON format — XML is optional
  4. Always host APIs using HTTPS
  5. When an API creates a new resource, it should return the identifier for that resource somewhere in the response

Examples & References

As a convenience, and to help solidify these concepts, I’ve included the following references and examples:

Common HTTP Methods

  • GET — safe, idempotent — Retrieves a representation of the resource at the requested URL
  • POST — not safe, not idempotent — Creates the submitted resource representation at a new location under the requested URL
  • PUT — not safe, idempotent — Stores the submitted resource representation at the requested URL, replacing any existing resource at that URL
  • PATCH — not safe, not (necessarily) idempotent — Partially updates the resource at the requested URL with the submitted information. Attempting to PATCH to a non-existent resource should fail with a status code of 400.
  • DELETE — not safe, idempotent — Removes the resource at the requested URL

Behavior of HTTP Methods On Asynchronous Task Queues

  • GET — Retrieve information about the queue or a specific task in the queue
  • POST — Add a task to the queue
  • PUT — Update an existing task in the queue
  • PATCH — Partially update an existing task in the queue
  • DELETE — Remove a task from the queue

Behavior of HTTP Methods On Agents

  • GET — Retrieve the status of an agent or one of it’s actions
  • POST — Trigger the desired action
  • PUT — Undefined
  • PATCH — Undefined
  • DELETE — Reverse, if possible, a previously triggered action

Common HTTP Status Codes — all but 2XX level status codes are considered unsuccessful.

  • 200 OK — Response was successful
  • 201 Created — The entity submitted in the request body was created (synchronously)
  • 202 Accepted — The entity submitted in the request body will be created (asynchronously)
  • 204 No Content — No need to update the view
  • 205 Reset Content — Client should reset the view
  • 206 Partial Content —Partial content returned (e.g. ranged or paginated content)
  • 400 Bad Request — The request was malformed
  • 401 Unauthorized — The client is not authenticated with the server
  • 403 Forbidden — The client is authenticated with the server, but not authorized to perform the requested operation on the requested resource
  • 405 Method Not Allowed — The HTTP method used is not allowed on the requested URL
  • 409 Conflict — There was a conflict when performing the operation, for example, the request attempted to update a resource that had already changed
  • 500 Internal Server Error — An error on the server occurred and was not handled
  • 501 Not Implemented —The HTTP method is not currently implemented for the requested resource
  • 503 Service Unavailable — The server or one of it’s dependencies (such as a database) is unable to respond due to overload, outages, etc.

Example: Doctors Office API

Resource API Endpoints:GET    /customers
POST /customers
GET /customers/<id>
PUT /customers/<id>
PATCH /customers/<id>
DELETE /customers/<id>
GET /customers/<id>/appointments
POST /customers/<id>/appointments
GET /customers/<id>/appointments/<id>
PUT /customers/<id>/appointments/<id>
PATCH /customers/<id>/appointments/<id>
DELETE /customers/<id>/appointments/<id>
GET /customers/<id>/cell_phone
PUT /customers/<id>/cell_phone
Asynchronous Task Queue API Endpoints:GET /outgoing-emails
POST /outgoing-emails
GET /outgoing-emails/<id>
PUT /outgoing-emails/<id>
PATCH /outgoing-emails/<id>
DELETE /outgoing-emails/<id>
Synchronous Agent API Endpoints:GET /patient-discharger
GET /patient-discharger/<id>
POST /patient-discharger
DELETE /patient-discharger/<id>

Common Request Use Cases (Methods & URLs):

Retrieve information about a generic resource not in a collectionGET    /resourceUpdate an existing resource not in a collectionPUT    /resourcePartially update an existing resource not in a collectionPATCH  /resourceRemove an existing resource not in a collectionDELETE /resourceRetrieve a collection of resourcesGET    /collectionRetrieve a specific resource from a collectionGET    /collection/resource-idCreate a new resource inside a collectionPOST   /collectionUpdate an existing resource inside a collectionPUT    /collection/resource-idUpdate a single property of an existing resource inside a collectionPATCH  /collection/resource-id
PUT /collection/resource-id/property
Remove a single property from an existing resource inside a collectionDELETE /collection/resource-id/propertyRemove an existing resource inside a collectionDELETE /collection/resource-idRemove or empty an entire collectionDELETE /collectionReplace or update an entire collectionPUT /collectionUpdate a range of resources inside a collectionPATCH /collectionAdd a task to a queuePOST /queueGet information about all tasks in a queue and/or the queue itselfGET /queueGet information about a specific task in a queueGET /queue/task-idUpdate a task in the queuePUT /queue/task-idPartially update a task in the queuePATCH /queue/task-idRemove a task from the queueDELETE /queue/task-idTrigger an agent to perform an actionPOST /agentGet the results of an action performed by an agentGET /agent/action-idReverse an action performed by an agent (undo)DELETE /agent/action-idGet the status of an agentGET /agent

Common Response Use Cases (Status Codes)

Client successfully requests "GET /resource":200Client attempts to PATCH a non-existent resource404

Further Reading

I include the following links, not necessarily as examples of best practices, but in some cases as examples of the confusion that has sprung up around the idea of “RESTful APIs.”

Feedback

If you feel I have missed an important best practice, or that the references or examples are deficient, or for any other feedback, please respond below. And thanks for reading!

--

--