yarn add @mediamonks/jsonapi-client
npm i -S @mediamonks/jsonapi-client
import JSONAPI, { Attribute, Relationship } from '@mediamonks/jsonapi-client'
import { isString } from 'isntnt'
// set up a Resource, passing the `type` and the endpoint path
export class Post extends JSONAPI.resource('Post', 'posts')<Post> {
@Attribute.required(isString) public title!: string
@Attribute.optional(isString) public content!: string | null
@Relationship.toOne(() => Author) public author!: Author | null
@Relationship.toMany(() => Comment) public comments!: Comment[]
}
// create a client
const url = new URL('http://www.example.com/api')
const client = JSONAPI.client(url)
// create an endpoint
const endpoint = client.endpoint(Post)
// get single item
endpoint.getOne(id, {
fields: { [Post.type]: ['title, author']},
includes: { author: null },
}).then(result => console.log('one', result))
// get collection
endpoint.getMany({
offset: 10,
limit: 3,
sort: ['title', '-author'],
filter: { 'author.id': authorId }
}, {
fields: { [Post.type]: ['title, author']},
includes: { author: null },
}).then(results => console.log('many', results))
// create a new resource
endpoint.create({
title: 'foo',
content: 'bar'
}).then(result => console.log('new', result))
// patch an existing resource
endpoint.patch(id, {
title: 'foo',
content: 'bar'
})
// delete an existing resource
endpoint.delete(id)
Below is a quick reference on the types you'll encounter or use on a daily basis.
Resource
-
ResourceId
- astring
, alias for the resource id -
ResourceType
- astring
, alias for the resource type -
ResourceIdentifier<ResourceType>
- type for a basic resource with id/type fields -
AnyResource
- an alias for the above, to represent any resource -
ResourceConstructor<AnyResource>
- type for a resource constructor/class -
ResourceCreateValues<AnyResource>
- to type data objects to create a specific resource -
ResourcePatchValues<AnyResource>
- to type data objects to patch a specific resource -
ResourceParameters<AnyResource>
- a type that represents a resource filter with fields and includes
Endpoint
-
Endpoint<AnyResource, ClientSetup>
- to type an Endpoint, potentially for a specific resource -
EndpointResource<Endpoint<any, any>>
- a helper type to extract a Resource from an Endpoint -
FilteredResource<AnyResource, ResourceParameters<AnyResource>>
- to type a resource that only contains specific fields (both attributes and nested relationships). -
ClientSearchParameters<ClientSetup>
- to type the collection query parameters for pagination, sorting, filtering, etc
Basic example for fetching filtered resources.
// create a filter
const postFilter = {
// It's good practise to specify which fields to return for all resources in the response.
// It's an object with the resource type as key, and an array with the fields as values.
// The field array should contain both attributes and relationships.
// A relationship specified here would only return the ResourceIdentifier (id/type), unless
// you also include the relationship below .
fields: {
[Post.type]: ['name', 'content', 'comments', 'author'],
[Comment.type]: ['title', 'author'],
[Author.type]: ['name', 'homepage'],
},
// Specify which relationship fields from the above defintion to include as additional resources.
// The nesting here should follow the one from the Resource field specification.
// Setting `null` means that resource won't include any others, while an object can be used
// to specify includes for that nested relationship, and can go many levels deep.
// As can be seen, author should be included on both the Post and the Comment.
// The includes object starts with the fields of the Resource to filter, which should
// also be placed as first Resource in the field list by convention
include: {
comments: {
author: null,
},
author: null,
},
} as const
// note the `as const` - that's needed for type checking
// this type contains only the (nested) field specified in the filter above
type FilteredPost = FilteredResource<Post, postFilter>
let items: Array<FilteredPost>
// the filter itself can be passed to the getOne/getMany methods
endpoint.getMany({}, postFilter).then(result => {
items = result
})
Basic example on how to specify function parameters
const postFilter = {
fields: {
[Post.type]: ['title', 'author'],
[Author.type]: ['name'],
},
include: { author: null },
} as const
function renderPost(post: FilteredResource<Post, postFilter>) {
// in here, only a few specific fields of `post` are used
// so it's good practise to use a filter instead of requesting the full Post type
}
// when not needing nested resources, a `Pick` can be used as well
function renderPost(post: Pick<Post, 'name' | 'author'>) {
// in here, only a few specific fields of `post` are used
// so it's good practise to use a filter instead of requesting the full Post type
}