JSON:API
- Using Foscia to interact with a JSON:API
Setup
Please follow the getting started guide to set up your JSON:API action factory.
Usage
Eager loading relations
Foscia supports
eager loading relations through include.
Internally, it will use the JSON:API relationships inclusion features.
import { query, all, include } from '@foscia/core';
const posts = await action().run(query(Post), include('author'), all());
Filtering requests
You can filter your request using the
filterBy enhancer. This function
both supports key-value or object parameters.
import { query, all } from '@foscia/core';
import { filterBy } from '@foscia/jsonapi';
const posts = await action().run(
query(Post),
// Key-value pair.
filterBy('published', true),
// Object.
filterBy({ published: true }),
all(),
);
Sorting results
You can sort results using multiple enhancers:
sortBy,
sortByAsc and
sortByDesc. sortBy supports
both object and arrays parameters and will apply ascending sorting by default.
sortByAsc and sortByDesc supports variadic keys parameter.
import { query, all } from '@foscia/core';
import { sortBy, sortByAsc } from '@foscia/jsonapi';
const posts = await action().run(
query(Post),
// Ascending sorting.
sortBy(['publishedAt', 'createdAt']),
// Custom sorting.
sortBy(['publishedAt', 'createdAt'], ['desc', 'asc']),
sortBy({ publishedAt: 'desc', createdAt: 'asc' }),
// Variadic keys.
sortByAsc('publishedAt', 'createdAt'),
all(),
);
Sparse fieldsets
You can filter the record fields you will retrieve using
fields and
fieldsFor enhancers. Those are
strongly typed to your model's properties and will automatically query for
properties' aliases if they are set.
fields applies for the currently targeted model, whereas fieldsFor applies
for the given model.
import { query, all, include } from '@foscia/core';
import { sortBy, sortByDesc } from '@foscia/jsonapi';
const posts = await action().run(
query(Post),
include('author'),
fields('title', 'author'),
fieldsFor(User, 'username'),
all(),
);
Paginating results
Pagination is agnostic when using JSON:API. That's why Foscia propose a
paginate enhancer in which you
can pass any object value. This provides support of all pagination style.
You can combine this with the
usingDocument utility
function to retrieve the whole JSON:API document (for example when your server
serialize pagination metadata in it).
import { query, all } from '@foscia/core';
import { paginate, usingDocument } from '@foscia/jsonapi';
const data = await action().run(
query(Post),
// A standard pagination.
paginate({ size: 10, number: 1 }),
// A cursor pagination.
paginate({ size: 10, after: 1 }),
all(usingDocument),
);
// The `all` result.
console.log(data.instances);
// The JSON:API document.
console.log(data.document);
// Some pagination metadata your server gives you.
console.log(data.document.meta!.page.hasMore);
Configuration recipes
Here are common configuration for @foscia/jsonapi implementation. You can read
the implementation and configuration guide
for more details.
You can also take a look at HTTP usage and common configuration recipes, as the JSON:API adapter is based on HTTP adapter.
Changing endpoint case
By default, JSON:API use kebab case for models and relations endpoints (e.g.
favorite-posts for a favoritePosts relation). If you want to use another
case for endpoints, you can use modelPathTransformer and
relationPathTransformer options.
import { camelCase } from 'lodash-es';
import { makeJsonApiAdapter } from '@foscia/rest';
makeJsonApiAdapter({
modelPathTransformer: (path) => camelCase(path),
relationPathTransformer: (path) => camelCase(path),
});
Changing serialization keys case
By default, serialized and deserialized attributes and relations keep keys
specified in the model. If you are using camel cased keys (e.g. firstName)
but want to exchange kebab cased keys (e.g. first-name) with your API,
you can use serializeKey and deserializeKey options.
import { kebabCase } from 'lodash-es';
import { makeJsonApiSerializer, makeJsonApiDeserializer } from '@foscia/rest';
makeJsonApiSerializer({
serializeKey: ({ key }) => kebabCase(key),
});
makeJsonApiDeserializer({
deserializeKey: ({ key }) => kebabCase(key),
});
Parsing URL IDs
Some API implementation may serialize records IDs as URL to the record endpoint
(such as https://example.com/api/posts/1 for post 1). You can customize
the deserializer to support ID and type extraction from URL ID using the
pullIdentifier option.
import { makeJsonApiDeserializer } from '@foscia/rest';
makeJsonApiDeserializer({
pullIdentifier: (record) => {
// This will support IDs like `https://example.com/api/posts/1`, `/api/posts/1`, etc.
const [id, type] = String(record.id).split('/').reverse();
return { id, type };
},
});