Skip to main content

Defaults & Links

Defaults

All attribute types support providing default values. There are three kinds of defaults:

  • putDefault: Applied on put actions (e.g. PutItemCommand)
  • updateDefault: Applied on update actions (e.g. UpdateItemCommand)
  • keyDefault: Overrides other defaults on key attributes (ignored otherwise)

The default method is a shorthand that acts as keyDefault on key attributes and putDefault otherwise.

info

☝️ In order for the .default(...) shorthand to work properly on key attributes, make sure to use it after calling .key().

Here are some simple examples:

const nameSchema = string().default('Pikachu')

// 🙌 Getters also work!
const createdSchema = string().default(() =>
new Date().toISOString()
)

const updatesCountSchema = number()
.putDefault(1)
.updateDefault(() => $add(1))

In DynamoDB, it is frequent to infer attribute values from other attributes (e.g. for secondary indexes). In DynamoDB-Toolbox, this is called linking attributes.

In TypeScript, the difficulty is that it's impossible to pass the shape of the parent schema to the .default method, and thus efficiently type the link arguments:

const pokemonSchema = schema({
...
level: number(),
levelPlusOne: number().default(
// ❌ Cannot infer the type
input => input.level + 1
)
})

The solution is to make good use of the .and(...) method (see Updating Schemas) and build the schema in two steps:

const basePokemonSchema = schema({
...,
level: number()
})

const completePokemonSchema = basePokemonSchema.and({
levelPlusOne: number().link<typeof basePokemonSchema>(
// 🙌 Correctly typed!
({ level }) => level + 1
)
})

// 👇 OR we can use the getter form:
const pokemonSchema = schema({
...
level: number()
}).and(prevSchema => ({
levelPlusOne: number().link<typeof prevSchema>(
// 🙌 Correctly typed!
({ level }) => level + 1
)
}))
note

This is only required if you need type inference. In vanilla JS, links can be used directly in the original schema.

Similarly to defaults, links come in three modes:

  • putLink: Applied on put actions (e.g. PutItemCommand)
  • updateLink: Applied on update actions (e.g. UpdateItemCommand)
  • keyLink: Overrides other links on key attributes (ignored otherwise)

The link method is a shorthand that acts as keyLink on key attributes and putLink otherwise.

info

☝️ In order for the .link(...) shorthand to work properly on key attributes, make sure to use it after calling .key().

Note that defaults are computed before links, so you can safely use defaults within links (see the Parser action for more details).

Update Syntax

If you use TypeScript, you may notice that the updateLink input type can be quite complex. This is to reflect that extended syntax (e.g. $add, $get etc.) is also passed to updateLink:

const pokemonSchema = schema({
...
level: number()
}).and(prevSchema => ({
levelPlusOne: number().updateLink<typeof prevSchema>(
({ level }) => {
if (level === undefined) {
return undefined
}

// ❌ `level` may be `$add(1)`, `$get('otherAttr')` etc.
return level + 1
}
)
}))

If you want to leverage extended syntax within the link, check the UpdateItemCommand docs for more details. If you don't, you can escape it with the isExtension type guard:

import { isExtension } from 'dynamodb-toolbox/entity/actions/update/symbols'

const pokemonSchema = schema({
...
level: number()
}).and(prevSchema => ({
levelPlusOne: number().updateLink<typeof prevSchema>(
({ level }) => {
if (level === undefined || isExtension(level)) {
return undefined
}

// 🙌 `level` is a number
return level + 1
}
)
}))