@prisma/orm-mongo 8.0.0-rc.16-dev.7 → 8.0.0-rc.16-dev.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prisma/orm-mongo",
|
|
3
|
-
"version": "8.0.0-rc.16-dev.
|
|
3
|
+
"version": "8.0.0-rc.16-dev.9",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
"skills"
|
|
11
11
|
],
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@prisma/orm-family-mongo": "8.0.0-rc.16-dev.
|
|
14
|
-
"@prisma/orm-framework": "8.0.0-rc.16-dev.
|
|
15
|
-
"@prisma/orm-target-mongo": "8.0.0-rc.16-dev.
|
|
16
|
-
"@prisma/orm-toolchain": "8.0.0-rc.16-dev.
|
|
13
|
+
"@prisma/orm-family-mongo": "8.0.0-rc.16-dev.9",
|
|
14
|
+
"@prisma/orm-framework": "8.0.0-rc.16-dev.9",
|
|
15
|
+
"@prisma/orm-target-mongo": "8.0.0-rc.16-dev.9",
|
|
16
|
+
"@prisma/orm-toolchain": "8.0.0-rc.16-dev.9",
|
|
17
17
|
"mongodb-connection-string-url": "^7.0.1",
|
|
18
18
|
"pathe": "^2.0.3",
|
|
19
19
|
"tinyglobby": "^0.2.17"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@internal/mongo": "8.0.0-rc.16-dev.
|
|
23
|
-
"@repo/tsconfig": "8.0.0-rc.16-dev.
|
|
24
|
-
"@repo/tsdown": "8.0.0-rc.16-dev.
|
|
22
|
+
"@internal/mongo": "8.0.0-rc.16-dev.9",
|
|
23
|
+
"@repo/tsconfig": "8.0.0-rc.16-dev.9",
|
|
24
|
+
"@repo/tsdown": "8.0.0-rc.16-dev.9",
|
|
25
25
|
"tsdown": "0.22.14",
|
|
26
26
|
"typescript": "5.9.3"
|
|
27
27
|
},
|
package/skills/prisma-8/SKILL.md
CHANGED
|
@@ -15,7 +15,7 @@ description: >-
|
|
|
15
15
|
ORM 7 or earlier (schema.prisma + @prisma/client).
|
|
16
16
|
metadata:
|
|
17
17
|
library: '@prisma/orm-mongo'
|
|
18
|
-
library_version: '8.0.0-rc.16-dev.
|
|
18
|
+
library_version: '8.0.0-rc.16-dev.9'
|
|
19
19
|
version: '2026-09-12'
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -59,10 +59,10 @@ Open the reference whose triggers match the task. If more than one matches, open
|
|
|
59
59
|
| --- | --- | --- |
|
|
60
60
|
| Adopt / set up / first steps | [`references/quickstart.md`](references/quickstart.md) | new project, existing database, "what can I do with Prisma 8", "where do I start", "just ran createprisma", `npx create-prisma`, first steps, first query, `prisma orm init` greenfield setup, `contract infer` + `db sign` brownfield adoption, connect-write-read first arc, day-to-day commands (`contract emit`, `db init`, `db update`, `migration plan`, `db migrate`, `db schema`, `db verify`), flags `--target` / `--authoring` / `--schema-path` / `--probe-db` / `--output` |
|
|
61
61
|
| Edit the data contract | [`references/contract.md`](references/contract.md) | schema, models, fields, attributes, relations, indexes, enums, value objects (composite types), type aliases, namespaces (Postgres schemas), cross-contract foreign keys (cross-space FK), polymorphic types (`@@discriminator` / `@@base`), extension namespaces (`pgvector.Vector(...)`, `postgis.Geometry(...)`), `prisma.config.ts` / `definePrismaConfig` / `ormConfig`, `prisma contract emit`, PSL, `contract.prisma`, `contract.ts`, `contract.json`, `contract.d.ts`, `@internal/postgres/config`, `@internal/postgres/contract-builder`, `@internal/mongo/config`, `extensions:`, pgvector, postgis, paradedb, Temporal / `temporal-polyfill` / `RUNTIME.TEMPORAL_UNAVAILABLE`, `@@control`, control policy (managed / tolerated / external / observed), soft delete, validations, callbacks |
|
|
62
|
-
| Author migrations | [`references/migrations.md`](references/migrations.md) | `db update` vs `migration plan`, `db migrate`, `migration new`, `migration show`, `db update --dry-run`, `db verify`, `db sign`, data migration, `dataTransform`, placeholder sentinels in framework-rendered `migration.ts`, `MIGRATION.HASH_MISMATCH`, `MIGRATION.UNFILLED_PLACEHOLDER`,
|
|
62
|
+
| Author migrations | [`references/migrations.md`](references/migrations.md) | `db update` vs `migration plan`, `db migrate`, `migration new`, `migration show`, `db update --dry-run`, `db verify`, `db sign`, data migration, `dataTransform`, placeholder sentinels in framework-rendered `migration.ts`, `MIGRATION.HASH_MISMATCH`, `MIGRATION.UNFILLED_PLACEHOLDER`, data-loss questions (`--delete`, `--rename`, `--allow`, `CLI.CONSENT_REQUIRED`), schema drift |
|
|
63
63
|
| Migration graph, refs, plan origin | [`references/migration-model.md`](references/migration-model.md) | migration graph, refs, `migration ref set` / `list` / `delete`, the `db` ref, `--advance-ref`, `--no-advance-ref`, `migration plan --from`, `from: (baseline)` in plan output, greenfield / from-scratch plan, baseline, first migration before deploy (Composer / CD-managed databases), chaining migrations, retrofitting migrations onto an existing database, `MIGRATION.HASH_NOT_IN_GRAPH`, `MIGRATION.PATH_UNREACHABLE` at plan/chain time |
|
|
64
64
|
| Review migrations on deploy | [`references/migration-review.md`](references/migration-review.md) | "what migrations are going to run", "what runs on deploy / merge", merge conflict, diamond convergence, concurrent migrations, migration status, ref management for CI, staging / production environment refs, `MIGRATION.MARKER_NOT_IN_HISTORY`, `MIGRATION.MISSING_INVARIANTS`, `db migrate --show` |
|
|
65
|
-
| Write queries | [`references/queries.md`](references/queries.md) | query, where, select, project, orderBy, limit, offset, take, skip, include, lookup, first, all, count, aggregate, groupBy, create, update, delete, upsert, returning, transaction, `db.orm`, `db.sql`, `runtime.query(plan)` vs `runtime.execute(plan)`, `db.prepare` / prepared statements, streaming / `for await`, many-to-many `include` and nested `connect`, `and` / `or` / `not` from `@prisma/orm-postgres/orm-client`, `db.query.from(...)` (Mongo pipeline), namespace-aware accessors, `.all()` Thenable, single-use iterators (`RUNTIME.ITERATOR_CONSUMED`), target-declared aggregate types (`count`, integer `sum`, and integer `avg` are `number`; `count` and integer `sum` throw outside ±(2^53 − 1) rather than round, while `avg` is a fraction already and carries no guard; `countBigInt` / `sumBigInt` / `avgDecimal` are the lossless forms, `avgDecimal` on PostgreSQL only), custom collection class (`extends Collection`), `with` and
|
|
65
|
+
| Write queries | [`references/queries.md`](references/queries.md) | query, where, select, project, orderBy, limit, offset, take, skip, include, lookup, first, all, count, aggregate, groupBy, create, update, delete, upsert, returning, transaction, `db.orm`, `db.sql`, `runtime.query(plan)` vs `runtime.execute(plan)`, `db.prepare` / prepared statements, streaming / `for await`, many-to-many `include` and nested `connect`, `and` / `or` / `not` from `@prisma/orm-postgres/orm-client`, `db.query.from(...)` (Mongo pipeline), namespace-aware accessors, `.all()` Thenable, single-use iterators (`RUNTIME.ITERATOR_CONSUMED`), target-declared aggregate types (`count`, integer `sum`, and integer `avg` are `number`; `count` and integer `sum` throw outside ±(2^53 − 1) rather than round, while `avg` is a fraction already and carries no guard; `countBigInt` / `sumBigInt` / `avgDecimal` are the lossless forms, `avgDecimal` on PostgreSQL only), custom collection class (`extends Collection`), `with` and query fragments (`Fragment`, `Filtered`, `Ordered`), shared query fragments and scopes (`db.orm.fragment(fields, body)` for a filter on every model with given fields, `CodecField`, `db.orm.public.Post.fragment(...)` for a shared `select` / `include`, `orderByField` for an order or sort parameter from a request), drizzle-style, kysely-style. Naming types (§ *Naming model and result types*): "type of my model", "return type of a query", `ResultType`, `Scalars`, `Shape`, `models` / `Models` namespace in `contract.d.ts`, `Models.public_User`, Prisma 7 `Prisma.User` / `GetPayload` / `UserGetPayload` equivalents. Postgres/SQLite specifics: [`references/queries-postgres.md`](references/queries-postgres.md); Mongo specifics: [`references/queries-mongo.md`](references/queries-mongo.md) |
|
|
66
66
|
| Wire the runtime | [`references/runtime.md`](references/runtime.md) | `db.ts`, `postgres<Contract>(...)` / `sqlite<Contract>(...)` / `mongo<Contract>(...)` façades, middleware composition (lints, budgets, cache via `@prisma/orm-extension-middleware-cache`, custom `afterQuery` middleware, `@prisma/orm-postgres/family-runtime`), `DATABASE_URL`, `.env`, connection pool / `poolOptions`, dev vs prod config, transactions, read replicas, multi-database, script won't exit / hangs, `db.close` / `pool.end`, `await using` / `[Symbol.asyncDispose]` |
|
|
67
67
|
| Build-tool integration | [`references/build.md`](references/build.md) | Vite plugin (`@internal/vite-plugin-contract-emit`, Vite 7/8), `vite.config.ts`, contract emit on save, HMR / dev server, Next.js / Webpack / esbuild / Rollup / Turbopack (named gaps, not fabricated) |
|
|
68
68
|
| Supabase | [`references/supabase.md`](references/supabase.md) | `@internal/extension-supabase`, RLS, row level security, policies (`policy_select` / `policy_update` / `@@rls`, `auth.uid()`), role binding (`asUser(jwt)` / `asAnon()` / `asServiceRole()`), `auth.users`, cross-space FKs to `supabase:auth.AuthUser`, JWT / JWKS (`SUPABASE_JWKS_URL`, `SUPABASE_JWT_SECRET`), `SUPABASE.JWT_INVALID`, `SUPABASE.CONFIG_INVALID`, `RoleBoundDb`, session pooler |
|
|
@@ -96,7 +96,9 @@ The single source of truth: read the envelope, find the row by `code`, follow th
|
|
|
96
96
|
| `CONTRACT.TARGET_MISMATCH` | `db verify`, runtime startup | Contract target ≠ config target; align them (see `meta.expected` / `meta.actual`). |
|
|
97
97
|
| `CONTRACT.SCHEMA_VERIFICATION_FAILED` | `db verify`, `db sign` (both exit 4 with the finding) | Live schema does not satisfy the contract; `meta.issues` lists the drifted paths per `meta.space`. Run `db update` to reconcile, or adjust the contract. |
|
|
98
98
|
| `MIGRATION.RUNNER_FAILED` | `db migrate`, `db update`, `db init` | Wrapper for an unclassified apply failure; `why` and `meta` carry the detail. Reconcile the reported failure, then re-run. Previously applied migrations are preserved. |
|
|
99
|
-
| `
|
|
99
|
+
| `CLI.CONSENT_REQUIRED` | `migration plan` / `db update` when a plan would lose data (or, on `db update`, widen access) and nobody can answer (`--no-interactive`, CI, `--yes`) | `meta.unanswered[]` and `nextActions` list each question with its flags. Re-run with `--delete <subject>`, `--rename <subject>:<new name>` or `--allow <Model>` as each subject means. `--confirm` and `--yes` answer none of them. `db migrate` does not ask. |
|
|
100
|
+
| `CLI.CONSENT_UNUSED` | `migration plan` / `db update` | Every question was answered, and a `--delete` or `--allow` was left over: a typo, a subject the plan does not touch, or a `--confirm` (which consents to nothing). Fix or drop the flag. A typo next to a question nobody answered is reported as `CLI.CONSENT_REQUIRED` instead, which names the flag that matched nothing. On `db update --dry-run` the same mistake is `MIGRATION.STATEMENT_ANSWERS_NO_QUESTION`. |
|
|
101
|
+
| `MIGRATION.STATEMENT_DID_NOT_RESOLVE_LOSS` | A rename typed at the data-loss prompt | The rename still loses the subject's data. Answer with `delete`, or give a rename whose old name is the subject. |
|
|
100
102
|
| `MIGRATION.NO_INVARIANT_PATH` / `MIGRATION.UNKNOWN_INVARIANT` | `db migrate` | Concurrent-migration and invariant flows — `references/migration-review.md`. |
|
|
101
103
|
| `DRIVER.NOT_CONNECTED` with message `Runtime is closed` | A query, prepared statement or `db.runtime().connection()` that starts after `db.close()` and after the runtime was idle for one turn of the event loop, or after the `await using` scope that held a serverless connection ended | The work started after the runtime closed. Almost always it is a lazy read (`db.orm...all()`, `db.runtime().query(...)`) returned without `await`, which starts only when the caller awaits it, or work that waited on something other than the database first. Write `return await ...`, or move the call before `close()`. See `references/runtime.md` § *Workflow — Serverless and per-request runtimes*. |
|
|
102
104
|
| `await db.close()` or the end of an `await using` scope never settles in a test | A test that installed fake timers (`vi.useFakeTimers()`, Jest's modern fake timers) before the Prisma runtime module was imported | `close()` waits one turn of the event loop through a `setTimeout(0)` that the runtime module takes when it loads. Install fake timers after the imports, or advance the fake clock (`await vi.runAllTimersAsync()`) before awaiting the close. |
|
|
@@ -177,7 +177,7 @@ The whole job is one command:
|
|
|
177
177
|
|
|
178
178
|
`migration status --to staging --db $URL` is what a human or agent runs to answer *"what will run on deploy?"* before merging: it reports the path from the live marker to the ref without changing anything. `db migrate --show --db $URL` gives the same path as an ordered preview; `migration log --db $URL` gives the applied history after a deploy.
|
|
179
179
|
|
|
180
|
-
`db migrate` is interactive-free and
|
|
180
|
+
`db migrate` is interactive-free and asks nothing — the questions about data loss are asked by `migration plan` when it writes the package, and by `db update` (see the `references/migrations.md` skill). Whatever the planner put in the migration graph is what `db migrate` runs; review happens at `migration plan` and at `migration status` time, before the apply step.
|
|
181
181
|
|
|
182
182
|
## Common Pitfalls
|
|
183
183
|
|
|
@@ -30,8 +30,8 @@ Once the contract changes, you choose how the change reaches the database. This
|
|
|
30
30
|
|
|
31
31
|
## Key Concepts
|
|
32
32
|
|
|
33
|
-
- **`db update` (quick path).** Reads the emitted contract, diffs against the live DB, applies the change. Optional `--dry-run` prints the plan without executing.
|
|
34
|
-
- **`migration plan` (formal path).** Reads the emitted contract, diffs it against a resolved origin — explicit `--from`, else the `db` ref, else the empty database; there is no "head of the graph" to chain from (see `references/migration-model.md`) — and writes a new migration package under `migrations/app/<YYYYMMDDTHHMM>_<snake_slug>/`. If any operation needs a data transform, the package's `migration.ts` contains `placeholder(...)` calls you fill in.
|
|
33
|
+
- **`db update` (quick path).** Reads the emitted contract, diffs against the live DB, applies the change. Optional `--dry-run` prints the plan without executing. Before an apply it asks what each operation that would lose data means, answered by `--delete <subject>` or `--rename <subject>:<new name>`, and whether each operation that would widen who can read or write a model's rows may run, answered by `--allow <Model>` (see *Answer the data-loss questions* below). **Writes no migration directory.** Operations needing data transforms are not handled by this path — `db update` excludes the `data` operation class entirely and short-circuits where a data transform would be required. Use only against a database that has no shared history with anyone else (your local dev DB).
|
|
34
|
+
- **`migration plan` (formal path).** Reads the emitted contract, diffs it against a resolved origin — explicit `--from`, else the `db` ref, else the empty database; there is no "head of the graph" to chain from (see `references/migration-model.md`) — and writes a new migration package under `migrations/app/<YYYYMMDDTHHMM>_<snake_slug>/`. A plan that would lose data is written only once `--delete` or `--rename` says what each such operation means. If any operation needs a data transform, the package's `migration.ts` contains `placeholder(...)` calls you fill in.
|
|
35
35
|
- **The `app/` segment in migration paths is the consuming application's contract-space id.** Every migration *you* author lives under `migrations/app/`. Extensions your contract depends on get their own sibling directories (`migrations/<extension-space-id>/`) — those are managed by the extension package and you don't write into them. The `app/` segment lands automatically the first time you run `migration plan` / `db init` against an app-level config.
|
|
36
36
|
- **Migration package files** (inside each `migrations/app/<dir>/`):
|
|
37
37
|
- `migration.json` — manifest (metadata + `migrationHash`).
|
|
@@ -71,7 +71,9 @@ Treat the rendered import lines as framework-managed on both targets:
|
|
|
71
71
|
| `MIGRATION.INVALID_DEFAULT_EXPORT` | Loading `migration.ts` | The file's default export is not a `Migration` subclass or factory function. Restore the planner-emitted scaffold from version control or re-run `migration plan` for a clean package. |
|
|
72
72
|
| `MIGRATION.DATA_TRANSFORM_CONTRACT_MISMATCH` | Building a data-transform query plan | The query builder was instantiated with a contract reference different from the `endContract` passed to `this.dataTransform(...)`. Use the `endContract` imported at module scope for both. |
|
|
73
73
|
| `MIGRATION.HASH_MISMATCH` *Migration package is corrupt* | `db migrate` (or any read of the package) | `ops.json` / `migration.json` were edited without self-emitting. Run `node migrations/app/<dir>/migration.ts` to re-emit, then re-run `db migrate`. |
|
|
74
|
-
| `
|
|
74
|
+
| `CLI.CONSENT_REQUIRED` | `migration plan` / `db update` run with nobody to answer (`--no-interactive`, CI, `--yes`) | The plan would lose data or (on `db update`) widen access. `meta.unanswered` and `nextActions` list each question with the flags that answer it: `--delete <subject>`, `--rename <subject>:<new name>`, `--allow <Model>`. Re-run with the ones you mean. `--confirm` and `--yes` answer none of them. |
|
|
75
|
+
| `CLI.CONSENT_UNUSED` | `migration plan` / `db update` | Every question was answered, and a `--delete` or `--allow` was left over: a typo, a subject the plan does not touch, or a `--confirm` (which consents to nothing). Fix or drop the flag. A typo next to a question nobody answered is reported as `CLI.CONSENT_REQUIRED` instead, which names the flag that matched nothing. On `db update --dry-run` the same mistake is `MIGRATION.STATEMENT_ANSWERS_NO_QUESTION`. |
|
|
76
|
+
| `MIGRATION.STATEMENT_DID_NOT_RESOLVE_LOSS` | A rename typed at the data-loss prompt | The plan was made again with the rename and still loses the subject's data. Answer with `delete`, or give a rename whose old name is the subject. |
|
|
75
77
|
| `CONTRACT.MARKER_MISMATCH` | `db verify` (finding, exit 4) | The marker disagrees with the contract hash (**Postgres:** `prisma_contract.marker`; **Mongo:** `_prisma_migrations`). The DB is at a different contract version than the code thinks. Either run a migration forward, or — if the DB is correct and the marker is stale after a manual fix-up — run `db sign`. |
|
|
76
78
|
| `CONTRACT.MARKER_MISSING` | `db verify` (finding, exit 4), runtime startup (warning) | The DB has no marker yet. Run `prisma db init --db <url>` to baseline an empty database, `db update --db <url>` to apply the current contract directly, or `db sign --db <url>` if the schema already matches the contract. |
|
|
77
79
|
| `MIGRATION.STATEMENT_INVALID` | `migration plan` / `db update` with `--rename` | The statement is malformed, mixes a model and a field, renames something to itself, swaps two names, or repeats a name. The error's next step is the statement to type instead; use it. |
|
|
@@ -147,7 +149,7 @@ Canonical detail: [Migration System § Contract resolution through the snapshot
|
|
|
147
149
|
|
|
148
150
|
## Workflow — `db update` (quick path)
|
|
149
151
|
|
|
150
|
-
The concept: `db update` resolves the destination (`emitted contract`) against the live DB and applies the difference. Preview with `--dry-run
|
|
152
|
+
The concept: `db update` resolves the destination (`emitted contract`) against the live DB and applies the difference. Preview with `--dry-run`, which asks nothing and lists the questions an apply would ask under `dataLoss` and `accessWidening`. An apply that would lose data or widen access asks first (see *Answer the data-loss questions*). The path excludes operations of the `data` class entirely — if the diff requires a data transform, `db update` fails with a planning error and you switch to `migration plan` to author the transform.
|
|
151
153
|
|
|
152
154
|
Run after a contract edit:
|
|
153
155
|
|
|
@@ -167,7 +169,7 @@ Inspect the JSON output to drive the next move:
|
|
|
167
169
|
pnpm prisma db update --db $DATABASE_URL --json
|
|
168
170
|
```
|
|
169
171
|
|
|
170
|
-
The JSON contains `plan.operations[]` with each `operationClass`, plus (in apply mode) `execution.operationsExecuted` and the post-apply `marker.storageHash`. If the command
|
|
172
|
+
The JSON contains `plan.operations[]` with each `operationClass`, plus (in apply mode) `execution.operationsExecuted` and the post-apply `marker.storageHash`. If the command was refused because nobody answered its questions, the error is `CLI.CONSENT_REQUIRED`, and its `meta.unanswered[]` lists each subject with the flags that answer it.
|
|
171
173
|
|
|
172
174
|
## Workflow — `migration plan` + `db migrate` (formal path)
|
|
173
175
|
|
|
@@ -212,7 +214,7 @@ Fill in any data transforms (see *Fill a placeholder*), self-emit if you edited
|
|
|
212
214
|
pnpm prisma db migrate --db $DATABASE_URL
|
|
213
215
|
```
|
|
214
216
|
|
|
215
|
-
`db migrate` runs without prompting
|
|
217
|
+
`db migrate` runs without prompting: the data-loss questions were answered when `migration plan` wrote the package. Review destructive ops in the plan output or in `migration show` *before* applying.
|
|
216
218
|
|
|
217
219
|
## Workflow — Fill a placeholder
|
|
218
220
|
|
|
@@ -362,7 +364,7 @@ On Postgres the operations are **methods on the `Migration` base class**, each t
|
|
|
362
364
|
**Postgres** operations (representative set, all `this.<name>({...})`):
|
|
363
365
|
|
|
364
366
|
- Tables: `createTable`, `renameTable`, `dropTable`. `renameTable` reads the migration's start and end contracts and returns the table rename plus a rename of every constraint and index named after the old table, so spread it: `...this.renameTable({ table: 'userProfile', to: 'UserProfile' })`.
|
|
365
|
-
- Columns: `addColumn` (`column: col(name, nativeType, { codecRef })`), `renameColumn` (also renames the constraints and indexes named after the column, so spread it: `...this.renameColumn({ table: 'User', column: 'email', to: 'emailAddress' })`), `dropColumn`, `alterColumnType
|
|
367
|
+
- Columns: `addColumn` (`column: col(name, nativeType, { codecRef })`), `renameColumn` (also renames the constraints and indexes named after the column, so spread it: `...this.renameColumn({ table: 'User', column: 'email', to: 'emailAddress' })`), `dropColumn`, `alterColumnType` (pass `operationClass: 'widening'` only when every value of the old type converts to the new type unchanged, such as `int4` to `int8`; the default is `'destructive'`), `setNotNull`, `dropNotNull`, `setDefault` (`column: col(name, nativeType, { default: lit(value) or fn(expression), codecRef })`; `fn('autoincrement()')` on a smallint, integer or bigint column creates or reuses the `{table}_{column}_seq` sequence, attaches it, and starts it past the column's largest value), `dropDefault`.
|
|
366
368
|
- Constraints: `addPrimaryKey`, `addForeignKey`, `addUnique`, `addCheckConstraint`, `renameConstraint` (`kind`: `primaryKey`, `unique`, `foreignKey` or `checkConstraint`), `renameCheckConstraint`, `dropCheckConstraint`, `dropConstraint`.
|
|
367
369
|
- Indexes: `createIndex`, `renameIndex`, `dropIndex`.
|
|
368
370
|
- Enums: `createNativeEnumType`, `addNativeEnumValue`, `dropNativeEnumType`.
|
|
@@ -470,22 +472,22 @@ pnpm prisma db migrate --db $DATABASE_URL
|
|
|
470
472
|
|
|
471
473
|
If self-emit itself fails (e.g. the contract has moved on and the operations no longer make sense against the migration's end contract), the package is stale. Either restore it from version control or delete it and re-plan with `migration plan`.
|
|
472
474
|
|
|
473
|
-
## Workflow —
|
|
475
|
+
## Workflow — Answer the data-loss questions (`migration plan` and `db update`)
|
|
474
476
|
|
|
475
|
-
The concept:
|
|
477
|
+
The concept: the planner cannot tell whether a dropped model or field was meant to go, or was renamed. So before `migration plan` writes a package, or `db update` applies, the command asks one question per model, field or storage name an operation would lose data from. `db update` also asks one per model whose rows an operation would open up, such as dropping a row-level-security policy. Each question is answered by a statement:
|
|
476
478
|
|
|
477
|
-
|
|
479
|
+
- `--delete <subject>` lets the data go: `--delete Legacy`, `--delete User.nickname`. A subject no model stores is named by its storage name (`public.audit_log` on Postgres, `audit_log` on SQLite) and can only be deleted.
|
|
480
|
+
- `--rename <subject>:<new name>` keeps it under a new name: `--rename Profile:User`, or for a field `--rename User.nickname:User.handle` (the new name keeps its model). A field of a renamed model is named through the new name (`--delete User.nickname` after `--rename Profile:User`).
|
|
481
|
+
- `--allow <Model>` (`db update` only) lets an operation that changes who can read or write the model's rows run: one flag per operation, so a policy drop and disabling row-level security on one model take `--allow User --allow User`.
|
|
478
482
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
483
|
+
```bash
|
|
484
|
+
pnpm prisma migration plan --name drop_legacy --delete Legacy
|
|
485
|
+
pnpm prisma db update --db $DATABASE_URL --rename Profile:User --delete Legacy --allow User
|
|
486
|
+
```
|
|
482
487
|
|
|
483
|
-
-
|
|
484
|
-
- Answer no, then either:
|
|
485
|
-
- Re-shape the migration via `migration plan` and hand-edit `migration.ts` to preserve the data (e.g. copy-to-new-column, then drop), or
|
|
486
|
-
- Skip the destructive operation by reverting the contract change.
|
|
488
|
+
In a terminal the command asks each question in turn; type `delete`, `allow`, or `rename <subject>:<new name>`. Where nobody can answer (CI, `--no-interactive`, `--yes`), it fails with `CLI.CONSENT_REQUIRED`; read `meta.unanswered[]` or the `nextActions`, decide what each subject means, and re-run with those flags. `--confirm` and `--yes` answer none of these questions. Don't reach for `--delete` by reflex: if the model or field was renamed, `--rename` keeps its rows.
|
|
487
489
|
|
|
488
|
-
|
|
490
|
+
`db migrate` does *not* ask: whatever the package contains runs, so review the plan or call `migration show` before `db migrate`.
|
|
489
491
|
|
|
490
492
|
## Common Pitfalls
|
|
491
493
|
|
|
@@ -506,7 +508,7 @@ Interactively, consent is typing the database name back (the prompt names it). I
|
|
|
506
508
|
- **Runtime-apply migrations.** Prisma 8 doesn't apply pending migrations from your app's startup code (the "Drizzle pattern" for serverless / edge). Workaround: run `prisma db migrate` from your deploy pipeline before the app starts. If you need runtime-apply built-in, file a feature request via the `references/feedback.md` skill.
|
|
507
509
|
- **Seeds-as-first-class.** Prisma 8 doesn't ship a `prisma db seed` equivalent. Workaround: write a TypeScript script that imports your `db` instance and runs your setup queries; invoke it from `package.json`'s scripts. If you need first-class seeding, file a feature request via the `references/feedback.md` skill.
|
|
508
510
|
- **Migration squashing.** Prisma 8 doesn't squash older migrations into a baseline. They accumulate; for very large histories, manual baseline-and-truncate is the path. If you need built-in squashing, file a feature request via the `references/feedback.md` skill.
|
|
509
|
-
- **Renames beyond models and fields, and renames on MongoDB.** `--rename` covers models and fields on Postgres and SQLite. It does not rename enum values, namespaces or value objects, and does not move a model to another namespace. On MongoDB, `--rename` fails with `MIGRATION.PLANNING_FAILED` and plans nothing. Workaround: follow the error's advice and make the rename by hand in `mongosh` on each database, before a plan made without `--rename` is applied there: `renameCollection` for a model, and for a field turn off the collection's validator, drop each unique index that includes the field, then `updateMany({}, { $rename: ... })`. Applying the plan then creates the new indexes and turns the validator back on. There is no Mongo rename operation to write in `migration.ts`. If you need one of these, file a feature request via the `references/feedback.md` skill.
|
|
511
|
+
- **Renames beyond models and fields, and renames on MongoDB.** `--rename` covers models and fields on Postgres and SQLite. It does not rename enum values, namespaces or value objects, and does not move a model to another namespace. On MongoDB, `--rename` fails with `MIGRATION.PLANNING_FAILED` and plans nothing. A data-loss question about a dropped collection offers only `--delete` and says how to keep the documents by hand; a migration written by `migration plan` still drops the collection, so remove that operation or do not apply it where the collection was renamed. Workaround: follow the error's advice and make the rename by hand in `mongosh` on each database, before a plan made without `--rename` is applied there: `renameCollection` for a model, and for a field turn off the collection's validator, drop each unique index that includes the field, then `updateMany({}, { $rename: ... })`. Applying the plan then creates the new indexes and turns the validator back on. There is no Mongo rename operation to write in `migration.ts`. If you need one of these, file a feature request via the `references/feedback.md` skill.
|
|
510
512
|
|
|
511
513
|
## Graph and history commands
|
|
512
514
|
|
|
@@ -538,4 +540,4 @@ The CLI collects anonymous usage data by default. To opt out, set `PRISMA_DISABL
|
|
|
538
540
|
- [ ] Used `db verify` only when diagnosing drift — not as a routine post-apply step.
|
|
539
541
|
- [ ] Did NOT use `db update` against a shared or production database.
|
|
540
542
|
- [ ] Did NOT edit `ops.json` directly.
|
|
541
|
-
- [ ]
|
|
543
|
+
- [ ] Answered each data-loss question deliberately: `--rename` for a rename, `--delete` only for data that may go, `--allow` only for a widening that is meant.
|
|
@@ -309,7 +309,7 @@ The same holds for an `if` with an early return, a `switch`, a loop and a reassi
|
|
|
309
309
|
A custom collection class gives a model its own named queries. Extend `Collection`, register the class with `orm({ collections })`, and build that client inside the request from `db.runtime()` and `db.context`:
|
|
310
310
|
|
|
311
311
|
```typescript
|
|
312
|
-
import { Collection, type Filtered, type Ordered, orm
|
|
312
|
+
import { Collection, type Filtered, type Fragment, type Ordered, orm } from '@prisma/orm-postgres/orm-client';
|
|
313
313
|
import type { Contract } from './prisma/contract.d';
|
|
314
314
|
|
|
315
315
|
class PostCollection extends Collection<Contract, 'Post'> {
|
|
@@ -341,10 +341,10 @@ After `.select(...)` or `.variant(...)` the class methods are gone: those return
|
|
|
341
341
|
|
|
342
342
|
Inside a class body, a class method called on the result of another call loses what that call established. So a class method whose body chains two class methods loses the first call's facts for every caller: with `latest() { return this.byAuthor(id).newestFirst(); }`, `Post.latest()` is known to be ordered but not filtered. The same holds for `.prepared` after `.include(...)` inside the class: it describes the class's row without the included relation. Inside the class, follow a class method with built-in methods (`this.byAuthor(id).orderBy(...)`), or chain the class methods from outside the class, where they keep every fact.
|
|
343
343
|
|
|
344
|
-
`with(fn)` calls a function with the collection and returns its result.
|
|
344
|
+
`with(fn)` calls a function with the collection and returns its result. The function is a query fragment, described in Workflow — Query fragments below, of type `Fragment<In, Out>`, so a query can be written once and applied to any collection of that class:
|
|
345
345
|
|
|
346
346
|
```typescript
|
|
347
|
-
const newest:
|
|
347
|
+
const newest: Fragment<PostCollection, Ordered<PostCollection>> = (posts) => posts.newestFirst();
|
|
348
348
|
await Post.with(newest).limit(20).all();
|
|
349
349
|
```
|
|
350
350
|
|
|
@@ -358,17 +358,17 @@ function deleteMatching(posts: Filtered<PostCollection>) {
|
|
|
358
358
|
await deleteMatching(Post.byAuthor(userId)).toArray();
|
|
359
359
|
```
|
|
360
360
|
|
|
361
|
-
## Workflow —
|
|
361
|
+
## Workflow — Query fragments
|
|
362
362
|
|
|
363
|
-
A piece of a query used in several places is a function. Do not build a filter object and spread it into each query; write a function and pass it to `.where(...)`, `.orderBy(...)` or `.with(...)`. A function
|
|
363
|
+
A piece of a query used in several places is a function. Do not build a filter object and spread it into each query; write a function and pass it to `.where(...)`, `.orderBy(...)` or `.with(...)`. A **row fragment** is a function of the model accessor, and `.where(...)` and `.orderBy(...)` take it. A **query fragment** (a **fragment** for short) is a function from a collection to a collection, and `.with(...)` runs it. A **scope** is a fragment that only imposes conditions on the query, such as `createdSince` below, and is also run with `.with(...)`. When the condition is a function of one row and needs no declared fields, pass a row fragment to `.where(...)`. When the same condition should apply to every model that has some fields, write a scope: it declares the fields, checks them at run time, and returns the collection with the `Filtered` fact. `.with(...)` also runs what `.where(...)` cannot express, such as a shared `select` and `include`, an order, a limit or offset, or a variant.
|
|
364
364
|
|
|
365
|
-
**The same filter on several models.** Define a scope with `db.orm.
|
|
365
|
+
**The same filter on several models.** Define a scope with `db.orm.fragment(fields, body)`; the method is named `fragment` because it cannot know whether the body only filters. Declare each field the fragment needs with the same builder the schema uses, from the `field` that `@prisma/orm-postgres/contract-builder` exports, adding `.optional()` for a field that may be null. The body sees only those fields:
|
|
366
366
|
|
|
367
367
|
```typescript
|
|
368
368
|
import { field } from '@prisma/orm-postgres/contract-builder';
|
|
369
369
|
|
|
370
370
|
const createdSince = (since: Temporal.Instant) =>
|
|
371
|
-
db.orm.
|
|
371
|
+
db.orm.fragment({ createdAt: field.temporal.timestamptz() }, (rows) =>
|
|
372
372
|
rows.where((r) => r.createdAt.gte(since)),
|
|
373
373
|
);
|
|
374
374
|
|
|
@@ -376,7 +376,7 @@ await db.orm.public.User.with(createdSince(since)).all();
|
|
|
376
376
|
await db.orm.public.Post.with(createdSince(since)).deleteAll();
|
|
377
377
|
```
|
|
378
378
|
|
|
379
|
-
Pick the builder whose codec matches the field's codec in `contract.d.ts`: a PSL `DateTime` is `field.temporal.timestamptz()` (`pg/timestamptz-temporal@1`), a `String` is `field.text()` (`pg/text@1`), a `Uuid` is `field.uuidNative()` (`pg/uuid@1`). `field.column(columnType)` is the explicit form. A package that offers a
|
|
379
|
+
Pick the builder whose codec matches the field's codec in `contract.d.ts`: a PSL `DateTime` is `field.temporal.timestamptz()` (`pg/timestamptz-temporal@1`), a `String` is `field.text()` (`pg/text@1`), a `Uuid` is `field.uuidNative()` (`pg/uuid@1`). `field.column(columnType)` is the explicit form. A package that offers a fragment and does not import the facade writes `{ codecId: 'pg/text@1', nullable: false }`, with a codec of the contract. For a list field such as `String[]`, add `.many()` to the builder or `many: { elementNullable: false }` to the object; for a `String?[]`, whose elements may be null, add `.many({ elementsNullable: true })` or `many: { elementNullable: true }`. Without it the declaration does not match a list field, and with it the element nullability must match too. A list of value objects such as `Address[]` is stored as one `jsonb` value and is still a list: `field.column(jsonbColumn).many()`. The body may call `where`, `orderBy`, `limit` and `offset`. The result keeps the collection's class and records the filter, so `update` and `delete` are allowed after a fragment that filters. `updateAll`, `deleteAll` and their `AndCount` forms change every matching row and throw `ORM.ARGUMENT_INVALID` when the chain has a `limit`, `offset`, `cursor`, `distinct` or `distinctOn`, so do not put a limit in a fragment that will be followed by one of them. `update` and `delete` change the one row `first()` returns, so the order, offset, cursor, `distinct` and `distinctOn` choose it, and after `limit(0)` they change nothing and return `null`; `update` with a relation callback throws on an order, a limit, an offset, a cursor, `distinct` or `distinctOn`, because it finds its row by the filter alone. A model without the field, or with the field under another codec or nullability, is a compile error, and a run-time `ORM.FIELD_UNKNOWN`. A custom collection class carries no namespace in its type, so at compile time it matches only a field that every model of that name has, with the same codec and nullability, and its real namespace is checked at run time. The codec, the nullability, whether the field is a list and the nullability of its elements are compared; the column type's parameters are not, so `field.uuidString()` (`char(36)`) also matches a `char(10)` field.
|
|
380
380
|
|
|
381
381
|
For a filter used inside a larger `where`, a plain function of the row works too. Type the field with `CodecField<Contract, CodecId, Nullable>`, the type of any field with that codec:
|
|
382
382
|
|
|
@@ -391,12 +391,12 @@ await db.orm.public.Post.where((p) => and(created(since)(p), p.title.ilike('%orm
|
|
|
391
391
|
|
|
392
392
|
Both forms check values against the codec's type, not the field's. For a field that narrows its codec's values, such as an enum stored as `pg/text@1`, they accept values the field would refuse: `row.kind.eq('superuser')` compiles even when `kind` has no such member. Write a filter on such a field inline, on the model, where the field's own type checks the value.
|
|
393
393
|
|
|
394
|
-
**The same `select` and `include` in several queries.** Define it once with `.
|
|
394
|
+
**The same `select` and `include` in several queries.** Define it once with `.fragment(...)` on a collection of the model, run it with `.with(...)`, and name its row with `CollectionRowOf`:
|
|
395
395
|
|
|
396
396
|
```typescript
|
|
397
397
|
import type { CollectionRowOf } from '@prisma/orm-postgres/orm-client';
|
|
398
398
|
|
|
399
|
-
const postSummary = db.orm.public.Post.
|
|
399
|
+
const postSummary = db.orm.public.Post.fragment((posts) =>
|
|
400
400
|
posts.select('id', 'title', 'createdAt').include('tags'),
|
|
401
401
|
);
|
|
402
402
|
type PostSummary = CollectionRowOf<ReturnType<typeof postSummary>>;
|
|
@@ -405,7 +405,7 @@ await db.orm.public.Post.where({ userId }).with(postSummary).limit(20).all();
|
|
|
405
405
|
await db.orm.public.User.include('posts', (posts) => posts.with(postSummary)).all();
|
|
406
406
|
```
|
|
407
407
|
|
|
408
|
-
The body receives the plain collection of the model, without a custom class's methods. Apply the
|
|
408
|
+
The body receives the plain collection of the model, without a custom class's methods. Apply the fragment before `.select(...)` or `.variant(...)`: it is refused on a collection they narrowed. Its result is always typed as the plain collection, even when the body keeps the row, so a custom class's methods are gone after it, and `update`, `delete` and `cursor` are refused, although an earlier `.where(...)` still runs; it is for reads. For a filter on one model, use a class method or `db.orm.fragment`. A fragment for one model refuses a collection of another model or namespace at run time with `ORM.ARGUMENT_INVALID`.
|
|
409
409
|
|
|
410
410
|
**A field to order by, from a request.** Pass the request's string to `orderByField` with the fields the endpoint allows. Do not index the field proxy with the raw string:
|
|
411
411
|
|
|
@@ -647,7 +647,7 @@ Cross-namespace relations (e.g. `public.Profile` → `auth.User`) follow the sam
|
|
|
647
647
|
- [ ] Expressed ranges as chained `.where(...)` clauses or a single `and(...)` clause — did NOT reach for a non-existent `.between(...)` operator.
|
|
648
648
|
- [ ] For cursor pagination, used `.orderBy(...).cursor({ field: lastValue }).limit(n).all()` — did NOT hand-write a `.where(p => p.field.lt(cursor))` workaround when the `.cursor()` API serves the same purpose.
|
|
649
649
|
- [ ] For ORM combinators, imported `and` / `or` / `not` from `@prisma/orm-postgres/orm-client`.
|
|
650
|
-
- [ ] Wrote a query piece shared between places as a function: a
|
|
650
|
+
- [ ] Wrote a query piece shared between places as a function: a query fragment from `db.orm.fragment(fields, body)` or `collection.fragment(body)` run with `.with(...)`, a `where` callback typed with `CodecField`, or `orderByField` for an order parameter from a request.
|
|
651
651
|
- [ ] Ran SQL-builder plans via `db.runtime().query(plan)` when they return rows and `db.runtime().execute(plan)` only for non-returning writes (`tx.query` / `tx.execute` inside a transaction). Passed `insert()` an array of rows.
|
|
652
652
|
- [ ] Wrapped multi-statement work in `db.transaction(async (tx) => { ... })` where atomicity matters.
|
|
653
653
|
- [ ] For top-N grouped aggregates at meaningful scale, dropped to `db.sql.<ns>.<table>` rather than JS-side sort + slice over `groupBy(...).aggregate(...)`.
|