[](https://www.npmjs.com/package/@vmelou/jsonapi) [](https://opensource.org/licenses/MIT)
npm install @vmelou/jsonapi

A TypeScript library for serializing and deserializing classes to/from JSON:API format.
- Features
- Installation
- Usage
- Define Your Models
- Serialization
- Deserialization
- Advanced Usage
- Custom Attribute Transformation
- Handling Relationships
- Accessing Links and Meta
- API Reference
- Contributing
- Changelog
- License
- Serialize TypeScript classes to JSON:API compliant objects
- Deserialize JSON:API responses into TypeScript class instances
- Support for relationships and included resources
- Handles primitive types and Date objects
- Support for collection responses with pagination
- Error handling with JSON:API error objects
``bash`
npm install @vmelou/jsonapior
yarn add @vmelou/jsonapi
Use decorators to define how your classes map to JSON:API resources:
`typescript
import { BaseResource, JsonResource, JsonAttribute } from '@vmelou/jsonapi';
@JsonResource('authors')
class Author extends BaseResource {
@JsonAttribute()
name = '';
@JsonAttribute(Date, 'created-at')
createdAt: Date = new Date();
}
@JsonResource('books')
class Book extends BaseResource {
@JsonAttribute()
title = '';
@JsonAttribute()
isbn = '';
@JsonAttribute(Author)
author: Author = new Author();
@JsonAttribute(Author, 'co-authors')
coAuthors: Author[] = [];
}
`
`typescript
import { serialize } from '@vmelou/jsonapi';
const author = new Author({
id: '1',
name: 'John Doe',
createdAt: new Date('2025-01-01'),
});
const book = new Book({
id: '1',
title: 'My Book',
isbn: '123-456-789',
author,
});
const serialized = serialize(Book, book);
// Result:
// {
// data: {
// id: '1',
// type: 'books',
// attributes: {
// title: 'My Book',
// isbn: '123-456-789'
// },
// relationships: {
// author: {
// data: { id: '1', type: 'authors' }
// }
// }
// }
// }
`
`typescript
import { deserialize, deserializeCollection } from '@vmelou/jsonapi';
// Deserialize single resource
const json = {
data: {
id: '1',
type: 'books',
attributes: {
title: 'My Book',
isbn: '123-456-789'
},
relationships: {
author: {
data: { id: '1', type: 'authors' }
}
}
},
included: [
{
id: '1',
type: 'authors',
attributes: {
name: 'John Doe',
'created-at': '2025-01-01'
}
}
]
};
const book = deserialize(Book, json.data, json.included);
// Deserialize collection
const collection = deserializeCollection(Book, {
data: [...],
included: [...],
links: {
first: '...',
last: '...',
next: '...',
prev: '...'
},
meta: {
pagination: {
count: 42,
page: 1,
pages: 5
}
}
});
`
Here are some examples for more complex scenarios:
You can provide a transformation function to the @JsonAttribute decorator to modify how values are serialized and deserialized. The transform function is applied in both directions (serialization and deserialization).
`typescript
import { BaseResource, JsonResource, JsonAttribute, serialize, deserialize } from '@vmelou/jsonapi';
@JsonResource('products')
class Product extends BaseResource {
// Always store and retrieve the name in uppercase
@JsonAttribute(String, 'product-name', (value) => {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
})
name = '';
// Store and display price in cents
@JsonAttribute(Number, 'price', (value) => {
if (typeof value === 'number') {
return Math.round(value);
}
return value;
})
priceInCents = 0;
}
// Example Usage
const product = new Product({ id: '1', name: 'widget', priceInCents: 1999 });
const serialized = serialize(Product, product);
/* serialized.data.attributes will be:
{
"product-name": "WIDGET",
"price": 1999
}
*/
const jsonData = {
id: '1',
type: 'products',
attributes: {
'product-name': 'gadget',
'price': 5000
}
};
const deserializedProduct = deserialize(Product, jsonData, []);
/* deserializedProduct will have:
{
id: '1',
name: 'GADGET',
priceInCents: 5000
}
`
The transform function receives the value being processed and should return the transformed value. The same transform is used for both serialization and deserialization, so make sure your transformation logic works in both directions.
Define relationships using @JsonAttribute with the related class type. The library automatically handles linking resources during serialization and populating related objects during deserialization using the included array.
`typescript
import { BaseResource, JsonResource, JsonAttribute, deserialize } from '@vmelou/jsonapi';
@JsonResource('stores')
class Store extends BaseResource {
@JsonAttribute()
storeName = '';
}
@JsonResource('employees')
class Employee extends BaseResource {
@JsonAttribute()
firstName = '';
@JsonAttribute(Store) // To-one relationship
store: Store | null = null;
}
@JsonResource('departments')
class Department extends BaseResource {
@JsonAttribute()
deptName = '';
@JsonAttribute(Employee, 'staff') // To-many relationship
employees: Employee[] = [];
}
// Example Deserialization with included data
const departmentJson = {
id: 'D1',
type: 'departments',
attributes: { deptName: 'Sales' },
relationships: {
staff: {
data: [
{ id: 'E1', type: 'employees' },
{ id: 'E2', type: 'employees' },
],
},
},
};
const includedData = [
{ id: 'E1', type: 'employees', attributes: { firstName: 'Alice' }, relationships: { store: { data: { id: 'S1', type: 'stores' } } } },
{ id: 'E2', type: 'employees', attributes: { firstName: 'Bob' }, relationships: { store: { data: { id: 'S1', type: 'stores' } } } },
{ id: 'S1', type: 'stores', attributes: { storeName: 'Main Street Branch' } },
];
const department = deserialize(Department, departmentJson, includedData);
// department.employees will be an array of Employee instances
// department.employees[0].store will be a Store instance
console.log(department.employees[0].firstName); // Output: Alice
console.log(department.employees[0].store?.storeName); // Output: Main Street Branch
`
When deserializing a collection using deserializeCollection, the returned Results object provides access to the links and meta objects from the JSON:API response.
`typescript
import { deserializeCollection, Results, Book } from '@vmelou/jsonapi'; // Assuming Book model exists
const jsonResponse = {
data: [
// ... book resource objects ...
],
included: [
// ... included author resource objects ...
],
meta: {
pagination: { total: 100, pages: 10, currentPage: 2 },
},
links: {
self: '/books?page=2',
first: '/books?page=1',
prev: '/books?page=1',
next: '/books?page=3',
last: '/books?page=10',
},
};
const results: Results
// Access deserialized data
const books: Book[] = results.data;
// Access pagination metadata
const totalBooks = results.meta?.pagination?.total; // 100
const currentPage = results.meta?.pagination?.currentPage; // 2
// Access links
const nextPageLink = results.links?.next; // '/books?page=3'
console.log(Displaying page ${currentPage} of ${results.meta?.pagination?.pages}. Total items: ${totalBooks});Next page: ${nextPageLink}
if (nextPageLink) {
console.log();`
}
- @JsonResource(type: string): Defines the JSON:API resource type for a class@JsonAttribute(type?: any, attribute?: string, transform?: Function | { serialize: Function, deserialize: Function })
- : Maps class properties to JSON:API attributes, optionally providing transformation logic.
- serializedeserialize
- deserializeCollection
-
- BaseResource: Base class for all JSON:API resources.Results
-
We welcome contributions! Please see our Contributing Guidelines for more details on how to get involved.
Detailed changes for each release are documented in the CHANGELOG.md file.
MIT