{"id":21857908,"url":"https://github.com/mathematic-inc/ts-japi","last_synced_at":"2025-10-03T23:28:47.185Z","repository":{"id":40792858,"uuid":"265634322","full_name":"mathematic-inc/ts-japi","owner":"mathematic-inc","description":"A highly-modular (typescript-friendly)-framework agnostic library for serializing data to the JSON:API specification","archived":false,"fork":false,"pushed_at":"2025-01-14T17:27:03.000Z","size":3742,"stargazers_count":213,"open_issues_count":6,"forks_count":17,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-14T19:57:18.389Z","etag":null,"topics":["json","json-api","rest","serialization","serializer"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mathematic-inc.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"docs/contributing.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"jrandolf"}},"created_at":"2020-05-20T17:09:56.000Z","updated_at":"2025-03-27T05:55:10.000Z","dependencies_parsed_at":"2024-03-21T10:06:02.221Z","dependency_job_id":"c009bd82-9f08-4399-b3b1-4b619cf93650","html_url":"https://github.com/mathematic-inc/ts-japi","commit_stats":{"total_commits":49,"total_committers":11,"mean_commits":4.454545454545454,"dds":0.6734693877551021,"last_synced_commit":"04160f08883d01f42fe7197049b7b0bcb5fbdf40"},"previous_names":["mu-io/ts-japi"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathematic-inc%2Fts-japi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathematic-inc%2Fts-japi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathematic-inc%2Fts-japi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathematic-inc%2Fts-japi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mathematic-inc","download_url":"https://codeload.github.com/mathematic-inc/ts-japi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254328384,"owners_count":22052632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["json","json-api","rest","serialization","serializer"],"created_at":"2024-11-28T02:33:17.765Z","updated_at":"2025-10-03T23:28:42.153Z","avatar_url":"https://github.com/mathematic-inc.png","language":"TypeScript","funding_links":["https://github.com/sponsors/jrandolf"],"categories":[],"sub_categories":[],"readme":"# ts:japi\n\n\u003cbr/\u003e\n\u003cimg src=\"https://raw.githubusercontent.com/mathematic-inc/ts-japi/main/src/docs/assets/images/logo.svg\" alt=\"{ts:japi}\" width=\"350\"/\u003e\n\u003cbr/\u003e\n\n![node-current](https://img.shields.io/node/v/ts-japi)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n\n\u003e A highly-modular (typescript-friendly)-framework agnostic library for serializing data to the\n\u003e JSON:API specification\n\n- [ts:japi](#tsjapi)\n  - [Features](#features)\n  - [Documentation](#documentation)\n  - [Installation](#installation)\n  - [Getting Started](#getting-started)\n    - [Examples](#examples)\n  - [Serialization](#serialization)\n    - [Links](#links)\n      - [Pagination](#pagination)\n    - [Relationships](#relationships)\n    - [Metadata](#metadata)\n    - [Serializing Errors](#serializing-errors)\n    - [Caching](#caching)\n  - [Deserialization](#deserialization)\n  - [Remarks](#remarks)\n  - [FAQ](#faq)\n  - [For Developers](#for-developers)\n  - [Contributing](#contributing)\n  - [License](#license)\n\n## Features\n\n- This is the **only** typescript-compatible library that fully types the JSON:API specification and\n  performs _proper_ serialization.\n- [**Zero dependencies**](#zdg).\n- This is the **only** library with [resource recursion](#wirr).\n- The modular framework laid out here _highly promotes_ the specifications intentions:\n  - Using links is no longer obfuscated.\n  - Meta can truly be placed anywhere with possible dependencies laid out visibly.\n- This library is designed to adhere to the specifications \"never remove, only add\" policy, so we\n  will remain backwards-compatible.\n\n## Documentation\n\nThe [documentation](https://mathematic-inc.github.io/ts-japi/) has everything that is covered here\nand more.\n\n## Installation\n\nYou can install ts-japi in your project's directory as usual:\n\n```bash\nnpm install ts-japi\n```\n\n## Getting Started\n\nThere are fives classes that are used to serialize data (only one of which is necessarily required).\n\n- [`Serializer`](https://mathematic-inc.github.io/ts-japi/classes/serializer.html) with\n  [`SerializerOptions`](https://mathematic-inc.github.io/ts-japi/interfaces/serializeroptions.html)\n- [`Relator`](https://mathematic-inc.github.io/ts-japi/classes/relator.html) with\n  [`RelatorOptions`](https://mathematic-inc.github.io/ts-japi/interfaces/relatoroptions.html)\n- [`Linker`](https://mathematic-inc.github.io/ts-japi/classes/linker.html) with\n  [`LinkerOptions`](https://mathematic-inc.github.io/ts-japi/interfaces/linkeroptions.html)\n- [`Metaizer`](https://mathematic-inc.github.io/ts-japi/classes/metaizer.html)\n- [`Paginator`](https://mathematic-inc.github.io/ts-japi/classes/paginator.html)\n- [`ErrorSerializer`](https://mathematic-inc.github.io/ts-japi/classes/errorserializer.html) with\n  [`ErrorSerializerOptions`](https://mathematic-inc.github.io/ts-japi/interfaces/errorserializeroptions.html)\n- [`Cache`](https://mathematic-inc.github.io/ts-japi/classes/cache.html) with\n  [`CacheOptions`](https://mathematic-inc.github.io/ts-japi/interfaces/cacheoptions.html)\n\nYou can check the [documentation](https://mathematic-inc.github.io/ts-japi) for a deeper insight\ninto the usage.\n\n### Examples\n\nYou can check the [examples](https://github.com/mathematic-inc/ts-japi/tree/master/examples) and the\n[test](https://github.com/mathematic-inc/ts-japi/tree/master/test) folders to see some examples\n(such as the ones below). You can check\n[this example](https://github.com/mathematic-inc/ts-japi/blob/master/examples/full.example.ts) to\nsee almost every option of\n[`Serializer`](https://mathematic-inc.github.io/ts-japi/classes/serializer.html) exhausted.\n\n## Serialization\n\nThe [`Serializer`](https://mathematic-inc.github.io/ts-japi/classes/serializer.html) class is the\nonly class required for basic serialization.\n\nThe following example constructs the most basic\n[`Serializer`](https://mathematic-inc.github.io/ts-japi/classes/serializer.html): (Note the `await`)\n\n```typescript\nimport { Serializer } from '../src';\nimport { User } from '../test/models';\nimport { getJSON } from '../test/utils/get-json';\n\nconst UserSerializer = new Serializer('users');\n\n(async () =\u003e {\n  const user = new User('sample_user_id');\n\n  console.log('Output:', getJSON(await UserSerializer.serialize(user)));\n\n  // Output: {\n  //  jsonapi: { version: '1.0' },\n  //  data: {\n  //   type: 'users',\n  //   id: 'sample_user_id',\n  //   attributes: {\n  //     createdAt: '2020-05-20T15:44:37.650Z',\n  //     articles: [],\n  //     comments: []\n  //   }\n  //  }\n  // }\n})();\n```\n\n### Links\n\nThe [`Linker`](https://mathematic-inc.github.io/ts-japi/classes/linker.html) class is used to\ngenerate a normalized [document link](https://jsonapi.org/format/#document-links). Its methods are\nnot meant to be called. See the [FAQ](#faq) for reasons.\n\nThe following example constructs a\n[`Linker`](https://mathematic-inc.github.io/ts-japi/classes/linker.html) for `User`s and `Article`s:\n\n```typescript\nimport { Linker } from '../src';\nimport { User, Article } from '../test/models';\nimport { getJSON } from '../test/utils/get-json';\n\n// The last argument should almost always be an array or a single object type.\n// The reason for this is the potential for linking several articles.\nconst UserArticleLinker = new Linker((user: User, articles: Article | Article[]) =\u003e {\n  return Array.isArray(articles)\n    ? `https://www.example.com/users/${user.id}/articles/`\n    : `https://www.example.com/users/${user.id}/articles/${articles.id}`;\n});\n\n// ! The rest of this example is just to illustrate internal behavior.\n(async () =\u003e {\n  const user = new User('sample_user_id');\n  const article = new Article('same_article_id', user);\n\n  console.log('Output:', getJSON(UserArticleLinker.link(user, article)));\n\n  // Output: https://www.example.com/users/sample_user_id/articles/same_article_id\n})();\n```\n\n#### Pagination\n\nThe [`Paginator`](https://mathematic-inc.github.io/ts-japi/classes/paginator.html) class is used to\ngenerate [pagination links](https://jsonapi.org/format/#fetching-pagination). Its methods are not\nmeant to be called.\n\nThe following example constructs a\n[`Paginator`](https://mathematic-inc.github.io/ts-japi/classes/paginator.html):\n\n```typescript\nimport { Paginator } from '../src';\nimport { User, Article } from '../test/models';\nimport { getJSON } from '../test/utils/get-json';\n\nconst ArticlePaginator = new Paginator((articles: Article | Article[]) =\u003e {\n  if (Array.isArray(articles)) {\n    const nextPage = Number(articles[0].id) + 1;\n    const prevPage = Number(articles[articles.length - 1].id) - 1;\n    return {\n      first: `https://www.example.com/articles/0`,\n      last: `https://www.example.com/articles/10`,\n      next: nextPage \u003c= 10 ? `https://www.example.com/articles/${nextPage}` : null,\n      prev: prevPage \u003e= 0 ? `https://www.example.com/articles/${prevPage}` : null,\n    };\n  }\n  return;\n});\n\n// ! The rest of this example is just to illustrate internal behavior.\n(async () =\u003e {\n  const user = new User('sample_user_id');\n  const article = new Article('same_article_id', user);\n\n  console.log('Output:', getJSON(ArticlePaginator.paginate([article])));\n\n  // Output: {\n  //  first: 'https://www.example.com/articles/0',\n  //  last: 'https://www.example.com/articles/10',\n  //  prev: null,\n  //  next: null\n  // }\n})();\n```\n\n### Relationships\n\nThe [`Relator`](https://mathematic-inc.github.io/ts-japi/classes/relator.html) class is used to\ngenerate top-level [included data](https://jsonapi.org/format/#document-top-level) as well as\nresource-level [relationships](https://jsonapi.org/format/#document-resource-object-relationships).\nIts methods are not meant to be called.\n\n[`Relator`](https://mathematic-inc.github.io/ts-japi/classes/relator.html)s may also take optional\n[`Linker`](https://mathematic-inc.github.io/ts-japi/classes/linker.html)s (using the\n[`linker`](https://mathematic-inc.github.io/ts-japi/interfaces/relatoroptions.html#linkers) option)\nto define [relationship links](https://jsonapi.org/format/#document-resource-object-relationships)\nand\n[related resource links](https://jsonapi.org/format/#document-resource-object-related-resource-links).\n\nThe following example constructs a\n[`Relator`](https://mathematic-inc.github.io/ts-japi/classes/relator.html) for `User`s and\n`Article`s:\n\n```typescript\nimport { Serializer, Relator } from '../src';\nimport { User, Article } from '../test/models';\nimport { getJSON } from '../test/utils/get-json';\n\nconst ArticleSerializer = new Serializer\u003cArticle\u003e('articles');\nconst UserArticleRelator = new Relator\u003cUser, Article\u003e(\n  async (user) =\u003e user.getArticles(),\n  ArticleSerializer\n);\n\n// ! The rest of this example is just to illustrate some internal behavior.\n(async () =\u003e {\n  const user = new User('sample_user_id');\n  const article = new Article('same_article_id', user);\n  User.save(user);\n  Article.save(article);\n\n  console.log('Output:', getJSON(await UserArticleRelator.getRelationship(user)));\n\n  // Output: { data: [ { type: 'articles', id: 'same_article_id' } ] }\n})();\n```\n\n### Metadata\n\nThe [`Metaizer`](https://mathematic-inc.github.io/ts-japi/classes/metaizer.html) class is used to\nconstruct generate metadata given some dependencies. There are several locations\n[`Metaizer`](https://mathematic-inc.github.io/ts-japi/classes/metaizer.html) can be used:\n\n- [`ErrorSerializerOptions.metaizers`](https://mathematic-inc.github.io/ts-japi/interfaces/errorserializeroptions.html#metaizers)\n- [`RelatorOptions.metaizer`](https://mathematic-inc.github.io/ts-japi/interfaces/relatoroptions.html#metaizer)\n- [`SerializerOptions.metaizers`](https://mathematic-inc.github.io/ts-japi/interfaces/serializeroptions.html#metaizers)\n- [`LinkerOptions.metaizer`](https://mathematic-inc.github.io/ts-japi/interfaces/linkeroptions.html#metaizer)\n\nLike [`Linker`](https://mathematic-inc.github.io/ts-japi/classes/linker.html), its methods are not\nmeant to be called.\n\nThe following example constructs a\n[`Metaizer`](https://mathematic-inc.github.io/ts-japi/classes/metaizer.html):\n\n```typescript\nimport { User, Article } from '../test/models';\nimport { Metaizer } from '../src';\nimport { getJSON } from '../test/utils/get-json';\n\n// The last argument should almost always be an array or a single object type.\n// The reason for this is the potential for metaizing several articles.\nconst UserArticleMetaizer = new Metaizer((user: User, articles: Article | Article[]) =\u003e {\n  return Array.isArray(articles)\n    ? { user_created: user.createdAt, article_created: articles.map((a) =\u003e a.createdAt) }\n    : { user_created: user.createdAt, article_created: articles.createdAt };\n});\n\n// ! The rest of this example is just to illustrate internal behavior.\n(async () =\u003e {\n  const user = new User('sample_user_id');\n  const article = new Article('same_article_id', user);\n\n  console.log('Output:', getJSON(UserArticleMetaizer.metaize(user, article)));\n\n  // Output: {\n  //  user_created: '2020-05-20T15:39:43.277Z',\n  //  article_created: '2020-05-20T15:39:43.277Z'\n  // }\n})();\n```\n\n### Serializing Errors\n\nThe [`ErrorSerializer`](https://mathematic-inc.github.io/ts-japi/classes/errorserializer.html) class\nis used to serialize any object considered an error (the\n[`attributes`](https://mathematic-inc.github.io/ts-japi/interfaces/errorserializeroptions.html#attributes)\noption allows you to choose what attributes to use during serialization). _Alternatively_\n(**recommended**), you can construct custom errors by extending the\n[`JapiError`](https://mathematic-inc.github.io/ts-japi/classes/japierror.html) class and use those\nfor all server-to-client errors.\n\nThe\n[error serializer test](https://github.com/mathematic-inc/ts-japi/tree/master/test/error-serializer.test.ts)\nincludes an example of the alternative solution.\n\nThe following example constructs the most basic\n[`ErrorSerializer`](https://mathematic-inc.github.io/ts-japi/classes/errorserializer.html): (Note\nthe lack of `await`)\n\n```typescript\nimport { ErrorSerializer } from '../src';\nimport { getJSON } from '../test/utils/get-json';\n\nconst PrimitiveErrorSerializer = new ErrorSerializer();\n\n(async () =\u003e {\n  const error = new Error('badness');\n\n  console.log('Output:', getJSON(PrimitiveErrorSerializer.serialize(error)));\n\n  // Output: {\n  //  errors: [ { code: 'Error', detail: 'badness' } ],\n  //  jsonapi: { version: '1.0' }\n  // }\n})();\n```\n\n### Caching\n\nThe [`Cache`](https://mathematic-inc.github.io/ts-japi/classes/cache.html) class can be placed in a\n[`Serializer`](https://mathematic-inc.github.io/ts-japi/classes/serializer.html)'s\n[`cache`](https://mathematic-inc.github.io/ts-japi/interfaces/serializeroptions.html#cache) option.\nAlternatively, setting that option to `true` will provide a default\n[`Cache`](https://mathematic-inc.github.io/ts-japi/classes/cache.html).\n\nThe default [`Cache`](https://mathematic-inc.github.io/ts-japi/classes/cache.html) uses the basic\n[`Object.is`](https://mathematic-inc.github.io/ts-japi/https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)\nfunction to determine if input data are the same. If you want to adjust this, instantiate a new\n[`Cache`](https://mathematic-inc.github.io/ts-japi/classes/cache.html) with a\n[`resolver`](https://mathematic-inc.github.io/ts-japi/interfaces/cacheoptions.html#resolver).\n\n## Deserialization\n\nWe stress the following: Given that there are many clients readily built to consume JSON:API\nendpoints (see [here](https://jsonapi.org/implementations/)), we do not provide deserialization. In\nparticular, since unmarshalling data is strongly related to the code it will be used in (e.g.\nReact), tighter integration is recommended over an unnecessary abstraction.\n\n## Remarks\n\nThere are several model classes used inside TS:JAPI such as `Resource` and `Relationships`. These\nmodels are used for normalization as well as traversing a JSON:API document. If you plan to fork\nthis repo, you can extend these models and reimplement them to create your own custom (non-standard,\nextended) serializer.\n\n## FAQ\n\n\u003e Why not just allow optional functions that return the internal `Link` Class (or just a URI\n\u003e `string`)?\n\nThe `Link` class is defined to be as general as possible in case of changes in the specification. In\nparticular, the implementation of metadata and the types in our library rely on the generality of\nthe `Link` class. Relying on user arguments will generate a lot of overhead for both us and users\nwhenever the specs change.\n\n\u003e Why does the `Meta` class exist if it is essentially just a plain object?\n\nIn case the specification is updated to change the meta objects in some functional way.\n\n\u003e What is \"resource recursion\"?\u003ca id=\"wirr\"\u003e\u003c/a\u003e\n\nDue to [compound documents](https://jsonapi.org/format/#document-compound-documents), it is possible\nto recurse through related resources via their\n[resource linkages](https://jsonapi.org/format/#document-resource-object-linkage) and obtain\n[included resources](https://jsonapi.org/format/#document-top-level) beyond primary data relations.\nThis is should be done with caution (see\n[`SerializerOptions.depth`](https://mathematic-inc.github.io/ts-japi/interfaces/serializeroptions.html#depth)\nand\n[this example](https://github.com/mathematic-inc/ts-japi/blob/master/examples/resource-recursion.example.ts))\n\n## For Developers\n\nTo get started in developing this library, run `yarn install`, `yarn build` and `yarn test` (in this\nprecise order) to assure everything is in working order.\n\n## Contributing\n\nThis project is maintained by the author, however contributions are welcome and appreciated. You can\nfind TS:JAPI on GitHub:\n[https://github.com/mathematic-inc/ts-japi](https://github.com/mathematic-inc/ts-japi)\n\nFeel free to submit an issue, but please do not submit pull requests unless it is to fix some issue.\nFor more information, read the\n[contribution guide](https://github.com/mathematic-inc/ts-japi/blob/master/CONTRIBUTING.html).\n\n## License\n\nCopyright © 2020 [mathematic-inc](https://github.com/mathematic-inc).\n\nLicensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathematic-inc%2Fts-japi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathematic-inc%2Fts-japi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathematic-inc%2Fts-japi/lists"}