Skip to main content

Map

Defines a map attribute, i.e. a finite list of key-value pairs. Child attributes can have any type:

import { map } from 'dynamodb-toolbox/attributes/map'

const pokemonSchema = schema({
...
name: map({
firstName: string(),
lastName: string()
})
})

type FormattedPokemon = FormattedItem<typeof PokemonEntity>
// => {
// ...
// name: {
// firstName: string
// lastName: string
// }
// }

const pokemonSchema = schema({
...
deepMagic: map({
does: map({
work: string().const('!'),
}),
}),
});

type FormattedPokemon = FormattedItem<typeof PokemonEntity>
// => {
// ...
// deepMagic: {
// does: {
// work: "!"
// }
// }
// }

Options

.required()

string | undefined

Tags the attribute as required (at root level or within other Maps). Possible values are:

  • 'atLeastOnce' (default): Required (starting value)
  • 'always': Always required (including updates)
  • 'never': Optional
// Equivalent
const nameSchema = map({
firstName: string(),
lastName: string()
})
const nameSchema = map({ ... }).required()
const nameSchema = map(
{ ... },
// Options can be provided as 2nd argument
{ required: 'atLeastOnce' }
)

// shorthand for `.required('never')`
const nameSchema = map({ ... }).optional()
const nameSchema = map({ ... }, { required: 'never' })

.hidden()

boolean | undefined

Skips the attribute when formatting items:

const nameSchema = map({
firstName: string(),
lastName: string()
}).hidden()
const nameSchema = map({ ... }, { hidden: true })

.key()

boolean | undefined

Tags the attribute as needed to compute the primary key:

// Note: The method also sets the `required` property to 'always'
// (it is often the case in practice, you can still use `.optional()` if needed)
const nameSchema = map({
firstName: string(),
lastName: string()
}).key()
const nameSchema = map({ ... }, {
key: true,
required: 'always'
})

Note that if child attributes are required to derive the primary key, you must also tag them as key:

const nameSchema = map({
// 👇 Required in get operations
firstName: string().key(),
// 👇 NOT required
lastName: string()
}).key()

.savedAs(...)

string

Renames the attribute during the transformation step (at root level or within other Maps):

const nameSchema = map({
firstName: string(),
lastName: string()
}).savedAs('n')
const nameSchema = map({ ... }, { savedAs: 'pt' })

.default(...)

ValueOrGetter<CHILD_ATTRIBUTES>

Specifies default values for the attribute. See Defaults and Links for more details:

Examples
const now = () => new Date().toISOString()

const timestampsSchema = map({
created: string(),
updated: string().optional()
})
.default(() => ({ created: now() }))
.updateDefault(() => ({ updated: now() }))
// 👇 Similar to
const timestampsSchema = map({ ... })
.putDefault(() => ({ created: now() }))
.updateDefault(() => ({ updated: now() }))
// 👇 ...or
const timestampsSchema = map({ ... }, {
defaults: {
key: undefined,
put: () => ({ created: now() }),
update: () => ({ updated: now() })
}
})

.link<Schema>(...)

Link<SCHEMA, CHILD_ATTRIBUTES>

Similar to .default(...) but allows deriving the default value from other attributes. See Defaults and Links for more details:

const pokemonSchema = schema({
name: string()
}).and(prevSchema => ({
parsedName: map({
firstName: string(),
lastName: string()
}).link<typeof prevSchema>(
// 🙌 Correctly typed!
({ name }) => {
const [firstName, lastName] = name.split(' ')
return { firstName, lastName }
}
)
}))

.validate(...)

Validator<CHILD_ATTRIBUTES>

Adds custom validation to the attribute. See Custom Validation for more details:

Examples
const nonEmptyMapSchema = map({
str: string().optional(),
num: number().optional()
}).validate(input => Object.keys(input).length > 0)
// 👇 Similar to
const nonEmptyMapSchema = map({
str: string().optional(),
num: number().optional()
}).putValidate(input => Object.keys(input).length > 0)
// 👇 ...or
const nonEmptyMapSchema = map(
{
str: string().optional(),
num: number().optional()
},
{
validators: {
key: undefined,
put: input => input.length > 0,
update: undefined
}
}
)