Hypermedia seems not to be a popular option between API designers these days. On the other hand REST (or RESTful) API design is supposed to be the orthodoxy in API design and implementation.
This is not a surprise since REST, by simple abuse of the term, has become just a concept vaguely related to the use of ‘resources’ and a conventional mapping CRUD operations to HTTP methods.
But REST should be much more than that. Fielding’s thesis was an attempt of systematically describe the network architecture of the Web built on a few key components (HTTP, URI, HTML). The meaning of architecture in REST is just a process of imposing constraints over the many possible ways of building distributed network systems.
Each of these constraints limit the way we can build the system, but at the same time, they make the final result more reliable, extensible, decoupled and able to scale. On the other hand, these same constraints can have a negative impact in other aspects, like raw performance. They are conscious trade-offs.
When building RESTful APIs these days, most of the problems come from the lack of reliability, fragility, coupling and the lack of scalability.These are the very same problems that REST architectural principles should help us to avoid.
There’s something wrong in our RESTful APIs.
A few months after the first Web server was bootstrapped by Tim Berners Lee at CERN, the Web was already composed of distributed servers linking information between multiple organisations. A couple of implementations of a generic client for the Web were already being written. Both things are still impossible to achieve in the Web of data APIs.
If we look again at the components of the Web, HTTP is the only one we truly embrace. URI and HTML (or a version of HTML for data), and their combined use as hypermedia are lacking in most APIs.
This post tries to explore how hypermedia can be used in API design to fix some of the issues we have mentioned before: fragility, coupling and lack of scalability.
Instead of just discussing abstract notions and enumerating technologies and standards, the approach will be slightly different. We’ll start with a fully RPC API, with almost no other constraints than using the HTTP protocol for data transport, and we will start adding REST constraints through different revisions, trying to understand the trade-offs involved in each small change.
The toy API we will be improving is just another variation of a ‘Todos’ service, where users can register and create and edit lists of Todo notes.
API Version 0: RPC
The initial version of the API is just a list of RPC operations invoked over the HTTP protocol.
#%raml 1.0 title: Todos Service baseUri: http://todosapp.com/api/version_0 mediaType: application/ JSON /usersServiceCreate: description: Creates a users post: body: properties: email: string name: string response: body: properties: id: string /usersServiceList: description: Lists all Users post: response: body: type: array: items: properties: id: string email: string name: string /usersServiceDelete: description: Creates a users post: body: properties: id: string /todosServiceCreate: description: Creates Todos for a user post: body: properties: user_id: string todos: type: array items: properties: title: string description: string response: body: properties: id: string /todosServiceList: description: Lists all Todos post: response: body: type: array items: properties: id: string user_id: string title: string description: string /todosServiceDelete: description: Deletes todos post: body: id: string
There are only two REST restrictions in this design:
- Client/server architecture: There’s a clear separation between the client side and server side of the service with a well defined boundary between them and clear separation of concerns.
- Sateless architecture: all requests to the API include all the information required for the service to process the request. There are no dependencies between requests that can be issued in any particular order. Session state is kept in the client.
The API uses the HTTP protocol just as a transport layer for the invocation of remote functions in the server. The semantics of HTTP are ignored, all requests are bound to POST HTTP requests. It doesn’t take advantage of any of the features of the protocol, like caching headers, HTTP is just a convenient mechanism to invoke remote functions.