diff --git a/apps/docs/content/docs/(index)/prisma-orm/add-to-existing-project/postgresql.mdx b/apps/docs/content/docs/(index)/prisma-orm/add-to-existing-project/postgresql.mdx index 5a5943f1a49..493588c0fed 100644 --- a/apps/docs/content/docs/(index)/prisma-orm/add-to-existing-project/postgresql.mdx +++ b/apps/docs/content/docs/(index)/prisma-orm/add-to-existing-project/postgresql.mdx @@ -101,12 +101,12 @@ The command replaces the example contract from step 2. For a database with a `us namespace public { model User { - id Int @id(map: "user_pkey") @default(autoincrement()) - email String @unique(map: "user_email_key") + id Int @id(map: "user_pkey") @default(autoincrement()) + email String @unique(map: "user_email_key") name String? - role String @default("member") + role String @default("member") age Int? - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzString @default(now()) @map("created_at") posts Post[] @@check(expression: "(age >= 0)", map: "user_age_check") @@ -136,7 +136,7 @@ namespace public { } ``` -Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `namespace public` holds the models of the tables in the PostgreSQL schema `public`. Where Prisma ORM 7 wrote `DateTime @db.Timestamptz`, the contract writes `Timestamptz`, and `String @db.VarChar(200)` becomes `VarChar(200)`. In the policy block, `roles = [public]` is the PostgreSQL role `PUBLIC`, not the schema. +Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `namespace public` holds the models of the tables in the PostgreSQL schema `public`. Where Prisma ORM 7 wrote `DateTime @db.Timestamptz`, the contract writes `TimestamptzString`, and `String @db.VarChar(200)` becomes `VarChar(200)`. In the policy block, `roles = [public]` is the PostgreSQL role `PUBLIC`, not the schema. `contract infer` reads these parts of the database and writes each one into the contract: @@ -153,11 +153,11 @@ Review the file before you go on. You can rename models, and you can remove the A table without a model stays in your database, and later migrations leave it alone. Do not remove the model of a table that has row-level security policies, though. If you do, `db sign` in step 6 fails, because it finds policies that the contract does not declare. -### Date and time columns on Node.js 24 and older +### Date and time columns -`contract infer` gives a `timestamptz` column the type `Timestamptz`, and a `timestamp` column the type `Timestamp`. Prisma ORM returns the values of these columns as `Temporal` objects. `Temporal` is the new date and time API of JavaScript. Node.js 26 has it built in. Node.js 24 and older do not, and there the first query that reads such a column fails with `RUNTIME.TEMPORAL_UNAVAILABLE`. +`contract infer` gives a `timestamptz` column the type `TimestamptzString`, a `timestamp` column `TimestampString`, a `date` column `DateString`, and a `time` column `TimeString`. Prisma ORM returns the values of these columns as the text PostgreSQL prints, such as `2026-09-30 07:36:38.60182+02`, so your app needs no `Temporal`. -On Node.js 24 or older, install the `temporal-polyfill` package: +To get `Temporal` objects instead, the new date and time API of JavaScript, change the type of each such field in the contract: `TimestamptzString` becomes `Timestamptz`, `TimestampString` becomes `Timestamp`, `DateString` becomes `Date`, and `TimeString` becomes `Time`. The column in the database stays as it is. Node.js 26 has `Temporal` built in. Node.js 24 and older do not, and there the first query that reads such a column fails with `RUNTIME.TEMPORAL_UNAVAILABLE`. On Node.js 24 or older, install the `temporal-polyfill` package: ```npm npm install temporal-polyfill @@ -169,8 +169,6 @@ Then add this line at the top of `src/prisma/db.ts`: import "temporal-polyfill/global"; ``` -If you would rather not add the package, change the type of each such field in the contract: `Timestamptz` becomes `TimestamptzString`, and `Timestamp` becomes `TimestampString`. The field then holds the text that PostgreSQL returns, such as `2026-09-30 07:36:38.60182+02`, and needs no `Temporal`. The column in the database stays as it is. - ### Tables in other PostgreSQL schemas `contract infer` reads only the PostgreSQL schema named `public`. It skips tables in any other schema and prints no message about them. diff --git a/apps/docs/content/docs/(index)/prisma-orm/from-scratch.mdx b/apps/docs/content/docs/(index)/prisma-orm/from-scratch.mdx index 32dafd47a5d..e09b195b9c9 100644 --- a/apps/docs/content/docs/(index)/prisma-orm/from-scratch.mdx +++ b/apps/docs/content/docs/(index)/prisma-orm/from-scratch.mdx @@ -43,7 +43,7 @@ npm install @prisma/orm-postgres dotenv npm install --save-dev prisma tsx typescript ``` -`@prisma/orm-postgres` is the library your code imports, and `prisma` is the command-line tool. `dotenv` loads `.env`, `tsx` runs `index.ts` without a build step, and `typescript` 5.9 or newer is a peer dependency of `@prisma/orm-postgres`. So is `temporal-polyfill`, which npm installs for you; with Yarn, add it yourself. The two Prisma packages have different version numbers (for example, `prisma` 8.0.0-rc.20 and `@prisma/orm-postgres` 8.0.0-rc.14); that is normal, and any two `latest` versions work together. +`@prisma/orm-postgres` is the library your code imports, and `prisma` is the command-line tool. `dotenv` loads `.env`, `tsx` runs `index.ts` without a build step, and `typescript` 5.9 or newer is a peer dependency of `@prisma/orm-postgres`. So is `temporal-polyfill`, which npm installs for you; with Yarn, add it yourself. The two Prisma packages have different version numbers (for example, `prisma` 8.0.0-rc.21 and `@prisma/orm-postgres` 8.0.0-rc.16); that is normal, and any two `latest` versions work together. ## 2. Create the config file and `.env` diff --git a/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/mongodb.mdx b/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/mongodb.mdx index 1f08723d300..cdd883d1b82 100644 --- a/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/mongodb.mdx +++ b/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/mongodb.mdx @@ -59,7 +59,7 @@ installed ├─ dotenv ├─ prisma@latest (dev) ├─ @types/node (dev) -└─ @prisma/cli-engine@0.6.2 (dev) +└─ @prisma/cli-engine@0.6.3 (dev) ✔ Done. Open prisma-8.md to get started. ``` diff --git a/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/postgresql.mdx b/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/postgresql.mdx index 29c4a7f232d..a5309b3f29d 100644 --- a/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/postgresql.mdx +++ b/apps/docs/content/docs/(index)/prisma-orm/quickstart/existing-app/postgresql.mdx @@ -59,7 +59,7 @@ installed ├─ dotenv ├─ prisma@latest (dev) ├─ @types/node (dev) -└─ @prisma/cli-engine@0.6.2 (dev) +└─ @prisma/cli-engine@0.6.3 (dev) ✔ Done. Open prisma-8.md to get started. ``` diff --git a/apps/docs/content/docs/cli/configuration.mdx b/apps/docs/content/docs/cli/configuration.mdx index 09d399c88aa..889ca8d2735 100644 --- a/apps/docs/content/docs/cli/configuration.mdx +++ b/apps/docs/content/docs/cli/configuration.mdx @@ -121,7 +121,7 @@ export default definePrismaConfig({ }); ``` -`prisma7Schema` takes one schema file, or a directory of `.prisma` files as Prisma ORM 7 reads it. It reads every `.prisma` file it is given, so these files do not need the `// use prisma-8` first line. `contract emit` writes `contract.json` and `contract.d.ts` into the directory that holds the schema, here `prisma/`, unless you set `output`. Prisma ORM 7 keeps owning the database and its migrations, and you run its commands as `prisma7`, the Prisma ORM 7 CLI that [`orm init --from-prisma7-schema`](/cli/orm-init#on-a-prisma-orm-7-project) installs next to Prisma ORM 8. +`prisma7Schema` takes one schema file, or a directory of `.prisma` files as Prisma ORM 7 reads it. A `DateTime` field, and a field with `@db.Timestamp`, `@db.Timestamptz`, `@db.Date`, or `@db.Time`, reads and writes strings such as `"2026-09-14 10:00:00.123"`, the text PostgreSQL prints, not `Temporal` values, so your application needs no `Temporal`. A `DateTime` value is in UTC, as Prisma ORM 7 writes it. To get `Temporal` values, write a Prisma ORM 8 contract with `npx prisma contract print --output prisma/contract.prisma` and change the types there. It reads every `.prisma` file it is given, so these files do not need the `// use prisma-8` first line. `contract emit` writes `contract.json` and `contract.d.ts` into the directory that holds the schema, here `prisma/`, unless you set `output`. Prisma ORM 7 keeps owning the database and its migrations, and you run its commands as `prisma7`, the Prisma ORM 7 CLI that [`orm init --from-prisma7-schema`](/cli/orm-init#on-a-prisma-orm-7-project) installs next to Prisma ORM 8. Once the config is in place, run `npx prisma contract emit` and then [`npx prisma db sign`](/cli/db-sign). `db sign` checks that the database matches the contract and then records that in the database, without changing your tables. Run both commands again after each Prisma ORM 7 migration, whether you ran `npx prisma7 migrate dev` or `npx prisma7 migrate deploy`, so that the record names the new contract. @@ -148,6 +148,8 @@ export default definePrismaConfig({ The schema file does not need the `// use prisma-8` first line. Run `npx prisma contract emit` and then [`npx prisma db sign`](/cli/db-sign), which checks that the database Prisma ORM 6 built matches the contract. +`prisma6Schema` reads each field as the BSON type Prisma ORM 6 stores. A plain `Int` and an `Int @db.Long` are a `number` in your code, written as a BSON long, and a stored value with a fraction fails to read with `RUNTIME.DECODE_FAILED`. A `Bytes @db.ObjectId` field reads as a 24-digit hex string and takes a hex string or an `ObjectId`, not a `Buffer`. Every native type Prisma ORM 6 accepts on MongoDB is read, except `DateTime @db.Timestamp`. + `contract emit` fails on any part of the schema that Prisma ORM 8 cannot represent exactly, with an error code that starts with `PSL.PRISMA6_MONGO_`. The [error reference](/orm/reference/error-reference) lists each code and what to change. ## Write your own contract source diff --git a/apps/docs/content/docs/cli/contract-emit.mdx b/apps/docs/content/docs/cli/contract-emit.mdx index ab26e30d514..e9cce6d654e 100644 --- a/apps/docs/content/docs/cli/contract-emit.mdx +++ b/apps/docs/content/docs/cli/contract-emit.mdx @@ -33,15 +33,25 @@ The command emits: - `contract.json`, the canonical machine-readable contract - `contract.d.ts`, the generated TypeScript contract declarations -Do not edit these files by hand. Re-run `contract emit` after changing the contract source or extension pack list. The first emit after you upgrade from a release before `8.0.0-rc.12` reorders the entries in `contract.d.ts` to follow the order in `contract.json`, which needs no change to your code. The first emit after you upgrade from a release before `8.0.0-rc.13` renames two keys in `contract.json`: each generated default under `execution.mutations.defaults` names its target as `entry` and `field` instead of `table` and `column`. The client refuses a contract that has the old keys, so run `contract emit` after you upgrade. `contract emit` does not rewrite the contracts stored under `migrations/`, such as the snapshots in `migrations/snapshots/`. To rename the keys in them, download the [`rename-execution-ref-keys` script](https://github.com/prisma/orm/blob/v8.0.0-rc.14/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.12-to-8.0.0-rc.13/scripts/execution-ref-entry-field/rename-execution-ref-keys.ts) into your project root and run it there: +Do not edit these files by hand. Re-run `contract emit` after changing the contract source or extension pack list. The first emit after you upgrade from a release before `8.0.0-rc.12` reorders the entries in `contract.d.ts` to follow the order in `contract.json`, which needs no change to your code. The first emit after you upgrade from a release before `8.0.0-rc.13` renames two keys in `contract.json`: each generated default under `execution.mutations.defaults` names its target as `entry` and `field` instead of `table` and `column`. The client refuses a contract that has the old keys, so run `contract emit` after you upgrade. `contract emit` does not rewrite the contracts stored under `migrations/`, such as the snapshots in `migrations/snapshots/`. To rename the keys in them, download the [`rename-execution-ref-keys` script](https://github.com/prisma/orm/blob/v8.0.0-rc.15/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.12-to-8.0.0-rc.13/scripts/execution-ref-entry-field/rename-execution-ref-keys.ts) into your project root and run it there: ```bash title="Terminal" -curl -O https://raw.githubusercontent.com/prisma/orm/v8.0.0-rc.14/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.12-to-8.0.0-rc.13/scripts/execution-ref-entry-field/rename-execution-ref-keys.ts +curl -O https://raw.githubusercontent.com/prisma/orm/v8.0.0-rc.15/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.12-to-8.0.0-rc.13/scripts/execution-ref-entry-field/rename-execution-ref-keys.ts npx tsx rename-execution-ref-keys.ts ``` The script renames `table` to `entry` and `column` to `field` in each `ref` under `execution.mutations.defaults`, in every `.json` and `.d.ts` file under a `migrations/` folder. It leaves each `executionHash` as it is, and a second run changes nothing. Pass `--check` to list the files it would change without writing them. +Since `8.0.0-rc.15`, each SQL column in `contract.json` stores its data type id, such as `"dataType": "pg/text"`, in place of `nativeType`, and the client refuses a contract in the old format. When you upgrade from a release before `8.0.0-rc.15`, upgrade every extension that ships migrations, such as pgvector and PostGIS, in the same step. Then, before the first emit, run the [`data-type-in-contract` script](https://github.com/prisma/orm/blob/v8.0.0-rc.15/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.14-to-8.0.0-rc.15/scripts/data-type-in-contract/data-type-in-contract.ts) in your project root, and sign every database with [`db sign`](/cli/db-sign): + +```bash title="Terminal" +curl -O https://raw.githubusercontent.com/prisma/orm/v8.0.0-rc.15/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.14-to-8.0.0-rc.15/scripts/data-type-in-contract/data-type-in-contract.ts +node data-type-in-contract.ts +npx prisma db sign --db "$DATABASE_URL" +``` + +The script needs Node 24 or later. It rewrites every contract, snapshot, migration, ref, `migration.ts`, and `contract.d.ts` in the project, and every storage hash changes once. Until a database is signed, `db migrate` refuses with `MIGRATION.MARKER_MISMATCH`. Commit your work before you run it, so you can review its changes. + When the contract source cannot be read, the command fails with `CONTRACT.SOURCE_LOAD_FAILED` and prints each finding, meaning each problem it found in the source, with the file and line where it knows them. In `--json` output, the findings are the entries of the `envelope.diagnostics` array in the final `result` event, next to `envelope.error`. Each entry has a `code`, a `summary`, and, where known, `where.path` and `where.line`. If an entry's `code` is `CONTRACT.SOURCE_DIAGNOSTIC`, the code of the specific problem, such as `PSL_NO_OPTED_IN_SCHEMA_FILES`, is in its `meta.code`. A warning in the contract source, such as `PSL_DEPRECATED_SCALAR_NAME` for a MongoDB scalar type name that is deprecated, does not stop the command. It is reported as a diagnostic whose `severity` is `warn`, and the command still writes both files. In `--json` output, the warning is an entry of `envelope.diagnostics` in the final `result` event, with `code` set to `CONTRACT.SOURCE_DIAGNOSTIC` and the warning's own code in `meta.code`. diff --git a/apps/docs/content/docs/cli/contract-infer.mdx b/apps/docs/content/docs/cli/contract-infer.mdx index 70991081887..c78437fc98e 100644 --- a/apps/docs/content/docs/cli/contract-infer.mdx +++ b/apps/docs/content/docs/cli/contract-infer.mdx @@ -47,6 +47,10 @@ The file starts with `// use prisma-8`, because `contract emit` reads only `.pri Every column default is written in a form `contract emit` accepts, so you can emit the file without editing it. [Default values](/orm/contract-authoring/psl-syntax#default-values) shows those forms. A list column that allows `NULL` is written `Type[]?`, and a list column that is `NOT NULL` is written `Type[]`. +Date and time columns are written as text types, so your application reads and writes them as strings and needs no `Temporal`: a `timestamp(3)` column is written `TimestampString(3)`, `timestamptz(3)` is `TimestamptzString(3)`, `date` is `DateString`, and `time(3)` is `TimeString(3)`. A `timestamp` value reads as `"2026-09-14 10:00:00.123"`. To get `Temporal` values instead, change the types to `Timestamp(3)`, `Timestamptz(3)`, `Date`, and `Time(3)` in the inferred file. A `bytea` default is written as the base64 text the `Bytes` type stores, such as `@default("aGVsbG8=")`. + +A contract inferred before `8.0.0-rc.15` keeps its types until you run `contract infer` again. When you do, the date and time fields and any `bytea` default change, and so does the storage hash: emit the contract, update the code that used `Temporal` values for these fields, then run `prisma db sign` for a database you keep with `db update`, or create an empty migration with [`migration new`](/cli/migration-new) and apply it when you use migrations. + If you keep an inferred contract in version control, running `contract infer` again after an upgrade can write the same database differently, so review the diff before you commit it. The command stops at `contract.prisma`. Follow it with the emit and sign steps: diff --git a/apps/docs/content/docs/cli/db-sign.mdx b/apps/docs/content/docs/cli/db-sign.mdx index 905a56f7b79..0494b8a3dee 100644 --- a/apps/docs/content/docs/cli/db-sign.mdx +++ b/apps/docs/content/docs/cli/db-sign.mdx @@ -8,9 +8,11 @@ metaDescription: Learn how to sign a database once it matches the current Prisma `db sign` verifies that the live database satisfies the emitted contract and, if so, writes or updates the database signature. The signature records that this database instance matches a specific contract version. +It signs every contract space: your application's contract, and the contract of each extension that ships one, such as pgvector or PostGIS. It verifies each space, then signs every space that verified, in one transaction on PostgreSQL and SQLite. A space that fails verification is not signed: the command prints its differences and exits with code `4`. + It is safe to run more than once, and safe to run in CI or a deployment pipeline. Use it after importing or inferring an existing schema, or after a deployment flow that already applied the required database changes. -After a successful signature, `db sign` also stores the signed contract as a snapshot and points the [ref](/cli/migration-ref) named `db` at it, so the next [`migration plan`](/cli/migration-plan) starts from the state you just signed instead of from an empty database. Unlike `db init` and `db update`, passing `--db` does not turn this off, because you normally sign the real database. Pass `--no-advance-ref` when you do not want the command to write a ref or a snapshot, for example in a deployment pipeline. +After a successful signature, `db sign` also stores the signed contract as a snapshot and points the [ref](/cli/migration-ref) named `db` of each signed space at it, so the next [`migration plan`](/cli/migration-plan) starts from the state you just signed instead of from an empty database. Unlike `db init` and `db update`, passing `--db` does not turn this off, because you normally sign the real database. Pass `--no-advance-ref` when you do not want the command to write a ref or a snapshot, for example in a deployment pipeline. ## Usage @@ -28,7 +30,7 @@ npx prisma db sign --db "$DATABASE_URL" | `--advance-ref ` | Advances this ref instead of `db` after a successful signature. | | `--no-advance-ref` | Signs without writing any ref or snapshot. Cannot be combined with `--advance-ref`. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | -| `--json` | Prints a machine-readable result. It includes `advancedRef` with the ref name and contract hash, or `null` when no ref was written. | +| `--json` | Prints a machine-readable result, with one outcome per contract space. See [JSON output](#json-output). | ### Contract references [#contract-references] @@ -38,9 +40,43 @@ A ref name is a name you gave a contract state with [`migration ref set`](/cli/m | Code | Meaning | | --- | --- | -| `0` | The database was signed. | -| `2` | The command could not run: unresolvable contract reference, no emitted contract, or unreachable database. | -| `4` | Schema verification failed and no signature was written. | +| `0` | Every contract space was signed. | +| `2` | The command could not run: unresolvable contract reference, no emitted contract, or unreachable database. Also used when it signed the database but could not write every ref. | +| `4` | Schema verification failed for a space, or another process changed that space's signature while `db sign` ran. That space's signature was not written. | + +## JSON output [#json-output] + +With `--json`, `db sign` prints one document: + +```json title="db sign --json output" +{ + "ok": true, + "summary": "Database signed", + "spaces": [ + { + "space": "app", + "status": "updated", + "contract": { "storageHash": "…", "profileHash": "…" }, + "previous": { "storageHash": "…", "profileHash": "…" } + } + ], + "advancedRefs": [{ "space": "app", "name": "db", "hash": "…" }] +} +``` + +`spaces` has one entry per contract space, with the hashes of its contract in `contract`. `status` is one of: + +| Status | Meaning | +| --- | --- | +| `created` | The space had no signature, and now has one. | +| `updated` | The signature held other hashes, now in `previous`. | +| `unchanged` | The signature already held the contract's hashes. | +| `failed` | Verification failed. The entry has `schema`, the verification result, and `ok` is `false`. | +| `conflict` | Another process, such as `db migrate`, changed the signature while `db sign` ran. The entry has `expected` and `found`, the hashes `db sign` read and the ones it found. Run `db sign` again once that process has finished. | + +`advancedRefs` lists each ref the command moved. It is empty with `--no-advance-ref`. + +Since `8.0.0-rc.15`, the document has this shape. Before, it was `{ ok, summary, contract, target, marker }` for the application's space only. ## Example diff --git a/apps/docs/content/docs/cli/db-verify.mdx b/apps/docs/content/docs/cli/db-verify.mdx index eaebf0e349b..2e0d41c953d 100644 --- a/apps/docs/content/docs/cli/db-verify.mdx +++ b/apps/docs/content/docs/cli/db-verify.mdx @@ -56,7 +56,7 @@ npx prisma db verify --db "$DATABASE_URL" --json | --- | --- | | Marker mismatch | The database was not signed for the emitted contract, or the contract changed after signing. | | Schema mismatch | The live database does not satisfy the emitted contract. | -| Strict mismatch | The database has extra schema elements not present in the contract. | +| Strict mismatch | The database has extra schema elements not present in the contract. With `--strict`, tables that no contract declares are reported as `CONTRACT.SCHEMA_VERIFICATION_FAILED`, and the result's `unclaimed` list names them. Before `8.0.0-rc.15` they were reported as `CONTRACT.MARKER_REQUIRED`, which now only means the database has not been signed. | | Extension mismatch | The contract requires an extension that is not wired in the config. | Fix the database or contract, emit again if needed, then verify again. diff --git a/apps/docs/content/docs/cli/orm-init.mdx b/apps/docs/content/docs/cli/orm-init.mdx index 456434ccf41..89f019325f5 100644 --- a/apps/docs/content/docs/cli/orm-init.mdx +++ b/apps/docs/content/docs/cli/orm-init.mdx @@ -129,6 +129,10 @@ It does not write a starter contract, and it changes nothing in `prisma/` or in To finish, set `DATABASE_URL` and run `npx prisma db sign` once. [`db sign`](/cli/db-sign) checks that the database matches the contract and then writes the marker, a row that records which contract the database matches. The row goes in the table `prisma_contract.marker`, which the first `db sign` creates in a PostgreSQL schema of its own named `prisma_contract`, so your own tables do not change. After each Prisma ORM 7 migration, whether you ran `npx prisma7 migrate dev` or `npx prisma7 migrate deploy`, run `npx prisma contract emit` and then `npx prisma db sign` again. [Configuration](/cli/configuration#use-a-prisma-orm-7-schema) explains `prisma7Schema`, and [the upgrade guide](/guides/upgrade-prisma-orm/postgresql) covers the rest of the move. +## On a Prisma ORM 6 MongoDB project + +A plain `orm init` in a project whose `schema.prisma` has `provider = "mongodb"` stops before it changes anything, with `CLI.INIT_PRISMA6_SCHEMA_FOUND`. It prints the steps to run Prisma ORM 8 beside Prisma ORM 6, with Prisma ORM 8 reading the schema through [`prisma6Schema`](/cli/configuration#use-a-prisma-orm-6-mongodb-schema). It does not set this up itself, because both CLIs are published as `prisma` and the Prisma ORM 6 CLI also reads `prisma.config.ts`. Passing `--target` and `--authoring` writes a Prisma ORM 8 starter in the same project instead. Its install step replaces the Prisma ORM 6 CLI with `prisma@latest`, and the `prisma.config.ts` it writes makes every Prisma ORM 6 command fail until Prisma ORM 6 gets its own config file. The side-by-side setup avoids both: it keeps the Prisma ORM 6 CLI under the package alias `prisma6` (`"prisma6": "npm:prisma@"`), run through a `prisma6` script with its own config file. + ## Examples ```npm diff --git a/apps/docs/content/docs/guides/deployment/cloudflare-workers.mdx b/apps/docs/content/docs/guides/deployment/cloudflare-workers.mdx index b2bf5d0c109..420b94769e8 100644 --- a/apps/docs/content/docs/guides/deployment/cloudflare-workers.mdx +++ b/apps/docs/content/docs/guides/deployment/cloudflare-workers.mdx @@ -95,7 +95,7 @@ Without `--yes`, the command asks for the contract authoring style and the schem ```json no-copy {"kind":"step-finished","step":"npm add @prisma/orm-postgres dotenv","outcome":"ok"} {"kind":"step-finished","step":"npm add -D prisma@latest","outcome":"ok"} -{"kind":"step-finished","step":"npm add -D @prisma/cli-engine@0.6.2","outcome":"ok"} +{"kind":"step-finished","step":"npm add -D @prisma/cli-engine@0.6.3","outcome":"ok"} {"kind":"step-finished","step":"Emit the contract","outcome":"ok"} {"kind":"result","envelope":{"ok":true,"result":{"target":"postgres","authoring":"psl","schemaPath":"src/prisma/contract.prisma","filesWritten":["src/prisma/contract.prisma","prisma.config.ts","src/prisma/db.ts","prisma-8.md",".env.example","tsconfig.json",".gitignore",".gitattributes","package.json","README.md"]}}} ``` diff --git a/apps/docs/content/docs/guides/deployment/docker.mdx b/apps/docs/content/docs/guides/deployment/docker.mdx index ffbe6ed159a..04b1363afef 100644 --- a/apps/docs/content/docs/guides/deployment/docker.mdx +++ b/apps/docs/content/docs/guides/deployment/docker.mdx @@ -90,7 +90,7 @@ package.json declares "type": "commonjs" ... the scaffolded prisma/db.ts uses an If you want the default, set "type": "module" in package.json. ✔ npm add @prisma/orm-postgres dotenv ✔ npm add -D prisma@latest @types/node -✔ npm add -D @prisma/cli-engine@0.6.2 +✔ npm add -D @prisma/cli-engine@0.6.3 ✔ Emit the contract │ target: postgres │ authoring: psl diff --git a/apps/docs/content/docs/guides/deployment/pnpm-workspaces.mdx b/apps/docs/content/docs/guides/deployment/pnpm-workspaces.mdx index 55093b30efe..e4b90779c36 100644 --- a/apps/docs/content/docs/guides/deployment/pnpm-workspaces.mdx +++ b/apps/docs/content/docs/guides/deployment/pnpm-workspaces.mdx @@ -95,8 +95,8 @@ Answer the prompts: choose `PSL` for the authoring style and keep the other defa ✔ pnpm add @prisma/orm-postgres dotenv ▸ pnpm add -D prisma@latest @types/node ✔ pnpm add -D prisma@latest @types/node -▸ pnpm add -D @prisma/cli-engine@0.6.2 -✔ pnpm add -D @prisma/cli-engine@0.6.2 +▸ pnpm add -D @prisma/cli-engine@0.6.3 +✔ pnpm add -D @prisma/cli-engine@0.6.3 ▸ Emit the contract ✔ Emit the contract │ target: postgres diff --git a/apps/docs/content/docs/guides/frameworks/react-router-7.mdx b/apps/docs/content/docs/guides/frameworks/react-router-7.mdx index 86e2131f4b4..baeffc07cd9 100644 --- a/apps/docs/content/docs/guides/frameworks/react-router-7.mdx +++ b/apps/docs/content/docs/guides/frameworks/react-router-7.mdx @@ -69,7 +69,7 @@ npx prisma@latest orm init --yes --target postgres --authoring psl Updated tsconfig.json with required compiler options. npm add @prisma/orm-postgres dotenv npm add -D prisma@latest -npm add -D @prisma/cli-engine@0.6.2 +npm add -D @prisma/cli-engine@0.6.3 Emit the contract filesWritten: src/prisma/contract.prisma, prisma.config.ts, src/prisma/db.ts, diff --git a/apps/docs/content/docs/guides/frameworks/solid-start.mdx b/apps/docs/content/docs/guides/frameworks/solid-start.mdx index aa87ae7f80a..ae3916e3104 100644 --- a/apps/docs/content/docs/guides/frameworks/solid-start.mdx +++ b/apps/docs/content/docs/guides/frameworks/solid-start.mdx @@ -71,7 +71,7 @@ npx prisma@latest orm init --yes --target postgres --authoring psl Updated tsconfig.json with required compiler options. ✔ npm add @prisma/orm-postgres dotenv ✔ npm add -D prisma@latest @types/node -✔ npm add -D @prisma/cli-engine@0.6.2 +✔ npm add -D @prisma/cli-engine@0.6.3 ✔ Emit the contract │ target: postgres │ authoring: psl @@ -395,9 +395,9 @@ project: ~/my-solid-prisma-app check: enabled Skill Package Version Installed into -prisma-8 @prisma/orm-postgres 8.0.0-rc.14 .claude/skills, .cursor/skills, .agents/skills, .devin/skills +prisma-8 @prisma/orm-postgres 8.0.0-rc.16 .claude/skills, .cursor/skills, .agents/skills, .devin/skills prisma-composer-core-concepts @prisma/composer 0.25.0 .claude/skills, .cursor/skills, .agents/skills, .devin/skills -prisma-platform-core-concepts prisma 8.0.0-rc.20 .claude/skills, .cursor/skills, .agents/skills, .devin/skills +prisma-platform-core-concepts prisma 8.0.0-rc.21 .claude/skills, .cursor/skills, .agents/skills, .devin/skills ⚠ [INIT.CONFIG_KEPT] prisma.config.ts already exists, so init left it alone instead of writing the skills section. → Add skills: { agents: ["claude", "cursor", "agents", "devin"] } to the object passed to definePrismaConfig in prisma.config.ts. diff --git a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-drizzle.mdx b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-drizzle.mdx index 49e876c1dd1..47b8f942c00 100644 --- a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-drizzle.mdx +++ b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-drizzle.mdx @@ -37,7 +37,7 @@ To delegate this guide to your coding agent, copy the prompt below and hand it o Migrate this Drizzle project to Prisma ORM without changing the database. 1. Run `npx prisma@latest orm init --target postgres` in the project root, choose PSL and the default schema path. Then run `npx prisma@latest init` so the Prisma agent skills are installed, and use them. Keep the existing `DATABASE_URL` in `.env`; Drizzle and Prisma ORM use the same connection string format. -2. Run `npx prisma contract infer --output ./src/prisma/contract.prisma` to read the live schema into `src/prisma/contract.prisma`. Review it with me: singularize the model names (keep the `@@map` attributes so the table names do not change), replace every `Timestamptz` field type with `TimestamptzString`, and for each many-to-many join model add a direct list field on both sides (for example `categories Category[]` on `Post` and `posts Post[]` on `Category`). +2. Run `npx prisma contract infer --output ./src/prisma/contract.prisma` to read the live schema into `src/prisma/contract.prisma`. Review it with me: singularize the model names (keep the `@@map` attributes so the table names do not change), and for each many-to-many join model add a direct list field on both sides (for example `categories Category[]` on `Post` and `posts Post[]` on `Category`). 3. Run `npx prisma contract emit`, then `npx prisma db sign`, then `npx prisma db verify`. All three must succeed before you touch application code. 4. Replace the Drizzle queries one route at a time following https://www.prisma.io/docs/guides/switch-to-prisma-orm/from-drizzle.md: import `db` from `src/prisma/db.ts`, use `db.orm.public.` with `.include(...)`, `.create(...)`, `.where(...).update(...)` and `.where(...).delete(...)`. Run each route with curl before and after, and show me the responses. 5. When no file imports `drizzle-orm` any more, remove `src/db/`, `drizzle.config.ts` and the Drizzle packages, and confirm `next build` passes. @@ -176,7 +176,7 @@ Choose `PSL` as the authoring style and keep the default schema path, `src/prism ```text no-copy ✔ npm add @prisma/orm-postgres dotenv ✔ npm add -D prisma@latest -✔ npm add -D @prisma/cli-engine@0.6.2 +✔ npm add -D @prisma/cli-engine@0.6.3 ✔ Emit the contract │ target: postgres │ authoring: psl @@ -263,11 +263,11 @@ namespace public { } model Users { - id Int @id(map: "users_pkey") @default(autoincrement()) - email String @unique(map: "users_email_unique") + id Int @id(map: "users_pkey") @default(autoincrement()) + email String @unique(map: "users_email_unique") name String? - role pg.enum(Role) @default("USER") - createdAt Timestamptz @default(now()) @map("created_at") + role pg.enum(Role) @default("USER") + createdAt TimestamptzString @default(now()) @map("created_at") posts Posts[] profiles Profiles? @@ -280,7 +280,7 @@ namespace public { content String? published Boolean @default(false) authorId Int @map("author_id") - createdAt Timestamptz @default(now()) @map("created_at") + createdAt TimestamptzString @default(now()) @map("created_at") postsToCategories PostsToCategories[] author Users @relation(fields: [authorId], references: [id], onDelete: Cascade, map: "posts_author_id_users_id_fk", index: false) @@ -320,30 +320,22 @@ Here is how each part of the Drizzle schema came through: - **Names.** Model names are PascalCase versions of the table names, so they are plural (`Users`, `Posts`); `@@map` keeps the real table name. Snake case columns become camelCase fields with `@map`, so `created_at` is `createdAt`. - **Constraints and defaults.** Primary keys, unique constraints and foreign keys keep their database names through `map:`. `serial` columns become `Int @id @default(autoincrement())`. The `default(false)` and `defaultNow()` calls come back as `@default(false)` and `@default(now())`. - **Enum.** The `pgEnum("role", ...)` type is a `native_enum Role` block with `@@map("role")`, and the column uses it as `pg.enum(Role) @default("USER")`. The TypeScript type of the field is `'USER' | 'ADMIN'`. -- **Timestamps.** `timestamp(..., { withTimezone: true })` columns are typed `Timestamptz`. You change this in the next step. +- **Timestamps.** `timestamp(..., { withTimezone: true })` columns are typed `TimestamptzString`. The next step covers what that means for your code. - **Relations.** Each `.references(...)` is an `@relation(...)` on the side that holds the foreign key, with `onDelete: Cascade` carried over. `index: false` records that Drizzle created no separate index for the foreign key. The list side (`posts Posts[]`) is derived for you; the `relations(...)` helpers in `schema.ts` have no equivalent because the contract already knows both directions. - **One-to-one.** The unique `user_id` on `profiles` came through as `profiles Profiles?` on `Users` and `user Users` on `Profiles`. - **Many-to-many.** The join table is an explicit `PostsToCategories` model with a composite `@@id`, and `Posts` and `Categories` each hold a list of the join model, not of each other. Prisma ORM has no implicit many-to-many; the [many-to-many section](#many-to-many-relations-in-prisma-8) below shows how to add the direct traversal. ## 3. Review the contract -Inference is a starting point. Make three edits before you emit. +Inference is a starting point. Make two edits before you emit, and check how timestamps come back. ### 3.1. Singularize the model names Rename the models to singular PascalCase and keep every `@@map`, so the table names do not change: `Categories` becomes `Category`, `Users` becomes `User`, `Posts` becomes `Post`, `Profiles` becomes `Profile`, and `PostsToCategories` becomes `PostToCategory`. Update the relation field types to match, and rename the `profiles` field on `User` to `profile`. -### 3.2. Use string timestamps - -Change both `Timestamptz` fields to `TimestamptzString`. `Timestamptz` reads and writes through the `Temporal` API, which Node.js does not ship yet; with the inferred type, the first query that touches a timestamp fails: - -```text no-copy -Error [StructuredError]: Codec 'pg/timestamptz-temporal@1' cannot decode a value because this runtime has no global Temporal implementation. - code: 'RUNTIME.TEMPORAL_UNAVAILABLE', - fix: "Run on a runtime with Temporal available, install a Temporal polyfill before creating the client, or author the column with its *String type to read and write PostgreSQL's own text instead." -``` +### 3.2. Timestamps are strings -`TimestamptzString` keeps the `timestamptz` column exactly as it is and hands your code the value as a string. Drizzle gave you a `Date` here, so a JSON response changes from `"2026-09-10T15:32:51.298Z"` to `"2026-09-10 21:32:51.298482+06"`. If a client depends on the ISO form, convert with `new Date(value).toISOString()` at the edge. +`contract infer` typed both timestamp fields `TimestamptzString`. It keeps the `timestamptz` column exactly as it is and hands your code the value as a string, so your app needs no `Temporal`. Drizzle gave you a `Date` here, so a JSON response changes from `"2026-09-10T15:32:51.298Z"` to `"2026-09-10 21:32:51.298482+06"`. If a client depends on the ISO form, convert with `new Date(value).toISOString()` at the edge. ### 3.3. Add direct many-to-many fields @@ -808,11 +800,11 @@ await db.orm.public.Post.where({ id: postId }).update({ :::warning -If you edit the contract after signing, `db verify` fails with `CONTRACT.MARKER_MISMATCH` (`Contract storageHash does not match database marker`). When the database still matches the new contract, as with a type change from `Timestamptz` to `TimestamptzString`, run `contract emit` and then `db sign` again; the signature moves from the old hash to the new one. When the change needs new columns or tables, use `db update` or `migration plan` instead of re-signing. +If you edit the contract after signing, `db verify` fails with `CONTRACT.MARKER_MISMATCH` (`Contract storageHash does not match database marker`). When the database still matches the new contract, as with a type change from `TimestamptzString` to `Timestamptz`, run `contract emit` and then `db sign` again; the signature moves from the old hash to the new one. When the change needs new columns or tables, use `db update` or `migration plan` instead of re-signing. ::: -- Inferred `Timestamptz` fields fail at query time with `RUNTIME.TEMPORAL_UNAVAILABLE` on Node.js. Change them to `TimestamptzString` before you emit, as in step 3.2. +- Inferred `TimestamptzString` fields return strings, not `Date` objects, as step 3.2 shows. - `.count()` is only valid inside an `.include(...)` callback. To count rows, use `.aggregate((a) => ({ total: a.count() }))`. - In a long-running server, do not call `db.close()` in route handlers; the connection pool is shared across requests. Close it only on process shutdown, and never in a Next.js route. - Bare `.update()` and `.delete()` need a `.where(...)` and touch one row. Use `.updateAll(...)` and `.deleteAll()` for many rows. diff --git a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-mongoose.mdx b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-mongoose.mdx index bba9b3438f3..e7eeda6562a 100644 --- a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-mongoose.mdx +++ b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-mongoose.mdx @@ -146,7 +146,7 @@ Choose `PSL` when asked for the contract authoring style and keep the default sc ```text no-copy ✔ npm add @prisma/orm-mongo dotenv ✔ npm add -D prisma@latest -✔ npm add -D @prisma/cli-engine@0.6.2 +✔ npm add -D @prisma/cli-engine@0.6.3 ✔ Emit the contract │ target: mongodb │ authoring: psl @@ -165,7 +165,7 @@ installed ├─ @prisma/orm-mongo ├─ dotenv ├─ prisma@latest (dev) -└─ @prisma/cli-engine@0.6.2 (dev) +└─ @prisma/cli-engine@0.6.3 (dev) ✔ Done. Open prisma-8.md to get started. ``` diff --git a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-sql-orms.mdx b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-sql-orms.mdx index c948d723c0c..c5a8ba80abd 100644 --- a/apps/docs/content/docs/guides/switch-to-prisma-orm/from-sql-orms.mdx +++ b/apps/docs/content/docs/guides/switch-to-prisma-orm/from-sql-orms.mdx @@ -34,7 +34,7 @@ To delegate this guide to your coding agent, copy the prompt below and hand it o Add Prisma ORM to this Sequelize or TypeORM project so I can migrate queries incrementally. 1. From the project root, run `npx prisma@latest orm init --yes --target postgres --authoring psl`, then `npx prisma@latest init` so the Prisma agent skills are installed, and use them. Before you run `orm init`, note whether package.json has "type": "module". If it does not, and the code uses `require` or compiles to `require` calls, the app is CommonJS: after `orm init`, apply the changes under "In a CommonJS project" in https://www.prisma.io/docs/cli/orm-init.md. Make sure `.env` contains the DATABASE_URL the app already uses. -2. Run `npx prisma contract infer --output ./src/prisma/contract.prisma` to read the existing tables into a contract. Open the file, change every `Timestamptz` field type to `TimestamptzString`, and keep the `@@map` attributes so model names stay clean while table names stay as they are. +2. Run `npx prisma contract infer --output ./src/prisma/contract.prisma` to read the existing tables into a contract. Open the file and keep the `@@map` attributes so model names stay clean while table names stay as they are. 3. Run `npx prisma contract emit`, then `npx prisma db sign`, then `npx prisma db verify`. Do not run `db init` or `db update`; the tables already exist. 4. Following https://www.prisma.io/docs/guides/switch-to-prisma-orm/from-sql-orms.md, rewrite the read queries first, then the writes, importing `db` from `src/prisma/db.ts`. Run each rewritten query with `node