prisma-next-contract

Edit the Prisma Next data contract — add models, fields, relations, indexes, enums, value objects (composite types), type aliases, namespaces (Postgres schemas), cross-contract foreign keys (cross-space FK), polymorphic types (`@@discriminator` / `@@base`), use extension namespaces (`pgvector.Vector

By prisma · 1,310 installs

npx skills add prisma/prisma-next --skill prisma-next-contract

Source repository · Upstream listing

Prisma Next — Contract Authoring Edit your data contract. Prisma handles the rest. The data contract is the single source of truth for your data layer. You edit a contract source — contract.prisma (PSL, the canonical surface) or contract.ts (TypeScript builder) — and the framework derives types, migrations, and runtime configuration from it. The three step user model: 1. You edit your data contract. 2. The system plans the migrations for you. ( prisma next migrations ) 3. If you need data migrations, you edit migration.ts and execute it. ( prisma next migrations ) Behind step 1 the agent runs prisma next contract emit after every contract edit (or installs the Vite plugin so the bundler runs it on save — see prisma next build ). Emit reads the contract source through the provider the façade picks based on the file extension of contract: in prisma next.config.ts , then writes two artefacts colocated with the source: contract.json — the canonical, content hashed Contract IR. Read by the planner, the runtime, and db verify . contract.d.ts — the precise TypeScript types the runtime + lanes propagate when you import Contract from it. Both files are emitted artefacts . Edit the source; never the JSON or .d.ts . When to Use User wants to add, change, or remove a model / field / relation. User wants to add an index, unique constraint, enum, or value object (composite type). User wants to add a namespace block (Postgres schema) or a cross contract foreign key. User wants to set @@control on a model or configure defaultControlPolicy . User wants to use a custom type from an extension ( pgvector.Vector(length: 1536) , cipherstash.EncryptedString({...}) ). User wants to install or configure an extension via extensions: [...] in prisma next.config.ts , including @prisma next/extension supabase . User is migrating between authoring sources (PSL ↔ TypeScript builder). User received PN CLI 4002 , PN CLI 4003 , or PN CLI 4011 from contract emit . User mentions: schema, fields, models, attributes, prisma schema, PSL, contract.prisma, contract.ts, contract.json, contract.d.ts, contract emit, façade imports, @prisma next/postgres/config , @prisma next/postgres/contract builder , extensions, pgvector, cipherstash, postgis, paradedb, supabase, namespaces, cross space FK, @@control , enums, value objects, validations, callbacks, soft delete, paranoid, scopes . (The last cluster routes to What Prisma Next doesn't do yet below.) When Not to Use User wants to apply a contract change to the DB → prisma next migrations . User wants to write a query against the contract → prisma next queries . User wants to wire db.ts (runtime entry point, middleware, env config) → prisma next runtime . User wants the Vite / bundler integration → prisma next build . User wants to set up Prisma Next for the first time → prisma next quickstart . User wants a deeper read of a single structured error envelope → prisma next debug . User wants to file a missing feature request → prisma next feedback . Key Concepts The @prisma next/<target façade is the only surface user authored code imports from. For a Postgres app: @prisma next/postgres/config , @prisma next/postgres/contract builder , @prisma next/postgres/control , @prisma next/postgres/runtime . Mongo has the same layout ( @prisma next/mongo/config , @prisma next/mongo/contract builder , @prisma next/mongo/runtime ). Each extension publishes its own façade — @prisma next/extension pgvector/control , @prisma next/extension postgis/control , @prisma next/extension paradedb/control . Never reach into @prisma next/cli/ , @prisma next/family , @prisma next/target , @prisma next/adapter , @prisma next/driver , or @prisma next/sql contract from user code. The façade bakes the family / target / adapter / driver wiring in. See Common Pitfalls 4. Contract source. A file the framework reads and lowers to the canonical Contract IR. Two flavours, both first class: contract.prisma (PSL) — schema flavoured DSL. Canonical for typical apps and brownfield Prisma users. Wired by contract: './<path /contract.prisma' — the defineConfig façade detects the .prisma extension and routes through the PSL provider. contract.ts (TypeScript builder) — programmatic authoring with defineContract({...}, ({ field, model, rel, type }) = ({...})) from @prisma next/postgres/contract builder (or @prisma next/mongo/contract builder ). Wired by contract: './<path /contract.ts' — the façade detects the .ts extension and routes through the TS provider. Use when you need programmatic composition (per tenant variants, generated fields) or constructs PSL doesn't yet express (e.g. registering a parameterised extension type — see pgvector's contract). prisma next.config.ts . Wires the contract source, the database connection, the migrations directory, and any installed extensions. Use defineConfig({...}) from @prisma next/postgres/config (or @prisma next/mongo/config ). The four fields the façade accepts: contract (path string — .prisma or .ts ), db ( { connection?: string } ), extensions (array of control descriptors), migrations ( { dir?: string } ). The output path for contract.json is auto derived from contract (e.g. ./src/prisma/contract.prisma → ./src/prisma/contract.json ). Emit pipeline. prisma next contract emit config <path ? reads prisma next.config.ts , calls the provider the façade picked, validates the resulting Contract, then atomically writes contract.json + contract.d.ts colocated with the source. Extension namespaces. Extensions contribute namespaced constructors ( pgvector.Vector(length: 1536) , cipherstash.EncryptedString({equality: true}) ) and helper presets. Install them by adding the descriptor to two places — both fields are named extensions , but the two surfaces consume two different descriptor types and shapes: In the config (façade and core): extensions: [pgvector] — array of control descriptors imported from @prisma next/extension <name /control . In the TS builder's defineContract (only when authoring contract.ts ): extensions: { pgvector } — record of pack descriptors imported from @prisma next/extension <name /pack . Contract space. Every package that emits a contract owns its own contract space — a prisma next.config.ts at package root, a contract source, the colocated emitted artefacts, and a migrations/ directory. There are two intentional on disk layouts , picked by whether the contract space is the consuming application or a contract space package (an extension, an internal aggregate root package, etc.): Application layout (what you use when building an app ). prisma next.config.ts at repo root; src/prisma/contract.{prisma,ts} ; src/prisma/contract.{json,d.ts} colocated; src/prisma/db.ts colocated; migrations under migrations/app/<timestamp <slug / . The app/ segment is the consuming application's space id; extension space ids land in sibling migrations/<extension space id / directories that the extension packages manage. This is what examples/prisma next demo uses. prisma next init currently scaffolds something different ( prisma/... at repo root) — that's a defect (TML 2532); the canonical layout is what every command actually expects to see. Contract space package layout (what you use when publishing a contract space package — extensions, internal monorepo packages). prisma next.config.ts at package root; src/contract.{prisma,ts} directly (no prisma/ subdir); src/contract.{json,d.ts} colocated; migrations/<timestamp <slug / directly under migrations/ (no <space id segment — the package is a single space). Documented in .cursor/rules/contract space package layout.mdc and ADR 212. Both layouts let defineConfig 's contract: path point at the source; the framework derives everything else (emit output, migration root) from there. Pick the layout that matches what you're building and stick with it — don't mix. Diagnostic codes you route on prisma next contract emit surfaces structured errors with stable codes; branch on code rather than message text. Code Meaning Next move PN CLI 4002 Contract configuration missing contract not set in prisma next.config.ts . Add contract: './src/prisma/contract.prisma' (app layout) or './src/contract.prisma' (contract space package layout) — likewise for .ts sources — to defineConfig({...}) from @prisma next/postgres/config . PN CLI 4003 Contract validation failed Source loaded but the Contract IR failed structural validation. Read meta.diagnostics / meta.issues for the offending model/field, fix the source, re emit. PN CLI 4011 Missing extension packs in config The contract uses a namespaced constructor (e.g. pgvector.Vector(...) ) but extensions in the config does not list a matching descriptor. meta.missingExtensions names them. Install the package, import its control descriptor ( import pgvector from '@prisma next/extension pgvector/control' ), add it to extensions: [...] in prisma next.config.ts . Workflow — Read the contract source of truth The concept: every contract change starts by locating the source file. The config is authoritative — read prisma next.config.ts , find the contract: field (a path string under the façade), and open the file it points at. The same field tells you the installed extensions: [...] . If contract: ends in .prisma , the source is PSL; if it ends in .ts , the source is the TS builder. If prisma next.config.ts is missing, route to prisma next quickstart . Workflow — Edit a model / field / relation (PSL) The concept: PSL models lower to tables (or collections, on Mongo); fields lower to columns; @relation(...) declares the FK side. Add the relation only on the owning side — the framework derives the back reference automatically. Then run pnpm prisma next contract emit (or rely on the Vite plugin — see prisma next build ). Specify cascade behaviour explicitly with onDelete / onUpdate ; the default is Restrict . @@index also accepts expression: (instead of a fields list), where: (partial index predicate), unique: , type: / options: (target registered access method), and name: xor map: : name: declares a managed index (physical name <name <8 hex hash , renames plan as ALTER INDEX … RENAME ); map: adopts an exact physical name verbatim (for infer captured objects — combining it with a SQL body warns, because drift detection byte compares the authored text against Postgres's reprint). An expression: requires name: or map: . The TS builder mirrors this via constraints.index([cols.x], {...}) / constraints.index({ expression, ... }) — see packages/2 sql/2 authoring/contract ts/README.md . PSL alias surface for repeated types lives in a top level types {} block: Note: scalar lists (e.g. String[] ) and implicit Prisma ORM many to many (list nav on both sides without a join model) are rejected by the SQL interpreter — use a join model. Composite/embeddable types ( type Address { ... } with address Address on a model) are supported: the interpreter lowers them to valueObjects in the domain and stores them as jsonb columns. See Workflow — Value objects below. Workflow — Edit a model / field / relation (TS builder) The concept: same model, different authoring surface. The façade re exports defineContract , field , model , rel , plus the family / target packs as default exports of @prisma next/postgres/family and @prisma next/postgres/target . Use the callback overload ( defineContract({...}, ({ field, model, rel, type }) = ({...})) ) to get the higher level helpers ( field.text() , field.id.uuidv7String() , field.temporal.createdAt() , t