npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vmelou/jsonapi

v0.4.2

Published

[![npm version](https://img.shields.io/npm/v/@vmelou/jsonapi)](https://www.npmjs.com/package/@vmelou/jsonapi) [![License: MIT](https://img.shields.io/npm/l/@vmelou/jsonapi)](https://opensource.org/licenses/MIT)

Downloads

41

Readme

@vmelou/jsonapi

npm version License: MIT

A TypeScript library for serializing and deserializing classes to/from JSON:API format.

Table of Contents

Features

  • 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

Installation

npm install @vmelou/jsonapi
# or
yarn add @vmelou/jsonapi

Usage

Define Your Models

Use decorators to define how your classes map to JSON:API resources:

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[] = [];
}

Serialization

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' }
//       }
//     }
//   }
// }

Deserialization

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
    }
  }
});

Advanced Usage

Here are some examples for more complex scenarios:

Custom Attribute Transformation

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).

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.

Handling Relationships

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.

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

Accessing Links and Meta

When deserializing a collection using deserializeCollection, the returned Results object provides access to the links and meta objects from the JSON:API response.

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<Book> = deserializeCollection(Book, jsonResponse);

// 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}`);
if (nextPageLink) {
  console.log(`Next page: ${nextPageLink}`);
}

API Reference

Decorators

  • @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.

Functions

  • serialize<T extends BaseResource>(cls: Constructor<T>, data: T | T[], relationship?: boolean): { data: JsonResource | JsonResource[] }
  • deserialize<T extends BaseResource>(cls: Constructor<T>, data: JsonResource, included?: JsonResource[]): T
  • deserializeCollection<T extends BaseResource>(cls: Constructor<T>, response: JsonListResponse): Results<T>

Classes

  • BaseResource: Base class for all JSON:API resources.
  • Results<T>: Container for collection responses, including data, links, and meta.

Contributing

We welcome contributions! Please see our Contributing Guidelines for more details on how to get involved.

Changelog

Detailed changes for each release are documented in the CHANGELOG.md file.

License

MIT