Skip to main content

JSON:API

What you'll learn
  • 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 };
},
});

Reference​