| name | yayson |
| description | Serialize and parse JSON API data with yayson. Use when writing Presenters, setting up Stores (standard or legacy), or working with yayson relationships and schema validation. |
YAYSON
Setup
import yayson from 'yayson'
const { Presenter, Store } = yayson()
import yayson from 'yayson/legacy'
const { Presenter, Store } = yayson()
const { Presenter } = yayson({ adapter: 'sequelize' })
import { getType, getMeta, getLinks, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'
Presenters
Presenters serialize JS objects into JSON API documents. Subclass Presenter and set static type.
Basic presenter
class UserPresenter extends Presenter {
static type = 'users'
}
UserPresenter.render({ id: 1, name: 'Ada' })
Field filtering
class UserPresenter extends Presenter {
static type = 'users'
static fields = ['name', 'email']
}
Relationships
Return a map of property name to Presenter class from relationships(). Related data goes into included.
class WheelPresenter extends Presenter {
static type = 'wheels'
}
class BikePresenter extends Presenter {
static type = 'bikes'
relationships() {
return { wheels: WheelPresenter }
}
}
BikePresenter.render({ id: 1, wheels: [{ id: 10 }, { id: 11 }] })
For to-many relationships and conditionally loaded (?include=) responses, declare cardinality and/or optional semantics via the config form:
class TicketPresenter extends Presenter {
static type = 'tickets'
relationships() {
return {
addons: { presenter: AddonPresenter, hasMany: true },
parentTicket: { presenter: TicketPresenter, optional: true },
guestTickets: { presenter: TicketPresenter, hasMany: true, optional: true },
}
}
}
hasMany: true → empty/missing data renders as data: [] instead of data: null (spec-compliant for to-many).
optional: true → when the relationship key is absent from the instance, the relationship is omitted from output entirely (or rendered as { links } only if links() configures one for that key). An explicit null on the instance still renders as data: null — optional distinguishes "not loaded" from "explicitly empty".
The bare-class form is unchanged.
Custom attributes
Override attributes() to transform or compute attributes.
class EventPresenter extends Presenter {
static type = 'events'
attributes(instance) {
const attrs = super.attributes(instance)
return { ...attrs, slug: attrs.name.toLowerCase().replace(/ /g, '-') }
}
}
Links
Override selfLinks() for resource links and links() for relationship links.
class CarPresenter extends Presenter {
static type = 'cars'
relationships() {
return { motor: MotorPresenter }
}
selfLinks(instance) {
return '/cars/' + this.id(instance)
}
links(instance) {
return {
motor: {
self: this.selfLinks(instance) + '/relationships/motor',
related: this.selfLinks(instance) + '/motor',
},
}
}
}
Render options
Pass meta and links as top-level document properties:
ItemPresenter.render(items, {
meta: { total: 100, page: 1 },
links: { self: '/items?page=1', next: '/items?page=2' },
})
Rendering null produces { data: null }. Arrays produce { data: [...] }.
Store (Standard)
Parses JSON API documents, resolves relationships, and caches models.
const store = new Store()
Syncing data
const event = store.sync({ data: { type: 'events', id: '1', attributes: { name: 'Demo' } } })
const events = store.syncAll(jsonApiDocument)
const event = store.retrieve('events', jsonApiDocument)
const images = store.retrieveAll('images', jsonApiDocument)
Querying cached data
store.find('events', 1)
store.findAll('events')
store.remove('events', 1)
store.remove('events')
store.reset()
Relationships resolve automatically
store.sync({
data: { type: 'events', id: '1', relationships: { images: { data: [{ type: 'images', id: '2' }] } } },
included: [{ type: 'images', id: '2', attributes: { url: 'pic.jpg' } }],
})
const event = store.find('events', '1')
event.images[0].url
Schema validation
Accepts any Zod-like schema (must have parse/safeParse methods). Use .passthrough() on Zod objects so extra attributes aren't stripped.
import { z } from 'zod'
const eventSchema = z
.object({
id: z.string(),
name: z.string(),
})
.passthrough()
const store = new Store({
schemas: { events: eventSchema },
strict: true,
})
Accessing metadata via symbols
import { getType, getMeta, getLinks, getRelationshipLinks, getRelationshipMeta } from 'yayson/utils'
const event = store.find('events', '1')
getType(event)
getMeta(event)
getLinks(event)
getRelationshipLinks(event.images)
getRelationshipMeta(event.images)
Store (Legacy)
For pre-JSON API 1.0 flat format. Import from yayson/legacy.
import yayson from 'yayson/legacy'
const { Store } = yayson()
const store = new Store({
types: { events: 'event', images: 'image' },
})
Legacy data format
store.sync({
links: {
'event.images': { type: 'images' },
'images.event': { type: 'event' },
},
event: { id: 1, name: 'Demo', images: [2] },
images: [{ id: 2, event: 1, url: 'pic.jpg' }],
})
const event = store.find('event', 1)
event.images[0].url
The API methods (sync, syncAll, retrieve, retrieveAll, find, findAll, remove, reset) work the same as the standard Store. Schema validation is also supported.
Quick Reference
For detailed API reference including all method signatures, return types, and edge cases, see references/api.md.