A JSON:API client library written in Typescript with emphasis on RESTful traversal of resources according to HATEOAS principles.
- Transparent access to included resources (in compound documents) as well as linked resources (fetched from a server)
Promise
-based access to resources allowingasync
/await
style programming- Adaptable to various HTTP client implementations
- Focus on traversing JSON:API resources without knowing specific URLs, as recommended by REST/HATEOAS
- Support for sparse fieldsets
- Uses memoization to avoid repeated network requests and repeated traversals of the document structure
- No dependencies (apart from
jest
for testing) - Implemented against the JSON:API 1.0 specification.
Grivet does not aim to be an ORM. It does not provide methods to manage resources on a server (e.g. deleting or updating resources).
npm i @muellerbbm-vas/grivet
To give an idea of what using Grivet looks like, the code snippet below shows how to traverse from an API entry point to the author of a specific article:
import { JsonApi } from '@muellerbbm-vas/grivet';
const apiEntryPointDoc = await JsonApi.Document.fromURL(new URL('http://example.com/api/'), context);
const articles = await apiEntryPoint.resource.relatedResources['articles'];
const author = await articles[0].relatedResource['author'];
const name = author.attributes['name'];
In the first line, a JSON:API document is constructed from a given URL and a Context
object
(see the documentation on contexts for more details). The Promise
returned by the fromURL
function is awaited to obtain the Document
(corresponding to the JSON:API top level document). The raw JSON:API document fetched from the server might look something like this:
GET http://example.com/api HTTP/1.1
Accept: application/vnd.api+json
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"type": "entrypoints",
"id": "1",
"relationships": {
"articles": {
"links": {
"related": "http://example.com/articles"
}
},
"people": {
"links": {
"related": "http://example.com/people"
}
}
}
}
}
The entry point document contains a primary resource with a relationship named "articles" pointing to the articles resources.
The second line of our short example gets the primary entry point resource and then directly awaits the relatedResources
property to fetch the
articles resources as an array of Resource
objects (corresponding to JSON:API resource objects).
As recommended by HATEOAS principles, we do not need to know the URL of the articles resource in advance, we just follow the provided relationship.
Let's assume the server responds with the following compound JSON:API document:
GET http://example.com/articles HTTP/1.1
Accept: application/vnd.api+json
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"type": "articles",
"id": "1",
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
}
}
],
"included": [
{
"type": "people",
"id": "9",
"attributes": {
"name": "example name"
}
}
]
}
The article resource contains a relationship to its author, which in this case is included in the document.
The third line of our small example uses the relatedResource
property to obtain the author resource linked in the first article.
We do not have to know whether the relationship links to an included resource or whether it needs to be fetched from a server.
We just await
the relatedResource
property.
The attributes of the author resource can then simply be obtained using its attributes
property.
- Implementing the
Context
interface - Using Sparse Fieldsets
- Experimental: constructing a document for POSTing to a server
The relationships
property of a Resource
contains all relationships as a map from relationship name to Relationship
object. This map can be iterated to find all relationships:
for (const relationshipName in articleResource.relationships) {
const relationship = articleResource.relationships[relationshipName];
// … do something with relationship
}
See test/tests.spec.ts for more examples of how to use this library.
Have a look at the library reference for more details.
Copyright © 2019-2022 Müller-BBM VibroAkustik Systeme GmbH
Licensed under the MIT license