The compiler is used to convert an array of schema nodes to a function with imperative JavaScript code that can be executed to validate a data object. The compiler operates at the low-level and does not offer any JavaScript API for creating the schema (see vinejs for user-land APIs).
Ajv performs better in some instances because Ajv holds a reference to the input data object and does not create a new output object. Though Ajv behavior results in slightly better performance, it comes at the cost of sharing the same pointers between the input and output objects, and hence mutating one will mutate the other as well.
Schema refers to an object or an array of objects that the compiler takes as an input to generate JavaScript output. Following is an example of an object
schema node with username
property.
{
type: 'root',
schema: {
type: 'object',
fieldName: '',
propertyName: '',
bail: true,
allowNull: false,
isOptional: false,
allowUnknownProperties: false,
properties: [
{
type: 'literal',
fieldName: 'username',
propertyName: 'userName',
transformFnId: 'ref://1',
parseFnId: 'ref://2',
bail: true,
allowNull: false,
isOptional: false,
validations: [
{
ruleFnId: 'ref://3',
isAsync: false,
implicit: false,
}
]
}
],
groups: [],
}
}
Since, we pre-compile the schema to a string, we cannot serialize certain JavaScript datatypes like functions or complex objects. Therefore, we use a refs
system, which works as follows.
- When you define a function or a complex data type inside your schema, we will generate a unique ref id for it.
- The compiler will receive the ref id (starting with
ref://
). - At the time of validations, we will pass the refs key-value pair. The key will be ref id and the value will be the complex data type and the compiled output will lookup the ref value based upon the ref id.
Consider the following simplified flow of using refs.
Following is the list of supported schema types.
literal
: Literal refers to any value that does not have children schema nodes. For example:number
,string
,boolean
,dates
, and so on. Custom data types likefile
can be a literal schema type.object
: Object refers to a JavaScript Object data type. An object may have children schema nodes as well.array
: Array refers to a JavaScript Array data type. An array may also define the schema node for the array elements.tuple
: Tuple refers to a JavaScript Array data type. A tuple can define a separate schema for each array element.union
: A union is a collection of conditions + schema. If the condition istrue
at runtime, the associated schema will validate the field.record
: Record refers to a JavaScript Object data type with unknown keys. Each element of a record must have the same type.
There are moving parts inside the schema nodes. These moving parts generate different outputs based on their state.
Standalone moving parts refers to conditions that act individually and does not get impacted if other moving parts are enabled or disabled.
-
parseFnId
: A function to convert the input value before the validation lifecycle of the input begins. The function receives the exact value of the input, so consider the value asunknown
. -
transformFnId
: A function to convert the output value after all the validations of the input field are over.- The function can only be defined for the
literal
schema type. - The function is invoked only when the value is written to the output. Any conditions that do not write value to the output will also skip calling the transform function.
- The function can only be defined for the
-
allowUnknownProperties
: When enabled, the flag will keep all the properties of an object or an array. The children properties with schema are still validated and replaced, but other properties are deeply copied from the source to the output. -
bail
: When enabled, the flag will stop validating the field after the first error. In the case ofarrays
andobjects
, the validation of children nodes will also be skipped.
Flags that behave differently when used together.
-
isOptional
: Mark the field under validation as optional. It means the field value can beundefined
ornull
. -
allowNull
: Mark the field under validation to benull
, but not undefined. However, when bothisOptional
andallowNull
flags are used, the undefined values will also be allowed.The
null
values are written to the output whenallowNull
flag is enabled.
The validations are functions executed one after the other in the same sequence as they are registered in the schema. Following is the default behavior of validations.
- Validation functions are not executed if the value is
null
orundefined
even when the field is marked as optional. - You may use the
implicit
flag on the validation rule to make the validation run fornull
orundefined
values as well. - When the
bail
mode is enabled for the field, the next validation will not run if the previous one fails. There is no way for rules to bypass this behavior.
The validation rules are not executed by default when the field's value is null
or undefined
. However, the implicit rules can bypass this check by enabling the implicit
flag inside the schema object.
If the value of a field is null
or undefined
it will not be written to the output. However, the null
values are written to the output when allowNull
flag is enabled.