Skip to main content

overview

Serializers are how your data gets from a Dream model into JSON for your API responses. They also feed into Psychic's automatic OpenAPI generation, so your docs stay in sync with your actual response shapes.

Dream provides two serializer functions: DreamSerializer for serializing Dream models, and ObjectSerializer for serializing view models or plain JavaScript objects. DreamSerializer knows about your database schema and associations, so it can automatically figure out OpenAPI shapes for you. ObjectSerializer needs you to tell it the shape of each attribute.

The API is fluent — you chain calls to .attribute(), .customAttribute(), .delegatedAttribute(), .rendersOne(), and .rendersMany() to build up the shape of your serialized output.

Each model can have multiple serializers for different contexts (e.g. a summary serializer for index endpoints and a default serializer for show endpoints), and serializers compose nicely — you can extend one from another.

One important design decision: serializers are intentionally synchronous. This prevents you from accidentally introducing N+1 query problems during serialization. Load your data upfront (using preloadFor is the easiest way), then let the serializer do its thing.

example

import { DreamSerializer } from '@rvoh/dream'
import Place from '../models/Place.js'

export const PlaceSummarySerializer = (place: Place) =>
DreamSerializer(Place, place).attribute('id').attribute('name')

export const PlaceSerializer = (place: Place) =>
PlaceSummarySerializer(place).attribute('style').attribute('sleeps').attribute('deletedAt')

Here, PlaceSerializer extends PlaceSummarySerializer — it includes everything the summary does, plus a few more fields. This is a common pattern for keeping your serializers DRY.