The Ketting library is an attempt at creating a 'generic' hypermedia client, it supports an opinionated set of modern features REST services might have.
The library supports HAL, JSON:API, Siren, Collection+JSON, Web Linking (HTTP Link Header) and HTML5 links. It uses the Fetch API and works both in the browsers and in node.js.
const ketting = new Ketting('https://api.example.org/');
// Follow a link with rel="author". This could be a HTML5 `<link>`, a
// HAL `_links` or a HTTP `Link:`.
const author = await ketting.follow('author');
// Grab the current state
const authorState = await author.get();
// Change the firstName property of the object. Note that this assumes JSON.
authorState.firstName = 'Evert';
// Save the new state
await author.put(authorState);
Ketting is a library that sits on top of a Fetch API to provide a RESTful interface and make it easier to follow REST best practices more strictly.
It provides some useful abstractions that make it easier to work with true
hypermedia / HATEAOS servers. It currently parses HAL and has a deep
understanding of links and embedded resources. There's also support for parsing
and following links from HTML documents, and it understands the HTTP Link:
header.
Using this library it becomes very easy to follow links from a single bookmark, and discover resources and features on the server.
Supported formats:
- HAL
- Siren
- HTML - Can automatically follow
<link>
and<a>
element withrel=
attributes. - HTTP Link header - automatically registers as links regardless of format.
- JSON:API - Understands the
links
object and registers collection members asitem
relationships. - application/problem+json - Will extract useful information from the standard problem object and embed them in exception objects.
Other features:
One core tenet of building a good REST service, is that URIs should be
discovered, not hardcoded in an application. It's for this reason that the
emphasis in this library is not on URIs (like most libraries) but on
relation-types (the rel
) and links.
Generally when interacting with a REST service, you'll want to only hardcode a single URI (a bookmark) and discover all the other APIs from there on.
For example, consider that there is a some API at https://api.example.org/
.
This API has a link to an API for news articles (rel="articleCollection"
),
which has a link for creating a new article (rel="new"
). When POST
ing on
that URI, the API returns 201 Created
along with a Location
header pointing
to the new article. On this location, a new rel="author"
appears
automatically, pointing to the person that created the article.
This is how that interaction might look like:
const ketting = new Ketting('https://api.example.org/');
const createArticle = await ketting.follow('articleCollection').follow('new'); // chained follow
const newArticle = await createArticle.post({ title: 'Hello world' });
const author = await newArticle.follow('author');
// Output author information
console.log(await author.get());
More details can be found in the Getting started docs and Following Links.
Embedded resources are a HAL feature. In situations when you are modeling a
'collection' of resources, in HAL you should generally just create links to
all the items in the collection. However, if a client wants to fetch all these
items, this can result in a lot of HTTP requests. HAL uses _embedded
to work
around this. Using _embedded
a user can effectively tell the HAL client about
the links in the collection and immediately send along the contents of those
resources, thus avoiding the overhead.
Ketting understands _embedded
and completely abstracts them away. If you use
Ketting with a HAL server, you can therefore completely ignore them.
For example, given a collection resource with many resources that hal the
relationshiptype item
, you might use the following API:
const ketting = new Ketting('https://api.example.org/');
const articleCollection = await ketting.follow('articleCollection');
const items = await someCollection.followAll('item');
for (const item of items) {
console.log(await item.get());
}
Given the last example, if the server did not use embedding, it will result in a HTTP GET request for every item in the collection.
If the server did use embedding, there will only be 1 GET request.
A major advantage of this, is that it allows a server to be upgradable. Hot paths might be optimized using embedding, and the client seamlessly adjusts to the new information.
Further reading:
If your server emits application/problem+json documents (RFC7807) on HTTP errors, the library will automatically extract the information from that object, and also provide a better exception message (if the title property is provided).