{"id":20668638,"url":"https://github.com/tsmetadata/json-api","last_synced_at":"2026-02-06T16:07:28.966Z","repository":{"id":261167088,"uuid":"882754797","full_name":"tsmetadata/json-api","owner":"tsmetadata","description":"Standardized set of JSON:API metadata decorators.","archived":false,"fork":false,"pushed_at":"2024-12-09T04:02:48.000Z","size":94,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-01T18:15:15.388Z","etag":null,"topics":["api","json","metadata","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@tsmetadata/json-api","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsmetadata.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"zenodo":null}},"created_at":"2024-11-03T17:14:43.000Z","updated_at":"2024-12-11T12:37:01.000Z","dependencies_parsed_at":"2024-11-05T03:37:24.012Z","dependency_job_id":"3f24ede2-76d4-410b-9658-69e3b4449e1b","html_url":"https://github.com/tsmetadata/json-api","commit_stats":null,"previous_names":["tsmetadata/json-api"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/tsmetadata/json-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmetadata%2Fjson-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmetadata%2Fjson-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmetadata%2Fjson-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmetadata%2Fjson-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsmetadata","download_url":"https://codeload.github.com/tsmetadata/json-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsmetadata%2Fjson-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29167871,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-06T15:38:29.831Z","status":"ssl_error","status_checked_at":"2026-02-06T15:37:48.592Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["api","json","metadata","typescript"],"created_at":"2024-11-16T20:10:21.913Z","updated_at":"2026-02-06T16:07:28.952Z","avatar_url":"https://github.com/tsmetadata.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# JSON API Metadata Decorators\n\n`@tsmetadata/json-api` provides a standardized set of [JSON:API](https://jsonapi.org/) metadata decorators for classes in TypeScript 5.2+.\n\nBy appending metadata to specific classes, class fields, and class methods, we enable out of the box support for developer tooling like ORMs, serialization, resource governance, and more. To continue, with the JSON API decorator metadata approach, you implicitly prioritize modularity and reuse.\n\n- [🌱 Install](#-install)\n- [📋 Feature Set](#-feature-set)\n- [⚙️ Usage](#️-usage)\n- [😍 Full Example](#-full-example)\n- [❓ FAQ](#-faq)\n\n## 🌱 Install\n```bash\nnpm install @tsmetadata/json-api@latest\n```\n\n## 📋 Feature Set\n- [🏷️ Metadata Decorators](#metadata-decorators)\n  - [Resource](#resource--type)\n    - [Type](#resource--type)\n    - [Id](#id)\n  - [Attribute](#attribute)\n  - [Relationship](#relationship)\n  - [Link](#link)\n  - [Meta](#meta)\n- [📄 Serializers](#serializers)\n  - [Resource Object](#resource-object)\n  - [Relationship Object](#relationship-object)\n  - [Included Resource Objects](#included-resource-objects)\n- [📄 Deserializers](#deserializers)\n  - [Resource Object](#resource-object-1)\n- [✨ Types](#types)\n  - [Attributes Object](#attributes-object)\n  - [Error Object](#error-object)\n  - [JSON API Object](#json-api-object)\n  - [Link Object](#link-object)\n  - [Links Object](#links-object)\n  - [Meta Object](#meta-object)\n  - [Pagination Links](#pagination-links)\n  - [Relationship Object](#relationship-object)\n  - [Relationships Object](#relationships-object)\n  - [Resource Identifier Object](#resource-identifier-object)\n  - [Resource Linkage](#resource-linkage)\n  - [Resource Object](#resource-object-2)\n  - [Top Level Object](#top-level-object)\n\n## ⚙️ Usage\n### Metadata Decorators\n#### [Resource / Type](https://jsonapi.org/format/#document-resource-objects)\nThe `@Resource(type: string)` decorator is available and will define a resource's `type` (part of identification).\n\nex.\n```typescript\nimport { Resource } from '@tsmetadata/json-api';\n\n@Resource('users')\nclass User {}\n```\n\n#### [Id](https://jsonapi.org/format/#document-resource-object-identification)\nThe `@Id()` decorator can be applied to one class field and denotes what field contains a resource's `id` (part of identification).\n\nex.\n```typescript\nimport { Id } from '@tsmetadata/json-api';\n\nclass Account {\n  @Id()\n  accountNumber: string;\n}\n```\n\nThe applied metadata can be retrieved using the Symbol `idSymbol` export.\n```typescript\nimport { idSymbol } from '@tsmetadata/json-api';\n```\n\n#### [Attribute](https://jsonapi.org/format/#document-resource-object-attributes)\nThe `@Attribute()` decorator can be applied to many class fields and denotes what fields are resource attributes.\n\nex.\n```typescript\nimport { Attribute } from '@tsmetadata/json-api';\n\nclass Account {\n  @Attribute()\n  isPastDue: boolean;\n}\n```\n\nThe applied metadata can be retrieved using the Symbol `attributesSymbol` export.\n```typescript\nimport { attributesSymbol } from '@tsmetadata/json-api';\n```\n\n\n#### [Relationship](https://jsonapi.org/format/#document-resource-object-relationships)\nThe `Relationship(foreignKey: string)` decorator can be applied many times to many class fields and denotes what fields are resource relationships.\n\nThe foreign key is type-safe to the field type.\n\nex.\n```typescript\nimport { Relationship, type JSONAPIResourceLinkage } from '@tsmetadata/json-api';\n\nclass Account {\n  @Relationship('accounts')\n  primaryDebtor: Customer | JSONAPIResourceLinkage;\n\n  @Relationship('accounts')\n  coDebtors: Customer[] | JSONAPIResourceLinkage;\n}\n\nclass Customer {\n  @Relationship('primaryDebtor')\n  @Relationship('coDebtors')\n  accounts: Account[] | JSONAPIResourceLinkage;\n}\n```\n\nThe applied metadata can be retrieved using the Symbol `relationshipsSymbol` export.\n```typescript\nimport { relationshipsSymbol } from '@tsmetadata/json-api';\n```\n\n#### [Link](https://jsonapi.org/format/#document-resource-object-related-resource-links)\nThe `Link()` decorator can be applied to many class fields and denotes what fields are resource links.\n\nex.\n```typescript\nimport { Link } from '@tsmetadata/json-api';\n\nclass Account {\n  @Link()\n  self: string;\n\n  @Link()\n  recentTransactions: string;\n}\n```\n\nThe applied metadata can be retrieved using the Symbol `linksSymbol` export.\n```typescript\nimport { linksSymbol } from '@tsmetadata/json-api';\n```\n\n#### [Meta](https://jsonapi.org/format/#document-meta)\nThe `Meta()` decorator can be applied to many class fields and denotes what fields are resource metadata.\n\nex.\n```typescript\nimport { Meta } from '@tsmetadata/json-api';\n\nclass Account {\n  @Meta()\n  createdAt: number;\n\n  @Meta()\n  lastUpdated: number;\n}\n```\n\nThe applied metadata can be retrieved using the Symbol `metaSymbol` export.\n```typescript\nimport { metaSymbol } from '@tsmetadata/json-api';\n```\n\n### Serializers\n#### Resource Object\nThe `serializeResourceObject(classInstance: object)` function will produce a [resource object](https://jsonapi.org/format/#document-resource-objects) from a decorated class instance.\n\nex.\n```typescript\nimport { Resource, Id, Attribute, serializeResourceObject } from '@tsmetadata/json-api';\n\n@Resource('users')\nclass User {\n  @Id()\n  customerId: string;\n\n  @Attribute()\n  active: boolean;\n}\n\nconst user = new User();\nuser.customerId = '123';\nuser.active = false;\n\nserializeResourceObject(user);\n\n/*\n  {\n    \"type\": \"users\".\n    \"id\": \"123\",\n    \"attributes\": {\n      \"active\": false\n    }\n  }\n*/\n```\n\n#### Relationship Object\nThe `serializeRelationshipObject(classInstance: object)` function will produce a (relationship object)[https://jsonapi.org/format/#document-resource-object-relationships] from a decorated class instance.\n\nex.\n```typescript\nimport { Resource, Id, Link, serializeRelationshipObject } from '@tsmetadata/json-api';\n\n@Resource('users')\nclass User {\n  @Id()\n  customerId: string;\n\n  @Link()\n  self: string;\n}\n\nconst user = new User();\nuser.customerId = '123';\nuser.self = 'some-link';\n\nserializeRelationshipObject(user);\n\n/*\n  {\n    \"data\": {\n      \"type\": \"users\",\n      \"id\": \"123\"\n    },\n    \"links\": {\n      \"self\": \"some-link\"\n    }\n  }\n*/\n```\n\n#### Included Resource Objects\nThe `serializeResourceObject(classInstance: object, keys: string[])` function will produce an array of [resource objects](https://jsonapi.org/format/#document-resource-objects) from a decorated class instance.\n\nex.\n```typescript\nimport { Resource, Id, Link, serializeIncludedResourceObjects, type JSONAPIResourceLinkage } from '@tsmetadata/json-api';\n\n// For the sake of brevity, the `Account` class definition is not included.\n\n@Resource('users')\nclass User {\n  @Id()\n  customerId: string;\n\n  @Relationship('primaryDebtor')\n  @Relationship('coDebtors')\n  accounts: Account[] | JSONAPIResourceLinkage;\n\n  @Relationship('spouse')\n  spouse: User | JSONAPIResourceLinkage;\n}\n\nconst user1 = new User();\nuser1.customerId = '123';\nuser1.accounts = [someAccount, someOtherAccount];\n\nconst user2 = new User();\nuser2.customerId = '456';\nuser2.accounts = [someAccount, someOtherAccount];\n\nserializeIncludedResourceObjects(user1, ['accounts', 'spouse']);\n```\n\n### Deserializers\n\n### Resource Object\nThe `deserializeResourceObject(resourceObject: JSONAPIResourceObject, cls: new (..._: any[]) =\u003e any)` function will produce a class instance from a [resource object](https://jsonapi.org/format/#document-resource-objects).\n\nex.\n```typescript\nimport { Resource, Id, Attribute, serializeResourceObject, deserializeResourceObject } from '@tsmetadata/json-api';\n\n@Resource('users')\nclass User {\n  @Id()\n  customerId: string;\n\n  @Attribute()\n  active: boolean;\n}\n\nconst user = new User();\nuser.customerId = '123';\nuser.active = false;\n\nconst serializedUser = serializeResourceObject(user);\n\n/*\n  {\n    \"type\": \"users\".\n    \"id\": \"123\",\n    \"attributes\": {\n      \"active\": false\n    }\n  }\n*/\n\nconst deserializedUser = deserializeResourceObject(user, User);\n\n/*\n  user.customerId === '123'\n  user.active === false\n*/\n```\n\n### Types\n\n#### Attributes Object\n\n- [Specification](https://jsonapi.org/format/#document-resource-object-attributes)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/attributesObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIAttributesObject } from '@tsmetadata/json-api';\n```\n\n#### Error Object\n\n- [Specification](https://jsonapi.org/format/#error-objects)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/errorObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIErrorObject } from '@tsmetadata/json-api';\n```\n\n#### JSON API Object\n\n- [Specification](https://jsonapi.org/format/#document-jsonapi-object)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/jsonApiObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIObject } from '@tsmetadata/json-api';\n```\n\n#### Link Object\n\n- [Specification](https://jsonapi.org/format/#document-links-link-object)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/linkObject.ts)\n\nex.\n```typescript\nimport type { JSONAPILinkObject } from '@tsmetadata/json-api';\n```\n\n#### Links Object\n\n- [Specification](https://jsonapi.org/format/#document-links-link-object)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/linksObject.ts)\n\nex.\n```typescript\nimport type { JSONAPILinksObject } from '@tsmetadata/json-api';\n```\n\n#### Meta Object\n\n- [Specification](https://jsonapi.org/format/#document-meta)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/metaObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIMetaObject } from '@tsmetadata/json-api';\n```\n\n#### Pagination Links\n\n- [Specification](https://jsonapi.org/format/#fetching-pagination)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/paginationLinks.ts)\n\nex.\n```typescript\nimport type { JSONAPIPaginationLinks } from '@tsmetadata/json-api';\n```\n\n#### Relationship Object\n\n- [Specification](https://jsonapi.org/format/#document-resource-object-relationships)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/relationshipObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIRelationshipObject } from '@tsmetadata/json-api';\n```\n\n#### Relationships Object\n\n- [Specification](https://jsonapi.org/format/#document-resource-object-relationships)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/relationshipsObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIRelationshipsObject } from '@tsmetadata/json-api';\n```\n\n#### Resource Identifier Object\n\n- [Specification](https://jsonapi.org/format/#document-resource-identifier-objects)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/resourceIdentifierObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIResourceIdentifierObject } from '@tsmetadata/json-api';\n```\n\n#### Resource Linkage\n\n- [Specification](https://jsonapi.org/format/#document-resource-object-linkage)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/resourceLinkage.ts)\n\nex.\n```typescript\nimport type { JSONAPIResourceLinkage } from '@tsmetadata/json-api';\n```\n\n#### Resource Object\n\n- [Specification](https://jsonapi.org/format/#document-resource-objects)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/resourceObject.ts)\n\nex.\n```typescript\nimport type { JSONAPIResourceObject } from '@tsmetadata/json-api';\n```\n\n#### Top Level Object\n\n- [Specification](https://jsonapi.org/format/#document-top-level)\n- [Definition](https://github.com/tsmetadata/json-api/blob/main/src/types/topLevelObject.ts)\n\nex.\n```typescript\nimport type { JSONAPITopLevelObject } from '@tsmetadata/json-api';\n```\n\n## 😍 Full Example\n```typescript\nimport { Attribute, Link, Meta, Relationship, Resource, serializeIncludedResourceObjects,\n         serializeResourceObject, deserializeResourceObject, type JSONAPIResourceLinkage } from '@tsmetadata/json-api';\n\n@Resource('accounts')\nexport class Account {\n  @Attribute()\n  accountNumber: string;\n\n  @Attribute()\n  pastDue: boolean;\n\n  @Relationship('accounts')\n  primaryDebtor: Customer | JSONAPIResourceLinkage;\n\n  @Relationship('accounts')\n  coDebtors: Customer[] | JSONAPIResourceLinkage;\n\n  @Link()\n  self: string;\n\n  @Meta()\n  lastUpdated: number;\n}\n\n@Resource('customers')\nexport class Customer {\n  @Id()\n  id: string;\n\n  @Attribute()\n  name: string;\n\n  @Relationship('primaryDebtor')\n  @Relationship('coDebtors')\n  accounts: Account[] | JSONAPIResourceLinkage;\n\n  @Link()\n  self: string;\n}\n\nconst account = new Account();\naccount.accountNumber = '123';\naccount.pastDue = false;\naccount.coDebtors = [];\naccount.self = 'some-url';\naccount.lastUpdated = Date.now();\n\nconst customer = new Customer();\ncustomer.id = '456';\ncustomer.name = 'Bob';\ncustomer.self = 'some-url';\n\naccount.primaryDebtor = customer;\ncustomer.accounts = [account];\n\nconst serializedCustomer = serializeResourceObject(customer);\n\n// Try logging out the results on your own!\nconsole.log(\n  serializedCustomer,\n  serializeRelationshipObject(customer),\n  serializeIncludedResourceObjects(customer, ['accounts'])\n);\n\n// You can deserialize too!\nconst customerWithResourceLinkages = deserializeResourceObject(serializedCustomer, Customer);\n```\n\n## ❓ FAQ\n\n### Q: I'm using a legacy runtime that doesn't yet support Symbol metadata.\nA: You may be able to take advantage of our `Symbol.metadata` polyfill found [here](https://github.com/tsmetadata/polyfill).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsmetadata%2Fjson-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsmetadata%2Fjson-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsmetadata%2Fjson-api/lists"}