Skip to main content
Version: 1.9.0

Lax

Lax properties are optional fields that carry a default value. They may be provided during creation or updates, and they support validation, allowed values, and lifecycle hooks.

Defining a lax field

A lax field is defined with a default value. It is not strictly required and does not use constant, dependsOn, or virtual.

import { Schema, type SetterFnData } from "ivo";

type Input = {
favoriteColor: string;
userName: string;
};

type Output = {
favoriteColor: string;
userName: string;
};

type SetterData = SetterFnData<Input, Output, CtxOptions>;

const userSchema = new Schema({
favoriteColor: { default: "indigo", validator: validateColor },
userName: {
default: ({ ctx }: SetterData) => "",
validator: validateUserName,
},
});

Note: If the default function throws, the property's value becomes null.

Default values

The default rule provides a fallback value. It can be a literal value or a function that returns a value at runtime.

const schema = new Schema({
status: { default: "pending", validator: validateStatus },
});

Allowed values

Use allow to restrict the values accepted by a lax field. If a validator is also provided, the value is first checked against the allowed list before being passed to the validator.

const schema = new Schema({
role: { default: "user", allow: ["admin", "moderator", "user"] },
});

For a custom error message, use the object form:

const schema = new Schema({
role: {
default: "user",
allow: {
values: ["admin", "moderator", "user"],
error: "Invalid role provided",
},
},
});

The error can be a string, an InputFieldError, or a function that receives the provided value and allowed values:

type NotAllowedError =
| string
| InputFieldError
| ((valueProvided: any, allowedValues: any[]) => string | InputFieldError);

Note: If the NotAllowedError function throws, the default error message is used.

Readonly

Lax fields support two readonly modes via the readonly rule.

readonly: 'lax'

The field is not required at creation or during updates. Once its value differs from the default, it no longer accepts updates.

const schema = new Schema({
receiptNumber: {
default: null,
readonly: "lax",
validator: validateReceipt,
},
});

readonly: true

The field is required at creation and never allows updates. Combine it with shouldInit: false to allow a single update later.

const schema = new Schema({
isComplete: {
default: false,
readonly: true,
shouldInit: false,
validator: validateBoolean,
},
});

Note: Readonly lax fields can be conditionally required, but they cannot be strictly required (required: true).

Validation

Lax fields can have one primary validator and one secondary reValidator. See the Validators page for details.

const schema = new Schema({
email: {
default: null,
validator: validateEmail,
reValidator: makeSureEmailIsUnique,
},
});

Ignore rules

Control when input is ignored with ignore, ignoreInit, or ignoreUpdate:

const schema = new Schema({
promoCode: {
default: null,
validator: validatePromoCode,
ignore({ ctx }) {
return ctx.isGuest;
},
},
});

Lifecycle hooks

Lax fields support onDelete, onFailure, and onSuccess handlers.

API summary

OptionTypeRequiredDescription
defaultany | functionYesDefault value or resolver for the property.
allowany[] | objectNoAllowed values and optional custom error.
readonlytrue | 'lax'NoMakes the field immutable; 'lax' locks once changed from default.
validatorfunctionNoPrimary validator.
reValidatorfunctionNoSecondary validator that runs after the primary validator.
requiredfunctionNoConditionally requires the field.
ignorefunctionNoDetermines whether input should be ignored.
ignoreInittrueNoIgnores the field during creation.
ignoreUpdatetrueNoIgnores the field during updates.
onDeletefunction | function[]NoHandler(s) invoked when the model instance is deleted.
onFailurefunction | function[]NoHandler(s) invoked after a failed create or update operation.
onSuccessfunction | function[]NoHandler(s) invoked after a successful create or update operation.