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

Continued from page 2. 

Since the API respects the semantics of the HTTP protocol, the caching features built in HTTP can be used to reduce the number of requests required to retrieve the information available in the service. Caching in HTTP is based in a number of headers: Cache-control, Expires, Etags, that can be used by clients but also by any other intermediate HTTP connectors, like proxys, to avoid new requests for resources whose state can be inferred to be valid.

Another problem with this version of the API is that clients still need to know in advance the identifiers for the resources exposed through the HTTP interface. Relationships between resources are only implicitly present in the structure of the URIs. Clients need to know these identifiers and relations in advance and any change in these identifiers or in the structure of the resources will break the client logic.

However, the adoption of HTTP uniform interface can improve the support for re-usable libraries in clients. For example, if certain conventions in the way URIs for the resources are constructed, generic ‘REST’ libraries like Rails ActiveResource can be used.

API Version 2: Hyperlinks

In this revision of the API we are going to explore a solution to the coupling between client and servers we detected in version 1 of the API. We’ll address the problem introducing a form of hypermedia: http hyperlinks.

In version 1 of the API clients need to know in advance the URIs of the resources in the API. Moreover, they need to compute URIs dynamically from hard-coded templates extracting resource string identifiers from the representation retrieved from other resources. We’ll remove these requirements adding links inside the resource representations.

Hypermedia can be defined as control information that is embedded in data, in our case in the representation of resources. Through hypermedia, the server bundles together with the data the mechanism that clients can use to request that the server perform a certain operation on a resource. Thanks to hypermedia, the interface to the information can be packaged with the information and stored away from the server.

One kind of hypermedia are hyperlinks. They encode a relation between resources identified by URIs. Following a link, a client can retrieve a representation of the resource through the HTTP uniform interface, without any need of documentation or previous knowledge of the resource URI.

We will introduce links into our API using properties marked with a special suffix _url. Clients will need to understand the combination of these specially marked properties and string value they have assigned as links to the related resources.

An example of this kind of APIs with hyperlinks introduced in an ad-hoc way into resource payloads is Github API.

#%RAML 1.0

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

types:
  User:
    properties:
      id: string
      email: string
      name: string
      self_url: string
      todos_url: string
  Todo:
    properties:
      id: string
      title: string
      description: string
      self_url: string
      user_url: string

# ...

Just adding links to the payload improves the reliability of the API and decouples clients from servers. Structure of URIs can change as long as the semantics for the link properties remain the same. If that is the case, client code does not need to change, we just need to find the right property and follow the link to retrieve the resource.

However, one of the problems with this solution is the use of JSON to encode the representation of resources. JSON doesn’t have syntactical support for links, we can only encode them as JSON strings in the payload.

Clients are still coupled with the server because it is impossible for them to discover links in the representation. Clients written to take advantage of links will only work for this particular API and if the property introducing the link changes, even those clients will break.

API Version 3: Data Graph

In the previous version of this API we have shown the benefits of adding hyperlinks to the representation of resources. However we found that we are limited in our capacity of working with these links when encoded as JSON because this format does not support hyperlinks. XML, the other representation we support, has a similar problem, but being an extensible data format, links can be introduced in a standard way using XLink.

In order to deal with hyperlinks in JSON we need to augmentate its syntax. Since JSON doesn’t provide extensibility features we will need a superset of the format identified by a completely new media type.

Many options have been proposed to introduce hypermedia on top of JSON, HAL or Siren are just two examples.

In this version of the API we will use for this purpose JSON-LD.

JSON-LD is a W3C recommendation, with a minimalistic syntax, good support for tools and increasing adoption.

Links in JSON-Ld are URI strings wrapped into a { "@id": URI } JSON object. To avoid ambiguity in the properties used in JSON objects, JSON-LD properties must also be URIs. To provide a simple way to transform properties strings into URIs, JSON-LD introduces a "@context" property where a "@vocab" prefix can be declared to be used by all properties in the object.

In the following revision of the API spec we will introduce a couple of additional types to describe JSON-LD syntax:

#%RAML 1.0

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

types:
  URI: string
  Link:
    properties:
      @id: URI
  Context:
    properties:
      @vocab: URI
  User:
    properties:
      @context: Context
      @id: URI
      email: string
      name: string
      todos: Link
  Todo:
    properties:
      @context: Context
      @id: URI
      title: string
      description: string
      user: Link

# ...

In this version of the spec we have introduced the Link and Context types and the URI alias to support JSON-LD syntax. We have also replaced the old id identifiers by JSON-LD @id URIs based identifiers.

When requesting a particular resource from the API:

curl -X GET -H "Accept: application/ld+json" http://todosapp.com/api/version_3/users/1/todos/1

The JSON-LD representation returned by the server looks like:

{
  "@context": { "@vocab": "http://todosapp.com/api/vocab#" },
  "@id": "http://todosapp.com/api/version_3/users/1/todos/1",
  "title": "Test",
  "description": "Test TODO",
  "user": { "@id": "http://todosapp.com/api/version_3/users/1" }
}

The same representation could be expressed in a different way omitting the @context property like:

{
  "@id": "http://todosapp.com/api/version_3/users/1/todos/1",
  "http://todosapp.com/api/vocab#title": "Test",
  "http://todosapp.com/api/vocab#description": "Test TODO",
  "http://todosapp.com/api/vocab#user": { "@id": "http://todosapp.com/api/version_3/users/1" }
}

The payload of the JSON-LD representation of this resource can be understood as a data graph where the properties are links between resources identified with URIs and literal properties:

The same graph can be serialised as sequence of assertions SUBJECT PREDICATE OBJECT:

<http://todosapp.com/api/users/1/todos/1> <http://todosapp.com/api/vocab#title> "Test" .
<http://todosapp.com/api/users/1/todos/1> <http://todosapp.com/api/vocab#description> "Test TODO" .
<http://todosapp.com/api/users/1/todos/1> <http://todosapp.com/api/vocab#user> <http://todosapp.com/api/users/1> .

This capacity of interpreting a JSON-LD document as a data graph is a powerful feature, specially when dealing with hyperlinks between resources.

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.