Skip to main content

Schema rules

While less common when using Vest, sometimes it might be useful to validate a value against a schema. Vest comes with some schema validation rules that are handy for data-shape validation.

To use it, simply import these rules in your project:

import 'vest/enforce/schema';

These rules will then become available in enforce:

enforce.shape() - Lean schema validation.

enforce.shape() validates the structure of an object.

enforce({
firstName: 'Rick',
lastName: 'Sanchez',
age: 70,
}).shape({
firstName: enforce.isString(),
lastName: enforce.isString(),
age: enforce.isNumber(),
});

You may also chain your validation rules:

enforce({
age: 22,
}).shape({
age: enforce.isNumber().isBetween(0, 150),
});

You may also nest calls to shape in order to validate a deeply nested object.

enforce({
user: {
name: {
first: 'Joseph',
last: 'Weil',
},
},
}).shape({
user: enforce.shape({
name: enforce.shape({
first: enforce.isString(),
last: enforce.isString(),
}),
}),
});

enforce.optional() - nullable values

In regular cases, a missing value would cause a validation failure. To prevent that from happening, mark your optional keys with enforce.optional.

enforce.optional will pass validations of a key that's either not defined, undefined or null.

enforce.optional takes as its arguments all the rules that their value must pass.

enforce({
firstName: 'Rick',
lastName: 'Sanchez',
}).shape({
firstName: enforce.isString(),
middleName: enforce.optional(enforce.isString()),
lastName: enforce.isString(),
});

partial() - allows supplying a subset of keys

When supplying a "shape" or a "loose" matcher, enforce requires at least the keys that are specified by the matcher, unless you manually wrap them with "optional". partial is a shorthand for applyong the optional modifier on all shape object keys. By wrapping the input of a matcher with partial, you can supply a subset of the keys that are required as if you had used optional on each key.

To be used, partial needs to be imported directly from vest/enforce/schema:

import { partial } from 'vest/enforce/schema';
enforce({}).shape(
partial({
firstName: enforce.isString(),
lastName: enforce.isString(),
})
);

This won't throw because all the fields are now treated as optional.

enforce.loose() - loose shape matching

By default, shape will treat excess keys in your data object as validation errors. If you wish to allow support for excess keys in your object's shape, you can use enforce.loose() which is a shorthand to enforce.shape(data, shape, { loose: true }).

enforce({ name: 'Laura', code: 'x23' }).shape({ name: enforce.isString() });
// 🚨 This will throw an error because `code` is not defined in the shape
enforce({ name: 'Laura', code: 'x23' }).loose({ name: enforce.isString() });
// ✅ This will pass with `code` not being validated

enforce.isArrayOf() - array shape matching

enforce.isArrayOf can be used to determine the allowed types and values within an array. It will run against each element in the array, and will only pass if all items meet at least one of the validation rules.

enforce([1, 2, 'hello!']).isArrayOf(enforce.isString(), enforce.isNumber());

You can also combine isArrayOf with other rules to validate other array properties:

enforce(someArrayValue)
.isArrayOf(enforce.isString(), enforce.isNumber().lessThan(3))
.longerThan(2);

And as part of shape:

enforce({ data: [1, 2, 3] }).shape({
data: enforce.isArrayOf(enforce.isNumber()),
});