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

yayson

v4.0.0

Published

A library for serializing and reading JSON API standardized data in JavaScript.

Readme

YAYSON

A library for serializing and reading JSON API data in JavaScript.

YAYSON supports both ESM and CommonJS, has zero dependencies, and works in the browser and in Node.js 20+.

NPM

Installing

npm install yayson

Upgrading from 3.x

Version 4.x is a full TypeScript rewrite with several new features and breaking changes.

New features

  • TypeScript support — Full type definitions included, with type inference from schema registries
  • Schema validation — Optional runtime validation using Zod or any compatible library with parse()/safeParse() methods
  • Dual ESM/CJS package — Native ES module support alongside CommonJS
  • yayson/utils entry point — Helper functions (getType, getLinks, getMeta, getRelationshipLinks, getRelationshipMeta) for reading model metadata
  • retrieveAll(type, data) — Sync data and return only models of a specific type
  • syncAll() — Always returns an array (useful when you always want array behavior)
  • fields property — Limit which attributes a Presenter includes in its output

Breaking changes

Node.js version

Node.js 20+ is now required (was 14+).

Metadata uses Symbols instead of plain properties

In 3.x, resource type, links, and meta were stored as plain properties on models (model.type, model.links, model.meta). Relationship metadata used model._links and model._meta.

In 4.x, these use Symbol keys to avoid collisions with your data. Use the helpers from yayson/utils:

// 3.x
model.type
model.links
model.meta
relatedModel._links
relatedModel._meta

// 4.x
import { getType, getLinks, getMeta, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'
getType(model)
getLinks(model)
getMeta(model)
getRelationshipLinks(relatedModel)
getRelationshipMeta(relatedModel)

sync() restores 3.x behavior

sync() now matches 3.x behavior: single resources return a model directly, collections return an array. If you always want an array, use syncAll().

// Single resource — returns model directly
const event = store.sync({ data: { type: 'events', id: '1', attributes: { name: 'Demo' } } })
event.name // 'Demo'

// Collection — returns array
const events = store.sync({ data: [{ type: 'events', id: '1', attributes: { name: 'Demo' } }] })
events.length // 1

// Always returns array
const events = store.syncAll(data)

// retrieve/retrieveAll also available
const event = store.retrieve('events', data)
const events = store.retrieveAll('events', data)

Document-level meta uses Symbols

In 3.x, document-level metadata was stored as result.meta. In 4.x, it uses a Symbol key:

// 3.x
const result = store.sync(data)
result.meta // { total: 100 }

// 4.x
import { META } from 'yayson/utils'
const result = store.sync(data)
result[META] // { total: 100 }

Presenting data

A basic Presenter can look like this:

// ESM
import yayson from 'yayson'
const { Presenter } = yayson()

// CommonJS
const yayson = require('yayson')
const { Presenter } = yayson()

class BikePresenter extends Presenter {
  static type = 'bikes'
}

const bike = {
  id: 5,
  name: 'Monark',
}

BikePresenter.render(bike)

This would produce:


{
  data: {
    id: '5',
    type: 'bikes',
    attributes: {
      name: 'Monark'
    }
  }
}

It also works with arrays, so if you send an array to render, "data" will be an array.

A bit more advanced example:

import yayson from 'yayson'
const { Presenter } = yayson()

class WheelPresenter extends Presenter {
  static type = 'wheels'

  relationships() {
    return { bike: BikePresenter }
  }
}

class BikePresenter extends Presenter {
  static type = 'bikes'
  relationships() {
    return { wheels: WheelPresenter }
  }
}

Filtering attributes with fields

Use the static fields property to limit which attributes are included in the output:

class UserPresenter extends Presenter {
  static type = 'users'
  static fields = ['name', 'email', 'createdAt']
}

const user = {
  id: 1,
  name: 'John',
  email: '[email protected]',
  password: 'secret',
  createdAt: '2024-01-01',
}

UserPresenter.render(user)

This would produce:

{
  data: {
    id: '1',
    type: 'users',
    attributes: {
      name: 'John',
      email: '[email protected]',
      createdAt: '2024-01-01'
    }
  }
}

The password field is excluded because it's not in the fields array. This is useful for hiding sensitive data or reducing payload size without overriding the attributes() method.

Sequelize support

By default it is set up to handle standard JS objects. You can also make it handle Sequelize.js models like this:

import yayson from 'yayson'
const { Presenter } = yayson({ adapter: 'sequelize' })

You can also define your own adapter globally:

import yayson from 'yayson'
const { Presenter } = yayson({
  adapter: {
    id: function (model) {
      return 'omg' + model.id
    },
    get: function (model, key) {
      return model[key]
    },
  },
})

Take a look at the SequelizeAdapter if you want to extend YAYSON to your ORM. Pull requests are welcome. :)

