How to Take Your API From RPC to Hypermedia in 7 Steps

Continued from page 1. 

Understanding this version of the API requires reading the textual description of the operation and examining the payloads of requests and responses. The API describes a private communication protocol.

Coupling between clients and servers is also high. Client code must be tailored to match the description of the API endpoints. Any change in the service interface will break the client and it’s impossible to rely on any kind of client library other than basic HTTP library support.

On the other hand the removal of some of the REST constraints also has benefits. For example, since we are not imposing the notion of resources, we can save some requests to the server just by introducing end-points that accept arrays of identifiers to operate over sets of entities in the server side. It might be also possible to change the transport layer easily without changing the client code, just replacing the basic HTTP functionality by some other transport protocol. One example of this kind of API is Dropbox HTTP API.

API Version 1: Resources

The main change we are going to make in this version of the API is to add one of the main REST architectural elements: the notion of resource and resource representations.

If we just look at the version 0 of the API, we can see how different operations in the API rely on identifiers being received or returned by the operations endpoints. These identifiers designate entities in the service that operations in the API manipulate: users and todos.

If we assigned an identifier, a URI, to each of these entities we can consider them resources from a REST point of view. Resources are not to be confused with the actual state they are in. We can have resources for values that don’t exist yet, we can have multiple resources pointing to the same state, we can have resources whose state don’t change in time, and resources whose value changes constantly in time. The only important thing is that the semantics of the resource should not change, for example, the resource identified by  http://todosapp.com/api/version_1/users/latest should always point to the latest user created in the system.
When we use the HTTP protocol to dereference a HTTP URI and retrieve the state associated to the resource the URI identifies, what we obtain is a representation of that state. This representation is fully transferred from the server to the client and it includes data and meta-data and can have multiple formats.

Clients need to select the most convenient representation for a resource among the representations available in the server using a mechanism known as content negotiation, involving the exchange of HTTP protocol headers containing media types.

Finally, in order to define how resources can be manipulated using the HTTP protocol, REST imposes another architectural constraint in our API: the uniform interface.

This interface defines the contract between client and servers and it is based in the semantics of the HTTP protocol. It includes a small set of methods that define how resources can be created, read, updated and deleted (CRUD).

In this new version of the API we will use these architectural elements and constraints to rewrite our API:

  • We will group our entities in 4 main resource types: collection of users, individual users, collection of todos and individual todos
  • Different representations for the resources: JSON and XML are introduced
  • We map the CRUD functionality offered by the functions exposed by the API version 0 and we map them to the right HTTP methods

The outcome is a API similar to most RESTful APIs built nowadays, for example Twitter’s REST API

#%RAML 1.0

title: Todos Service
baseUri: http://todosapp.com/api/version_1
mediaType: [ application/json, application/xml ]

types:
  User:
    properties:
      id: string
      email: string
      name: string
  Todo:
    properties:
      id: string
      title: string
      description: string

/users:
  get:
    description: Gets all the users in the service
    responses:
      200:
        body: User[]
  post:
    description: Creates a new user
    body:
      properties:
        email: string
        name: string
        password: string
    responses:
      201:
        headers:
          Location:
            type: string

/users/{id}:
  get:
    description: Information about a single user of the service
    responses:
      200:
        body: User
  delete:
    description: Deletes a user from the service

/users/{id}/todos:
  get:
    description: Lists all the Todos created by a user
    responses:
      200:
        body: Todo[]
  post:
    description: Creates a new Todo for a user
    body:
      properties:
        title: string
        description: string
    responses:
      201:
        headers:
          Location:
            type: string

/users/{id}/todos/{todo_id}:
  get:
    description: Information about a single Todo for a user
    responses:
      200:
        body: Todo
  delete:
    description: Deletes a Todo for a User from the service

One of the trade-offs we have introduced with the addition of resources and the HTTP uniform interface is that achieving certain functionality might involve a bigger number of HTTP requests.

A partial solution to this problem can be found in another additional architectural constraints introduced by REST: cacheability.

Antonio Garrote Principal Engineer at Mulesoft in the mornings, researching on RESTful semantic web services in the evenings. Clojure enthusiast all day long.
 

Comments

Comments(4)

alexsmith84

Great article! I'm currently only learning but i do have some ideas for future projects and i feel like your tips will help me a lot! I'm currently trying to do something with doahomework.com API

jamesrogers84

This seems to look interesting, looking forward for more of these.