Unexpected Token
Published in

Unexpected Token

illustration by adrien griveau

How your API could benefit from Hypermedia

  • client-server
  • stateless
  • cacheable
  • layered-system
  • uniform interface
  • code-on-demand (optionally)
  • resources are unambiguously requested via URIs,
  • representations of resources are manipulated (you’re always playing with a “view” of a resource, not the resource itself),
  • messages are self-descriptive and self-contained,
  • transitions and actions should be clearly exposed to the client by the server, via hyperlinks and hypertext. This point is often referred as HATEOS: Hypermedia as the engine of application state.

HATEOS constraints applied to APIs

GET https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000
{
"previous_cursor": 0,
"ids": [
657693,
...,
783214
],
"previous_cursor_str": "0",
"next_cursor": 0,
"next_cursor_str": "0"
}

Server developers are responsible for providing the next actions a client can take from each point in the application.

  • “As a client developer, is it really possible to create an API client smart enough to use hypermedia relations to perform actions on websites without any knowledge of the website structure?”
  • “As a server developer, can I describe my application logic via links and relations that would be self-comprehensive by any hypermedia client?”
client(any_website).auth(login, password).get_friends()
  • by creating formats for our APIs: Atom+Pub, Collections+JSON, Siren, HAL, …
  • by creating repositories of link relations (see schema.org)

To approach hypermedia APIs, it is important to understand that every media type (or Content-Type) should define a processing model.

Hypermedia APIs remain not massively used

  • writing an exhaustive list of link relations seems unrealistic: even if efforts are made to simplify the description of new link relations (JSON-LD is a good example)
  • APIs are not (quite) the web, they are designed to interconnect services and feed end-user applications. Using hyperlinks and describing link relations yield bigger (in size) replies and slightly more requests in a world when connectivity is still an issue in many cases (notably in mobility: smartphones, etc..)
  • definition of a processing model according to a Content-Type (or simpler: formatting of replies),
  • comprehensive description of your API via links and their associated descriptors (especially standard ones as defined by the IANA).

Hypermedia API case in point

GET https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000
{
"previous_cursor": 0,
"ids": [
657693,
...,
783214
],
"previous_cursor_str": "0",
"next_cursor": 0,
"next_cursor_str": "0"
}
  • I have a list of id, which are, as we can guess, our friends’ ids, but what can I do with these? How can I fetch their user information?
  • As a human, I guess that next_cursor is something to fetch the next page of ids, but what is the associated URL? And is there something more generic to say that something can be used to fetch the previous and next page of results?
  • « prev »: indicates that the link’s context is a part of a series, and that the previous in the series is the link target.
  • « next »: indicates that the link’s context is a part of a series, and that the next in the series is the link target.
{
"prev": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000",
"ids": [
657693,
...,
783214,
],
"next": "https://api.twitter.com/1.1/friends/ids.json?screen_name=twitterapi&count=10&cursor=0",
}
{
"prev": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000",
"ids": [
{
"657693": {
"user_lookup": "https://api.twitter.com/1.1/users/lookup.json?user_id=657693",
"friends": "https://api.twitter.com/1.1/friends/ids.json?user_id=657693"
},
...,
"783214": {
"user_lookup": "https://api.twitter.com/1.1/users/lookup.json?user_id=783214",
"friends": "https://api.twitter.com/1.1/friends/ids.json?user_id=783214"
}
}
],
"next": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=10&cursor=0",
}
{
"collection": {
"version": "1.0",
"href": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000",
"links": [
{
"rel": "prev",
"href": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=5000"
},
{
"rel": "next",
"href": "https://api.twitter.com/1.1/friends/ids.json?cursor=-1&screen_name=twitterapi&count=10&cursor=0"
}
],
"items": [
{
"href": "https://api.twitter.com/1.1/users/lookup.json?user_id=657693",
"data": [
{
"name": "user_id",
"value": "657693"
}
],
"links": [
{
"rel": "user_lookup",
"href": "https://api.twitter.com/1.1/users/lookup.json?user_id=657693",
"prompt": "User Info"
},
{
"rel": "friends",
"href": "https://api.twitter.com/1.1/friends/ids.json?user_id=657693",
"prompt": "Friends",
"render": "collection"
}
]
},
{
"href": "https://api.twitter.com/1.1/users/lookup.json?user_id=783214",
"data": [
{
"name": "user_id",
"value": "783214"
}
],
"links": [
{
"rel": "user_lookup",
"href": "https://api.twitter.com/1.1/users/lookup.json?user_id=783214",
"prompt": "User Info"
},
{
"rel": "friends",
"href": "https://api.twitter.com/1.1/friends/ids.json?user_id=783214",
"prompt": "Friends",
}
]
}
]
}
}
  • stable
  • comprehensive
  • adaptable

Useful Resources

You like? Hit “Recommend” and subscribe to our collection!

--

--

Bi-monthly stories about code, tech and shared experiences. For developers. UT is brought to your by eFounders.co — startup studio building great SaaS startups.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store