render() options

The second argument to render() accepts an options object with meta and links:

ItemsPresenter.render(items, {
  meta: { total: 100, page: 1 },
  links: {
    self: 'http://example.com/items?page=1',
    next: 'http://example.com/items?page=2',
  },
})

This would produce:

{
  meta: {
    total: 100,
    page: 1
  },
  links: {
    self: 'http://example.com/items?page=1',
    next: 'http://example.com/items?page=2'
  },
  data: [...]
}

Both meta and links are optional and add top-level properties to the JSON API document.

Parsing data

You can use a Store like this:

import yayson from 'yayson'
const { Store } = yayson()
const store = new Store()

const data = await adapter.get({ path: '/events/' + id })
const event = store.sync(data) // single resource → model directly

The sync() method returns a single model for single resources and an array for collections. Use syncAll() if you always want an array.

const allSynced = store.syncAll(data) // always returns array

Filtering by type with retrieveAll

Use retrieveAll() to sync data and return only models of a specific type:

const data = await adapter.get({ path: '/events/' })

// Sync and return only events (filters out included resources)
const events = store.retrieveAll('events', data)

This is useful when the response contains multiple types but you only need the primary resources:

// Response contains events and their related images
const response = {
  data: [
    { type: 'events', id: '1', attributes: { name: 'Conference' } },
    { type: 'events', id: '2', attributes: { name: 'Meetup' } },
  ],
  included: [{ type: 'images', id: '10', attributes: { url: 'http://example.com/img.jpg' } }],
}

// Get only the events, not the included images
const events = store.retrieveAll('events', response)
// events.length === 2

Finding synced data

You can also find in previously synced data:

const event = store.find('events', id)

const images = store.findAll('images')

Schema Validation and Type Inference

YAYSON supports optional schema validation using Zod or any compatible schema library. This enables:

  • Runtime validation: Ensure your data matches expected schemas
  • TypeScript type inference: Get full type safety without manual type definitions
  • Strict or safe modes: Throw errors or collect validation issues

Basic Usage with Zod

import { z } from 'zod'
import yayson from 'yayson'

const { Store } = yayson()

const eventSchema = z
  .object({
    id: z.string(),
    name: z.string(),
    date: z.string(),
  })
  .passthrough()

const schemas = {
  events: eventSchema,
} as const

const store = new Store({ schemas, strict: true })

store.sync({
  data: {
    type: 'events',
    id: '1',
    attributes: { name: 'TypeScript Meetup', date: '2025-01-15' },
  },
})

// TypeScript infers the correct type automatically
const event = store.find('events', '1')
// event.name, event.date are fully typed!

Validation Modes

Strict mode (throws errors on validation failure):

const store = new Store({ schemas, strict: true })

Safe mode (collects errors without throwing):

const store = new Store({ schemas, strict: false })

store.sync(invalidData)

if (store.validationErrors.length > 0) {
  console.warn('Validation issues:', store.validationErrors)
}

Schemas must be Zod-like objects with parse() and safeParse() methods. Any library that provides this interface will work.

Utilities

YAYSON stores metadata on models using Symbol keys. The yayson/utils entry point provides helpers for reading them:

