Skip to main content
Version: v2

Map

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

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

const fullNameSchema = map({
firstName: string(),
lastName: string()
})

type FullName = FormattedValue<typeof fullNameSchema>
// => {
// firstName: string
// lastName: string
// }

const deepMagic = map({
does: map({
work: string().const('!')
})
})

type DeepMagic = FormattedValue<typeof deepMagic>
// => { does: { work: "!" } }

Properties

.required()

string | undefined

Tags schema values 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

Omits schema values during formatting:

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

.key()

boolean | undefined

Tags schema values as a primary key attribute or linked to a primary key attribute:

// 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 schema values 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. 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({ ... }, {
putDefault: () => ({ created: now() }),
updateDefault: () => ({ 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 = item({
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. 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()
},
{ putValidator: input => Object.keys(input).length > 0 }
)

Methods

Map schemas can be used to build new schemas with the following methods:

and(...)

(attr: NEW_ATTR | (MapSchema<OLD_ATTR> => NEW_ATTR)) => MapSchema<OLD_ATTR & NEW_ATTR>

Produces a new map schema by extending the original schema with new attributes:

const extendedSchema = baseSchema.and({
newAttribute: string(),
...
})
info

In case of naming conflicts, new attributes override the previous ones.

The method also accepts functions that return new attributes. In this case, the previous schema is provided as an argument (which is particularly useful for building Links):

const extendedSchema = mySchema.and(prevSchema => ({
newAttribute: string(),
...
}))

pick(...)

(...attrNames: ATTR_NAMES[]) => MapSchema<Pick<ATTR, ATTR_NAMES>>

Produces a new map schema by picking only certain attributes from the original schema:

const picked = pokemonSchema.pick('name', 'pokemonLevel')

Due to the potential disruptive nature of this method on links, they are reset in the process:

const nameSchema = map({
firstName: string(),
lastName: string(),
completeName: string().link(({ firstName, lastName }) =>
[firstName, lastName].join(' ')
)
})

const picked = nameSchema.pick('lastName', 'completeName')

picked.attributes.completeName.props.putLink
// => undefined

omit(...)

(...attrNames: ATTR_NAMES[]) => MapSchema<Omit<ATTR, ATTR_NAMES>>

Produces a new map schema by omitting certain attributes out of the original schema:

const omitted = pokemonSchema.omit('name', 'pokemonLevel')

Due to the potential disruptive nature of this method on links, they are reset in the process:

const nameSchema = map({
firstName: string(),
lastName: string(),
completeName: string().link(({ firstName, lastName }) =>
[firstName, lastName].join(' ')
)
})

const omitted = nameSchema.omit('firstName')

omitted.attributes.completeName.props.putLink
// => undefined