Introducing Heracles - Hydra Core Hypermedia Client
Lately I’ve been working on a library to consume Hydra Core hypermedia-rich APIs. This is something I’ve been planning for a long time now and given that the Argolis server-side component pretty much works it was about time I started working on consuming the API Documentation.
In this post I showcase the simplest usage of heracles and describe some design decisions. I guess I should write about Argolis too in the near future.
The source code of heracles is naturally on GitHub. It is written in TypeScript and bundled as an AMD format package.
Getting started
Installation
To start using heracles first download it using JSPM package manager.
1
|
|
Basic usage
Now you are ready to start using the library. It is as simple as importing and executing the static load
function. It
returns a promise of a resource.
1 2 3 4 5 6 |
|
The returned model will always be expanded although in the future I could consider adding an optional @context
parameter.
Every time a resource is loaded the Link
ed Hydra API Documentation will be fetched as well to discover possible operations
for the resource(s). Here’s an example of a documentation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
|
The above states a number of facts about the API:
The server is known to return resources of the type
vocab:Person
.
A
GET
request is known to be supported for resources of typesvocab:Person
The
vocab:Person
class can be expected to include avocab:pets
link to another resource
That other, linked resource can be requested using
POST
with an instance of classvocab:Pet
A valid instance of
vocab:Pet
must include thefoaf:name
property
All this information can be accessed from resources loaded using the Hydra.Resource.load
method above. Given a representation
of the resource http://my.api/Tomasz
1 2 3 4 5 6 7 8 9 |
|
It is possible to discover operations available for any of the instances
1 2 3 4 5 6 7 8 9 10 11 |
|
Important bits and pieces
There are some decisions I made, which may influence how the server and client must act. Most notably
Resources are expanded
First of all, as I’ve stated above, the loaded resource representation is expanded by default. This is because otherwise
it would be quite difficult to process them. This is true for example for inspecting the resource @type
.
load
returns object with matching @id
If a resource representation is a larger graph of objects, the load
function will always look for that identifier and
return that object even if it was not the root of the JSON-LD document. For example, the current design of collections in
Hydra is that each collection can be partitioned into views (for example for the purpose of paging). Requesting a resource
http://my.api/Tomasz/pets?page=2
could return something similar to:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
As you see the requested resource is not the root of the representation tree. Still the load
promise will resolve with
that object and not http://my.api/Tomasz/pets
. This may be counterintuitive in the case of simple JSON-LD documents but
considering that the server could be returning expanded or flattened documents it seems the only logical
way. Not to mention that other RDF media type could be requested by the client in which case, there would no obvious root
object.
Each common case from Hydra Core vocabulary like the PartialCollectionView
(possible any object of the hydra:view
property) will be enriched with a link to the parent collection. Otherwise it wouldn’t be possible to access it from the
returned object.
Hydra documentation objects are compacted
For convenience elements of the Hydra Core vocabulary are compacted with the default hydra @context
so that on can write
op.method
instead of op['https://www.w3.org/ns/hydra/core#method']
. If the object contained any non-standard content,
such as SHACL
constraints for a supported property, it is possible to recompact with a custom context:
1 2 3 |
|
Going further
A rich interaction with the loaded resource isn’t possible just yet. As you see above currently only the basic metadata about operations is available. I’ve also started work on accessing supported properties. In the future I plan a number of facilities to ease invoking operations, handling common Hydra objects in specific ways, easier extensions, improved error handling, etc.