import { getType, getLinks, getMeta, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'

const events = store.syncAll(data)
const event = events[0]

getType(event) // 'events'
getLinks(event) // { self: 'http://...' }
getMeta(event) // { createdBy: 'admin' }
getRelationshipLinks(event.image) // { self: 'http://.../relationships/image' }
getRelationshipMeta(event.image) // { permission: 'read' }

The raw symbols (TYPE, LINKS, META, REL_LINKS, REL_META) are also exported from yayson/utils if you prefer direct access.

Note that syncAll() and retrieveAll() return arrays with a META symbol property for document-level metadata. Array methods like .filter() and .map() return plain arrays without this property, so extract it before transforming:

import { META } from 'yayson/utils'

const result = store.syncAll(data)
const meta = result[META] // { total: 100, page: 1 }
const filtered = result.filter((e) => e.name === 'Demo')
// filtered[META] is undefined — use the extracted `meta` instead

Claude Code skill

YAYSON includes a Claude Code skill that helps you write Presenters and set up Stores. To install it, copy the skill/yayson directory to your project's .claude/skills/ or your personal ~/.claude/skills/:

# Project-level (available to everyone working on that project)
cp -r node_modules/yayson/skill/yayson .claude/skills/

# Personal (available across all your projects)
cp -r node_modules/yayson/skill/yayson ~/.claude/skills/

Use in the browser

Recommended way is to use it via webpack or similar build system wich lets you just require the package as usual.

Legacy support

Earlier versions of JSON API worked a bit different from 1.0. Therefore YAYSON provides legacy presenters and stores in order to have interoperability between the versions.

Basic Usage

// ESM
import yayson from 'yayson/legacy'
const { Presenter, Store } = yayson()

// CommonJS
const yayson = require('yayson/legacy')
const { Presenter, Store } = yayson()
const store = new Store({
  types: {
    events: 'event',
    images: 'image',
  },
})

const event = store.sync({
  event: { id: '1', name: 'Demo Event' },
})
// single resource → returns model directly

const allSynced = store.syncAll({
  event: { id: '1', name: 'Demo Event' },
  images: [{ id: '2', url: 'http://example.com/image.jpg' }],
})
// syncAll always returns array

const event = store.find('event', '1')

Filtering by type with retrieveAll

Use retrieveAll() to sync data and return only models of a specific type:

const events = store.retrieveAll('event', {
  event: [
    { id: '1', name: 'Event 1' },
    { id: '2', name: 'Event 2' },
  ],
  image: [{ id: '3', url: 'http://example.com/image.jpg' }],
})
// events.length === 2 (only events, not images)

Options can be passed when creating the store:

const store = new Store({
  types: { events: 'event' },
})

Schema Validation for Legacy Store

The legacy store also supports schema validation and type inference, maintaining full backward compatibility:

import yayson from 'yayson/legacy'
import { z } from 'zod'

const { Store } = yayson()

const eventSchema = z.object({
  id: z.string(),
  name: z.string(),
  date: z.string(),
})

const store = new Store({
  schemas: { event: eventSchema },
  strict: true,
})

store.sync({ event: { id: '1', name: 'Event', date: '2025-01-15' } })

// TypeScript infers the correct type
const event = store.find('event', '1')
// event.name, event.date are fully typed!

Validation with Type Mapping

You can combine type mapping with schemas:

const store = new Store({
  types: {
    events: 'event', // Map plural to singular
  },
  schemas: {
    event: eventSchema, // Schema uses normalized type
  },
  strict: false, // Safe mode
})

store.sync({ events: [{ id: '1', name: 'Event' }] })

if (store.validationErrors.length > 0) {
  console.warn('Validation issues:', store.validationErrors)
}

Validation with Relations

Schemas validate the complete model after relations are resolved:

const eventSchema = z.object({
  id: z.string(),
  name: z.string(),
  images: z.array(
    z.object({
      id: z.string(),
      url: z.string(),
    }),
  ),
})

const imageSchema = z.object({
  id: z.string(),
  url: z.string(),
})

const store = new Store({
  schemas: { event: eventSchema, image: imageSchema },
  strict: true,
})

store.sync({
  links: {
    'event.images': { type: 'image' },
  },
  event: { id: '1', name: 'Event', images: ['2'] },
  image: [{ id: '2', url: 'http://example.com/image.jpg' }],
})

const event = store.find('event', '1')
// Relations are resolved before validation
// event.images is an array of validated image objects

Note: Validation happens eagerly during sync() when schemas are configured. This allows you to check store.validationErrors immediately after syncing.