Fresh Blurbs

Pragmatic RESTful API Design

"REST vs. SOAP" has been an ongoing battle for quite a while, in the tech industry. There is a seemingly upwards trend of the number of open RESTful APIs emerging vs. the SOAP ones. It's probably safe to conclude that, at least among open APIs, REST is winning.

The problem, however is the ambiguity of the answer to the basic question: what is REST?

RESTful API Design

Representational State Transfer (REST) is an architectural style, for distributed hypermedia systems. It was initially defined in 2000 by Roy Fielding in his doctoral dissertation. REST is based on the concept of transfer of representations of resources. A resource is any application entity that can be uniquely addressed with a Uniform Resource Locator (URL). A representation of a resource is a document that captures current or intended state of a resource.

Since REST is an architectural style not: a well-defined specification, it leaves a lot for interpretation. However, with the increased proliferation of the RESTful style in popular web APIs, some common traits have emerged which are now recognized as best-practices in RESTful web API design. REST, in theory, is not limited to the web, but for the purposes of this blog post we assume to be discussing REST in the context of the world-wide web.

Basic Principles

  • Proper RESTful APIs extensively utilize HTTP Protocol. Usage of HTTP methods for CRUD, standard HTTP response codes, common HTTP headers and Mime Types is a common practice.
  • URLs in RESTful APIs are semantic, resource-centric and have a general structure which looks something like the following:\

    http://api.example.com/{ver}/{lang}/{resource_type}/{resource_id}.{output_format}?{filters and api_key as arguments}
    

    where:

    • {ver} indicates the version of the API and allows changing API syntax/behavior without breaking legacy clients (not necessarily providing "new" functionality for them, however). Usually version can be omitted, and it defaults to the latest stable version of the API.
    • {lang} in multilingual APIs, is a two-letter ISO abbreviation for a language, e.g. "en" for English, "es" for Spanish etc. Typically defaults to "en" for English, if ommited.
    • {resource_type} is the name of the resource, e.g.: user, story, cart, line item etc.
    • {resource_id} is a unique identifier of the resource. Can be numeric or alpha-numeric (e.g. in usernames). Often sequence numbers of the internal database schemas are used as identifiers, however usage of universally unique identifiers (UUIDs) leads to better-designed APIs.
    • {output_format} - commonly used to let API know which format response is requested in: xml, html, json, bson, rss, atom are some of the common formats implemented. Frequently format can also be indicated using the HTTP Accept-headers.
  • Resources in RESTful URLs are often chained. For instance, to access an item in a user's order the resource part of the URL may look like: "user/2323/order/54234/line_item/73321". Important thing to remember about resource chaining is that it represents a hierarchy: the line item belongs to the order and the order belongs to the user.

  • Client-server interactions in REST are stateless: in that request must always contain all of the information necessary to understand the request and may not take advantage of the state stored on the server.

  • REST encourages caching of requests that can be cached, to minimize network bandwidth utilization. APIs should implicitly or explicitly mark response to a request as cacheable or non-cacheable. If a response is cacheable, then clients are encouraged to locally cache it and reuse data for later, equivalent requests. HTTP headers are typically used to label cacheable content and indicate the permitted duration of cache.

Idempotent vs Non-idempotent Operations

Idempotence (pronounced: eye-dəm-poh-təns) is the property of certain operations in mathematics and computer science, that they can be applied multiple times without changing the result.

~ Wikipedia

  • A unary operation is idempotent if, whenever it is applied twice to any value, it gives the same result as if it were applied once. For example, the arithmetic absolute value function is idempotent:\

    abs(abs (x)) = abs(x).
    
  • A binary operation is idempotent if, whenever it is applied to two values, it always gives the same value for those two as the result. For example, the operation giving the minimum value of two values is idempotent:\

    min(a, b) = x
    

API Operations and Idempotence

Read/Write APIs commonly use following major HTTP Methods: GET, PUT, DELETE and POST. Out of these HTTP methods, only GET is cacheable and only POST is non-idempotent. Considering idempotence properties, entity CRUD operations are matched to HTTP methods as follows:

  • Create - is either HTTP POST or HTTP PUT:

    • HTTP POST is used for non-idempotent creation. This is when entity identifier is not passed during creation but needs to be generated by the back-end. For instance, news story creation operations are non-idempotent. Entity identifier (primary key) for the created entity is returned in the response, if entity creation was successful. Since the operation is non-idempotent, issuing it twice will create two similar entities.
    • HTTP PUT is used for idempotent creation. User creation is often idempotent because client provides specific, unique entity identifier (username) that the user must be created with. Typically each entity type only supports either idempotent or non-idempotent creation. Usually most content is non-idempotent and is created with HTTP POST.
  • Read - is HTTP GET. It is cacheable, and idempotent.

  • Update - is HTTP PUT. It's idempotent, and entity identifier must be supplied in the request.

  • Delete - is HTTP DELETE if it can be idempotent, which is the case if content is only marked as "deleted" (delete == disable). Since content is never really "deleted", but just marked disabled or inactive, you could keep "deleting" the same content and you keep receiving HTTP 200 (idempotence in action), until the content is actually removed from the database i.e. does not exist anymore (see truncate operation).

  • Truncate* - is HTTP POST. It removes an item from the database / deletes it permanently. It's non-idempotent. If you attempt to truncate an already truncated object, you will get HTTP 404 as a response because the second time entity does not exist, anymore.

Most Common HTTP Response Codes Used in RESTful APIs

200 OK: Success!

201 Created: a common return code for a successful HTTP POST (or PUT in idempotent creations)

202 Accepted: request was accepted, but has not fully been processed, yet. A common code to return for asynchronous calls.

304 Not Modified: There was no new data to return (think: cache).

400 Bad Request: Invalid Request. Error message will be returned to provide further details.

401 Unauthorized: Authentication credentials were missing or incorrect.

403 Forbidden: Valid request that was refused. Attempt to access a resource that the client does not have permission to. Error message will be returned to provide further details.

404 Not Found: The URL requested is invalid or the resource requested, such as a story or a user, does not exists.

406 Not Acceptable: Returned when parameters passed are correct in theory and individually, but when combined can not be satisfied because the combination makes no sense (e.g. cart_id from one user is used with a user_id from another user). If possible, an error message will be returned to provide further details.

500 Internal Server Error: Something went horribly wrong and we were not smart enough to provide more details :) Hopefully a very rare response code.

503 Service Unavailable: Servers are offline for maintenance or went down under load (oops).\

TL:DR; Summary

REST is an architectural style. It is loosely defined and often misinterpreted. While there is no official specification, a pragmatic approach to implementing reasonably RESTful API is to follow the fundamental principles as outlined in Fielding's original work, implemented in HTTP 1.1 and adopt best practices from the industry.

While this blog post is in no way a canonical specification for REST, it emphasizes some of the major architectural principles behind REST: resource-orientation, HTTP methods, statelessness, idempotent vs. non-idempotent operations, Mime Types, versioning and standard response codes.

comments powered by Disqus