Skip to main content

rendering

Once you've defined your serializers, you need to actually use them to render data in your controllers. There are two ways to go about this: explicitly calling .render() yourself, or letting Psychic do it for you automatically.

explicit rendering

If you want full control, you can call a serializer directly and render it yourself:

export default class StuffsController extends ApplicationController {
public async show() {
const stuff = await Stuff.findOrFail(this.castParam('id', 'bigint'))
this.ok(StuffSerializer(stuff).render())
}
}

implicit rendering

The more common approach is to let Psychic handle serialization for you. When you pass a Dream model instance (or an array of them) to a response method like this.ok(), Psychic will automatically look up the right serializer and call .render() on your behalf.

For this to work, your model needs a serializers getter that maps names to serializer function names:

export default class Stuff extends ApplicationModel {
public get serializers(): DreamSerializers<Stuff> {
return {
default: 'StuffSerializer',
summary: 'StuffSummarySerializer',
}
}
}

With that in place, you can just pass models straight through:

public async index() {
const stuffs = await Stuff.preloadFor('summary').all()
this.ok(stuffs) // uses the 'default' serializer
}

public async show() {
const stuff = await Stuff.preloadFor('default').findOrFail(this.castParam('id', 'bigint'))
this.ok(stuff) // uses the 'default' serializer
}

serializer passthrough data

Often you'll want to make request-specific data — like the current user's locale — available to your serializers. Psychic controllers provide this.serializerPassthrough() for exactly this. You'll typically call it in a BeforeAction on a base controller:

export default class ApplicationController extends PsychicController {
@BeforeAction()
public configureSerializers() {
this.serializerPassthrough({
locale: getLocaleFromHeaders(this.ctx.headers),
})
}
}

The passthrough data then shows up as the second argument to your serializer function:

export const PlaceForGuestsSerializer = (place: Place, passthrough: { locale: LocalesEnum }) =>
DreamSerializer(Place, place, passthrough)
.customAttribute('style', () => i18n(passthrough.locale, `places.style.${place.style}`), {
openapi: 'string',
})

When using rendersOne or rendersMany with the serializer option, passthrough data from the parent serializer is automatically forwarded to the nested serializer — no extra wiring needed.