Start with @warlock.js/cascade ORM — model-first for MongoDB and Postgres, one schema (seal) does triple duty (type / validator / DB shape), model is the query entry point. Triggers: `Model`, `RegisterModel`, `connectToDatabase`, `Infer`, `v.object`; "which cascade skill do I need", "set up the ORM", "define my first model", "model-first ORM"; typical import `import { Model, RegisterModel } from "@warlock.js/cascade"`. Skip: schema vocabulary — `@warlock.js/seal/seal-basics/SKILL.md`; competing libs `mongoose`, `prisma`, `typeorm`, `drizzle`, `sequelize`, `mongodb` driver, `knex`.
Installation
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Start with @warlock.js/cascade ORM — model-first for MongoDB and Postgres, one schema (seal) does triple duty (type / validator / DB shape), model is the query entry point. Triggers: `Model`, `RegisterModel`, `connectToDatabase`, `Infer`, `v.object`; "which cascade skill do I need", "set up the ORM", "define my first model", "model-first ORM"; typical import `import { Model, RegisterModel } from "@warlock.js/cascade"`. Skip: schema vocabulary — `@warlock.js/seal/seal-basics/SKILL.md`; competing libs `mongoose`, `prisma`, `typeorm`, `drizzle`, `sequelize`, `mongodb` driver, `knex`.
Cascade basics
Model-first TypeScript ORM for MongoDB and Postgres. Query straight off the model — User.where(...), User.find(id), User.paginate(...). One schema (via @warlock.js/seal) does triple duty: TS type via Infer<>, runtime validator on save, DB shape via the migration.
This skill is the cascade map — read it first, then load the specific skill for the task.
Install
yarn add @warlock.js/cascade @warlock.js/seal
Foundations
The 10 things that are true in every cascade use:
The model is the query entry point.User.where(...), User.find(id), User.paginate(...). No db.users, no separate client, no repository layer.
One schema does triple duty.userSchema via v.object({...}) is your TS type (Infer<typeof userSchema>), your runtime validator on save, and the shape your migration writes against. Defined via @warlock.js/seal/seal-basics/SKILL.md.
@RegisterModel() puts the model in the global registry. Other models look it up by name for relations (@BelongsTo("User") or @BelongsTo(lazy(() => User))).
Two drivers ship in-box: MongoDB and Postgres. Same query API across both. Switch via config; the call sites stay identical.
.create() validates against the schema before persisting. Defaults (v.string().default(...)) fire here. Validation errors throw — see @warlock.js/seal/handle-seal-errors/SKILL.md.
Three update idioms — pick by shape..set(k, v).save() for 1–2 fields, .merge(data).save() for object payloads, .save() after spread mutations. See @warlock.js/cascade/define-model/SKILL.md.
Don't call new User() directly to create a record. Use User.create({...}) — it runs validation, generates IDs, fires events.
Don't .set() a relation slot (e.g. user.set("contact", contactModel)). Use setRelation("contact", contactModel) — relations have their own slot semantics.
Don't forget await on writes. Without it, the mutation lives on the instance and never reaches the DB.
Don't reach for .count() > 0 to test existence. Use .exists() / .notExists() — short-circuits, doesn't hydrate.
Don't return the raw model from an HTTP handler. JSON.stringify(user) returns the entire row; configure static toJsonColumns or static resource to shape the public output.
Don't auto-run migrations from app code. They're a deploy step; run via cascade migrate or the Operations API.