Skip to main content

Record

Defines a different kind of map attribute. Records differ from maps as they can have a non-explicit (and potentially infinite) range of keys, but have a single value type:

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

const pokeTypeSchema = string().enum('fire', ...)

const pokemonSchema = schema({
...
weaknesses: record(pokeTypeSchema, number())
})

type FormattedPokemon = FormattedItem<typeof PokemonEntity>
// => {
// ...
// weaknesses: {
// [key in PokeType]?: number
// }
// }

Record elements can have any type. However, they must respect some constraints:

  • They cannot be optional or always required
  • They cannot be hidden or key (tagging the record itself as key is enough)
  • They cannot have default or links
// ❌ Raises a type AND a run-time error
const strRecord = record(string(), string().optional())
const strRecord = record(string(), string().hidden())
const strRecord = record(string(), string().key())
const strRecord = record(string(), string().default('foo'))

Record keys share the same constraints and must be of type string.

Options

.required()

string | undefined

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

  • 'atLeastOnce' (default): Required (starting value)
  • 'always': Always required (including updates)
  • 'never': Optional
// Equivalent
const weaknessesSchema = record(
string().enum('fire', ...),
number()
)
const weaknessesSchema = record(
string().enum('fire', ...),
number()
).required()
const weaknessesSchema = record(
string().enum('fire', ...),
number(),
// Options can be provided as 3rd argument
{ required: 'atLeastOnce' }
)

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

.hidden()

boolean | undefined

Skips the attribute when formatting items:

const weaknessesSchema = record(
string().enum('fire', ...),
number()
).hidden()
const weaknessesSchema = record(..., { 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 idsSchema = record(string(), string()).key()
const idsSchema = record(..., {
key: true,
required: 'always'
})

.savedAs(...)

string

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

const weaknessesSchema = record(
string().enum('fire', ...),
number()
).savedAs('w')
const weaknessesSchema = record(..., { savedAs: 'w' })

.default(...)

ValueOrGetter<ATTRIBUTES>

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

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

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

.link<Schema>(...)

Link<SCHEMA, 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: record(string(), string()).link<
typeof prevSchema
>(
// 🙌 Correctly typed!
({ name }) => {
const [firstName, lastName] = name.split(' ')
return { firstName, lastName }
}
)
}))

.validate(...)

Validator<ATTRIBUTES>

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

Examples
const nonEmptyRecordSchema = record(
string(),
string()
).validate(input => Object.keys(input).length > 0)
// 👇 Similar to
const nonEmptyRecordSchema = record(
string(),
string()
).putValidate(input => Object.keys(input).length > 0)
// 👇 ...or
const nonEmptyRecordSchema = record(string(), string(), {
validators: {
key: undefined,
put: input => input.length > 0,
update: undefined
}
})