Getting Started
ivo for TypeScript lets you define a schema with a fluent field builder, then derive a model with
create, update, and delete methods.
Installation
npm i ivo
Defining a schema
A schema is created with new Schema((b) => ...) where b is a FieldBuilder. Fields are declared
with b.required(...), b.lax(...), b.constant(...), b.dependent(...), or b.virtual(...),
then passed to b.field(...).
Model methods
The model returned by schema.getModel() exposes async methods:
| Method | Description |
|---|---|
create | Creates a new instance from a partial input. |
update | Applies a partial update to an existing instance. |
delete | Triggers all registered onDelete listeners on the provided entity. |
Creating an entity
Unknown properties and output-only properties (constant, dependent, timestamps) are ignored
automatically.
const { data, error } = await UserModel.create({
email: "john.doe@mail.com",
id: 5, // ignored because 'id' is constant
name: "John Doe", // ignored because it is not on the schema
username: "john_doe",
updatedAt: new Date(), // ignored because it is a timestamp
usernameLastUpdatedAt: new Date(), // ignored because it is dependent
});
if (error) return handleError(error);
console.log(data);
// {
// id: '...',
// createdAt: Date,
// email: 'john.doe@mail.com',
// phoneNumber: null,
// updatedAt: null,
// username: 'john_doe',
// usernameLastUpdatedAt: null
// }
Updating an entity
const user = await usersDb.findByID(id);
if (!user) return handleError({ message: "User not found" });
const { data, error } = await UserModel.update(user, {
usernameLastUpdatedAt: new Date(), // dependent -> ignored
id: 75, // constant -> ignored
age: 34, // not on schema -> ignored
username: "johndoe",
});
if (error) return handleError(error);
console.log(data);
// {
// updatedAt: Date,
// username: 'johndoe',
// usernameLastUpdatedAt: Date
// }
Field categories
Other topics
Schema options
The second argument to new Schema accepts options:
new Schema((b) => ..., {
equalityDepth: 1,
sanitizeError: (payload, ctxOptions) => payload,
onDelete: [listener],
onSuccess: [listener],
postValidate: { fields: ['email', 'phoneNumber'], validator: ... },
ignore: { fields: ['secret'], handler: () => true },
ignoreUpdate: { fields: ['email'], handler: () => true },
required: { fields: ['email', 'phoneNumber'], handler: ... },
timestamps: true,
});
| Option | Description |
|---|---|
equalityDepth | Nesting depth used to compare values during updates (default: 1). |
sanitizeError | Transform the error payload before it is returned. |
onDelete | Global listener(s) invoked by model.delete. |
onSuccess | Global listener(s) invoked after a successful create/update. |
postValidate | Cross-field validation configuration (fields + validator). |
ignore | Ignore input fields when the handler returns true. |
ignoreUpdate | Ignore update values for the listed fields when the handler returns true. |
required | Cross-field required constraint (fields + handler). |
timestamps | Enable createdAt/updatedAt (boolean or { createdAt?, updatedAt? }). |
See Life cycles and Validators for more.
Extending a schema
Use .extend() to create a new schema that inherits fields and options from the parent:
const AdminSchema = userSchema.extend<AdminInput, AdminOutput>(
(b) => b.field(b.required("role").validate(validateRole)),
{ useParentOptions: true },
);
Set useParentOptions: false to drop parent options and start from the provided options only.
Fields can be removed with the remove option.