Skip to main content

Serializing Attributes

Basic Attributes

The attribute method includes a property from your source object in the serialized output.

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

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

// PlaceSummarySerializer serializes to:
// { id: 1234, name: 'My place' }
// and PlaceSerializer serializes to:
// { id: 1234, name: 'My place', style: 'cabin', sleeps: 5 }
info

CalendarDate and DateTime attributes (even arrays of CalendarDate and DateTime values) are automatically converted to their ISO values as part of serialization.

openapi option

When serializing a Dream model with DreamSerializer, the OpenAPI shape of attributes corresponding to database columns is determined automatically (except for json and jsonb columns). However, you may still want to include an openapi description:

.attribute('name', { openapi: { description: 'The name of the Place' } })`
info

ObjectSerializer serializer attributes always requires the openapi option.

For a virtual attribute on a Dream model, openapi is required even for DreamSerializer:

export const PlaceSerializer = (place: Place) =>
DreamSerializer(Place, place)
.attribute('id')
.attribute('myVirtualAttribute', { openapi: ['string', 'null'] })

This example leverages OpenAPI shorthand, but the full OpenAPI shape is available for representing complex shapes, as one must do for attributes that correspond to json/jsonb columns:

export const PlaceSerializer = (place: Place) =>
DreamSerializer(Place, place)
.attribute('id')
.attribute('myJsonAttribute', {
openapi: {
type: 'object',
properties: {
label: 'string',
value: 'decimal',
},
},
})

default option

The default option provides a fallback value when the attribute is null or undefined. The default value undergoes the same transformations as regular data (e.g., decimals are rounded, dates are converted to ISO format).

export const PlaceSerializer = (place: Place) =>
DreamSerializer(Place, place)
.attribute('id')
.attribute('name', { default: 'unnamed' })
.attribute('sleeps', { default: 0 })

renaming attributes with as

Use the as option to change the name of the attribute in the output.

export const PlaceSerializer = (place: Place) =>
DreamSerializer(Place, place).attribute('id').attribute('sleeps', { as: 'accomodates' })

// PlaceSerializer serializes to:
// { id: 1234, accomodates: 5 }

precision

Decimal values can be automatically rounded with the precision option.

export const PlaceSerializer = (place: Place) =>
DreamSerializer(Place, place).attribute('id').attribute('rating', { precision: 1 })

// A place with rating 4.66666 would serialize to:
// { id: 1234, rating: 4.7 }

Delegated Attributes

The delegatedAttribute method renders an attribute from a nested object into the serialized output. delegatedAttribute attributes are automatically inspected as part of preloadFor (so when delegating to an association, that association will be automatically preloaded).

export const PlaceSummaryForGuestsSerializer = (place: Place) =>
DreamSerializer(Place, place)
.attribute('id')
.delegatedAttribute('currentLocalizedText', 'title', { openapi: 'string' })

// A place with currentLocalizedText with { title: 'Hello world' } will render:
// { id: 1234, title: 'Hello world' }

default option

All options from the attribute method are supported on delegatedAttribute. On a delegated attribute, the default will be applied whether then association is null or the attribute on the association is null:

export const PlaceSummaryForGuestsSerializer = (place: Place) =>
DreamSerializer(Place, place).attribute('id').delegatedAttribute('currentLocalizedText', 'title', {
openapi: 'string',
default: 'Untitled',
})

// A place without a currentLocalizedText or a currentLocalizedText with { title: null } will render:
// { id: 1234, title: 'Untitled' }

The default option provides a fallback value when either the target object is null/undefined OR when the delegated attribute itself is null/undefined.

Custom Attributes

customAttribute is used for arbitrary data transformation. It is especially useful for rendering or leveraging passthrough data into the serialized output. customAttribute always requires the openapi option.

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

See the passthrough setting documentation for details on how to set passthrough data on automatically rendered serializers.

flatten

When flatten: true is included, the attributes of the returned object are flattened into the serialized results.

const UserSerializer = (user: User) =>
DreamSerializer(User, user)
.attribute('id')
.customAttribute('profileInfo', () => ({ age: 30, city: 'Metropolis' }), {
flatten: true,
openapi: {
age: { type: 'integer' },
city: { type: 'string' },
},
})

// renders { id: 1, age: 30, city: 'Metropolis' }