Custom Validation
All schema types support adding custom validation during the parsing step (see the Parser
action for more details).
There are three kinds of validators:
putValidate
: Applied on put actions (e.g.PutItemCommand
)updateValidate
: Applied on update actions (e.g.UpdateItemCommand
)keyValidate
: Overrides other validators on key schemas (ignored otherwise)
The validate
method is a shorthand that acts as keyValidate
on key schemas and putValidate
otherwise.
βοΈ In order for the .validate(...)
shorthand to work properly on key schemas, make sure to use it after calling .key()
.
Validatorsβ
A custom validator is a function that takes an input (validated by the schema) and returns a boolean
.
For instance, you can make sure that a string
has more than 3 characters like this:
const nameSchema = string().validate(
// π Types are correctly inferred!
name => name.length > 3
)
// β Raises a `parsing.customValidationFailed` error
nameSchema.build(Parser).parse('foo')
In case of invalid value, you can return a string
to provide more context through the error message:
const nameSchema = string().validate(name =>
name.length > 3 ? true : 'Provide a longer name'
)
nameSchema.build(Parser).parse('foo')
// => β Custom validation for attribute 'name' failed with message: Provide a longer name.
Finally, note that the schema itself is also passed to the validator:
import type { Schema } from 'dynamodb-toolbox/schema'
const validator = (input: unknown, attr: Schema) => {
... // custom validation here
}
const mySchema = item({
name: string().validate(validator)
})
Recursive Schemasβ
Validators are a great way to create recursive schemas:
import { Parser } from 'dynamodb-toolbox/schema/actions/parse'
const isValidBulletList = (bulletList: unknown): boolean =>
bulletListSchema.build(Parser).validate(bulletList)
const bulletListSchema = item({
title: string(),
subBulletList: any()
.optional()
.validate(isValidBulletList)
})
Actually, you can improve the performances of this code by instanciating a single Parser
:
π Show code
let bulletListParser:
| Parser<typeof bulletListSchema>
| undefined
const isValidBulletList = (
bulletList: unknown
): boolean => {
if (bulletListParser === undefined) {
bulletListParser = bulletListSchema.build(Parser)
}
return bulletListParser.validate(bulletList)
}
const bulletListSchema = item({
title: string(),
subBulletList: any()
.optional()
.validate(isValidBulletList)
})
In those cases, type inference only works partially as the subBulletList
property is inferred as unknown
.
However, a slight override of the inferred types gets you there:
import type { FormattedValue } from 'dynamodb-toolbox/schema/actions/format'
// π Works as intended!
type FormattedBulletList = FormattedValue<
typeof bulletListSchema
> & { subBulletList?: FormattedBulletList }