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.
To start using heracles first download it using JSPM package manager.
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
Every time a resource is loaded the
Linked 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
GETrequest is known to be supported for resources of types
vocab:Personclass can be expected to include a
vocab:petslink to another resource
That other, linked resource can be requested using
POSTwith an instance of class
A valid instance of
vocab:Petmust include the
All this information can be accessed from resources loaded using the
Hydra.Resource.load method above. Given a representation
of the resource
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
load returns object with matching
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
Each common case from Hydra Core vocabulary like the
PartialCollectionView (possible any object of the
property) will be enriched with a link to the parent collection. Otherwise it wouldn’t be possible to access it from the
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,
SHACL constraints for a supported property, it is possible to recompact with a custom context:
1 2 3
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.