Getting Started

jsonapi-nano is a lightweight, framework-agnostic JSON:API serializer for TypeScript.

It transforms your application data into consistent JSON:API resource and error documents without imposing runtime dependencies, middleware, decorators, or framework-specific abstractions.


Installation

npm install @emelon/jsonapi-nano

Defining a Resource

Resources describe how your data should be presented.

import { createResource, belongsTo } from "@emelon/jsonapi-nano";

interface Article {
  id: string;
  title: string;
  body: string;
  authorId: string;
}

interface Author {
  id: string;
  name: string;
}

export const authorResource = createResource<Author>("authors", {
  attributes: (author) => ({ name: author.name }),
});

export const articleResource = createResource<Article>("articles", {
  attributes: (article) => ({
    title: article.title,
    body: article.body,
  }),
  relationships: (article) => ({
    author: belongsTo("authors", article.authorId),
  }),
});

Compound Documents & Including Relations

Pass related raw data records into your serialize execution configurations using the include map parameter. The serialization engine will match relation keys, build full JSON:API relationship definitions on your primary data records, and safely deduplicate elements inside your root included collection block automatically.

import { serialize } from "@emelon/jsonapi-nano";

const mockArticles = [
  {
    id: "1",
    title: "Say Hello",
    body: "A presentation engine.",
    authorId: "99",
  },
];
const mockAuthors = [{ id: "99", name: "Naruto Uzumaki" }];

const response = serialize(mockArticles, articleResource, {
  include: {
    author: [mockAuthors, authorResource],
  },
});

Output Target Shape:

{
  "data": [
    {
      "type": "articles",
      "id": "1",
      "attributes": {
        "title": "Say Hello",
        "body": "A presentation engine."
      },
      "relationships": {
        "author": {
          "data": { "type": "authors", "id": "99" }
        }
      }
    }
  ],
  "included": [
    {
      "type": "authors",
      "id": "99",
      "attributes": {
        "name": "Naruto Uzumaki"
      }
    }
  ],
  "meta": {
    "timestamp": "2026-06-13T18:20:33.243Z"
  }
}

Sparse Fieldsets

To let clients request a lightweight network payload containing only explicit fields, use the built-in fieldsFromQuery parameter parser from the isolated /query subpath.

import { serialize } from "@emelon/jsonapi-nano";
import { fieldsFromQuery } from "@emelon/jsonapi-nano/query";

// Automatically parses standard incoming HTTP formats (e.g. ?fields[articles]=title)
// into valid serialization maps: { articles: ["title"] }
const fields = fieldsFromQuery(req.query);

const response = serialize(mockArticles, articleResource, { fields });

Next Steps