Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,14 @@ The part after the last `/`, and before any `?`, is the name of the database.
```typescript title="src/prisma/db.ts"
import 'dotenv/config';
import mongo from '@prisma/orm-mongo/runtime';
import { contractFromJson } from '@prisma/orm-mongo/static';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = mongo<Contract>({
contractJson,
export const contract = contractFromJson<Contract>(contractJson);

export const db = mongo({
contract,
url: process.env['DATABASE_URL']!,
});
```
Expand Down Expand Up @@ -106,7 +109,7 @@ model Post {
}
```

Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `@@map("users")` names the collection that stores the model. MongoDB stores the identifier of every document in `_id`. On MongoDB, `contract emit` names each field in `contract.json` by the name in its `@map`, so in queries and in results you use `_id`, not `id`. The same goes for any other field with `@map`.
Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `@@map("users")` names the collection that stores the model. MongoDB stores the identifier of every document in `_id`, and `@map("_id")` stores the `id` field there. In `db.orm` queries and in their results you use the field name, `id`, and Prisma ORM reads and writes `_id` for you. The same goes for any other field with `@map`.

Change the models to match your documents: one model for each collection, with one field for each field of its documents. [Data modeling](/orm/data-modeling) lists the field types.

Expand Down Expand Up @@ -151,7 +154,7 @@ npx tsx script.ts
```text no-copy
{
username: null,
_id: '6abca6a1e7a9bb8d8c716f65',
id: '6abca6a1e7a9bb8d8c716f65',
email: 'existing@example.com',
name: 'Existing User'
}
Expand All @@ -163,6 +166,8 @@ The document has no `username` field, and `username` is optional in the contract

`db.query` builds a MongoDB aggregation pipeline on a collection, which it names as it is stored in MongoDB. `.build()` returns the pipeline, and `runtime.query()` runs it, where `runtime` is the object that `await db.runtime()` returns. Replace `users`, `email`, and the value with your own again.

A pipeline reads documents as MongoDB stores them, so it uses stored names, not the field names in your schema. Write `_id`, not `id`, and use the name in `@map` for every other field that has one.

Replace `script.ts` with this version:

```typescript title="script.ts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,14 @@ DATABASE_URL="postgres://username:password@host:5432/database?sslmode=require"
```typescript title="src/prisma/db.ts"
import 'dotenv/config';
import postgres from '@prisma/orm-postgres/runtime';
import { contractFromJson } from '@prisma/orm-postgres/static';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = postgres<Contract>({
contractJson,
export const contract = contractFromJson<Contract>(contractJson);

export const db = postgres({
contract,
url: process.env['DATABASE_URL']!,
});
```
Expand Down Expand Up @@ -290,7 +293,7 @@ npx tsx script.ts

## 8. Query a table with `db.sql`

`db.sql` builds SQL queries: where `db.orm` names models and fields, `db.sql` names tables and columns as they are in the database. This example reads the table `user`, which the `User` model maps with `@@map("user")`. `.build()` returns the query without running it, and `db.runtime().query()` runs it.
`db.sql` builds SQL queries: where `db.orm` names models and fields, `db.sql` names tables and columns as they are in the database. This example reads the table `user`, which the `User` model maps with `@@map("user")`. `.build()` returns the query without running it, and `db.query()` runs it.

Replace `script.ts` with this version:

Expand All @@ -303,7 +306,7 @@ async function main() {
.limit(2)
.build();

const rows = await db.runtime().query(query);
const rows = await db.query(query);
console.log(rows);

await db.close();
Expand Down
15 changes: 11 additions & 4 deletions apps/docs/content/docs/(index)/prisma-orm/from-scratch.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ 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.24 and `@prisma/orm-postgres` 8.0.0-rc.18); 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`.

`temporal-polyfill` is a peer dependency of `@prisma/orm-postgres` too. It adds `Temporal`, the newer JavaScript date and time API, to runtimes such as Node.js 24 that do not have it built in. npm installs it for you, but with Yarn you add it yourself.

The two Prisma packages have different version numbers, for example `prisma` 8.0.0-rc.24 and `@prisma/orm-postgres` 8.0.0-rc.19. That is normal, and any two `latest` versions work together.

## 2. Create the config file and `.env`

Expand Down Expand Up @@ -134,16 +138,19 @@ The `marker` line in the output is the record that signing stores in the databas

## 5. Write and read data

Create `index.ts`, which builds the client from the two emitted files and then deletes, creates, updates, and reads rows on the `User` model:
Create `index.ts`, which passes the two files from step 4 to `contractFromJson`. That function checks `contract.json` and gives it the types from `contract.d.ts`, and the file then builds the client from the result and deletes, creates, updates, and reads rows on the `User` model:

```typescript title="index.ts"
import "dotenv/config";
import postgres from "@prisma/orm-postgres/runtime";
import { contractFromJson } from "@prisma/orm-postgres/static";
import type { Contract } from "./prisma/contract.d";
import contractJson from "./prisma/contract.json" with { type: "json" };

const db = postgres<Contract>({
contractJson,
const contract = contractFromJson<Contract>(contractJson);

const db = postgres({
contract,
url: process.env["DATABASE_URL"]!,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,14 @@ You do not need `prisma-8.md` for this page. It is a short reference for writing
```typescript title="src/prisma/db.ts"
import 'dotenv/config';
import mongo from '@prisma/orm-mongo/runtime';
import { contractFromJson } from '@prisma/orm-mongo/static';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = mongo<Contract>({
contractJson,
export const contract = contractFromJson<Contract>(contractJson);

export const db = mongo({
contract,
url: process.env['DATABASE_URL']!,
});
```
Expand Down Expand Up @@ -117,7 +120,7 @@ model User {
}
```

Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `@@map("users")` names the collection that stores the model. MongoDB stores the identifier of every document in `_id`. On MongoDB, `contract emit` names each field in `contract.json` by the name in its `@map`, so in queries and in results you use `_id`, not `id`. The same goes for any other field with `@map`. The file has no `datasource` or `generator` block. The connection string comes from `.env`, and `contract emit` in the next step takes the place of the generator. [Data modeling](/orm/data-modeling) covers field types and relations.
Keep the first line, because `contract emit` reads only `.prisma` files that start with it. `@@map("users")` names the collection that stores the model. MongoDB stores the identifier of every document in `_id`, and `@map("_id")` stores the `id` field there. In queries and in their results you use the field name, `id`, and Prisma ORM reads and writes `_id` for you. The same goes for any other field with `@map`. The file has no `datasource` or `generator` block. The connection string comes from `.env`, and `contract emit` in the next step takes the place of the generator. [Data modeling](/orm/data-modeling) covers field types and relations.

## 4. Generate the files your code imports

Expand Down Expand Up @@ -206,20 +209,20 @@ npx tsx script.ts

```text no-copy
Created: {
_id: '6abc2ce7b04f66be2c85564d',
id: '6abc2ce7b04f66be2c85564d',
email: 'alice@example.com',
name: 'Alice'
}
All users: [
{
_id: '6abc2ce7b04f66be2c85564d',
id: '6abc2ce7b04f66be2c85564d',
email: 'alice@example.com',
name: 'Alice'
}
]
```

The script passes no identifier, and the new document gets one in `_id`, returned as a string. To find a document by it, filter on `_id`, as in `db.orm.users.where({ _id: created._id }).first()`.
The script passes no identifier, and the new document gets one, returned as a string in `id`. To find a document by it, filter on `id`, as in `db.orm.users.where({ id: created.id }).first()`.

In your app, import `db` from `src/prisma/db.ts` the same way. [Reading data](/orm/fundamentals/reading-data) and [Writing data](/orm/fundamentals/writing-data) show the other queries.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,14 @@ You do not need `prisma-8.md` for this page. It is a short reference for writing
```typescript title="src/prisma/db.ts"
import 'dotenv/config';
import postgres from '@prisma/orm-postgres/runtime';
import { contractFromJson } from '@prisma/orm-postgres/static';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };

export const db = postgres<Contract>({
contractJson,
export const contract = contractFromJson<Contract>(contractJson);

export const db = postgres({
contract,
url: process.env['DATABASE_URL']!,
});
```
Expand Down
18 changes: 16 additions & 2 deletions apps/docs/content/docs/cli/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,25 @@ export default definePrismaConfig({
});
```

`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.
`prisma7Schema` takes one schema file, or a directory of `.prisma` files as Prisma ORM 7 reads it. `prisma7Schema` 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. `orm init --from-prisma7-schema` also writes this config for you.

Prisma ORM 7 gave your application date and time values as JavaScript `Date` objects. With `prisma7Schema`, Prisma ORM 8 gives them as strings instead. This applies to a `DateTime` field and to a field with `@db.Timestamp`, `@db.Timestamptz`, `@db.Date`, or `@db.Time`: each value is the text PostgreSQL prints, such as `"2026-09-14 10:00:00.123"`. A `DateTime` value is in UTC, as Prisma ORM 7 writes it.

Prisma ORM 8 can also give these values as `Temporal` objects. `Temporal` is the newer JavaScript date and time API, and runtimes without it, such as Node.js 24, need the `temporal-polyfill` package. To get `Temporal` values, write a Prisma ORM 8 contract with `npx prisma contract print --output prisma/contract.prisma`, and in that file change `TimestampString(3)` to `Timestamp(3)`, `TimestamptzString(3)` to `Timestamptz(3)`, `DateString` to `Date`, and `TimeString(3)` to `Time(3)`. Then change `contract` in `prisma.config.ts` from `prisma7Schema("prisma/schema.prisma")` to `"./prisma/contract.prisma"`, so that `contract emit` reads the edited file.

When your code creates or updates a row, Prisma ORM fills each `@updatedAt` field with the time from your application's clock, as text in the same form. This also works on a `@db.Date`, `@db.Time`, or `@db.Timetz` field. A field can have both `@updatedAt` and `@default(now())`, and `@default(now())` stays the column's default in the database.

A list such as `String[]` reads as a list whose elements may be `null`, because the database that Prisma ORM 7 built does not stop a list from holding `null` elements.

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.

`contract emit` fails on any part of the schema that Prisma ORM 8 cannot represent exactly, such as a `view` block, `Unsupported(...)`, or `relationMode = "prisma"`. The error names the file and the line and suggests an edit to the Prisma ORM 7 schema. When that edit would also change the database on the next Prisma ORM 7 migration, the error says so. For example, removing an `Unsupported(...)` field drops its column, so for that field the error suggests adding `@@ignore` to the model instead, which leaves the next Prisma ORM 7 migration empty but removes the model from the Prisma ORM 7 client. Prisma ORM 8 has no views, so for a `view` block the error suggests removing the view or replacing it with a model over the table the view reads. `orm init --from-prisma7-schema` writes this config for you.
An `Unsupported("ltree")` field becomes a column of that database type with no field on the model, as in the Prisma ORM 7 client: migrations and `db verify` keep the column, and your code cannot read or write it. `contract print` cannot write such a column yet, so a schema with `Unsupported(...)` fields stays your contract source.

`contract emit` fails on any part of the schema that Prisma ORM 8 cannot represent exactly, such as a `view` block or `relationMode = "prisma"`. The error names the file and the line and suggests an edit to the Prisma ORM 7 schema. When that edit would also change the database on the next Prisma ORM 7 migration, the error says so.

Prisma ORM 8 has no views, so for a `view` block the error suggests removing the view or replacing it with a model over the table the view reads.

`contract emit` also fails when a default function gets an argument it does not take, such as `uuid(5)` or `cuid(1)`. The error code is `PSL.PRISMA7_UNKNOWN_DEFAULT`, and the message names the values the function takes, for example `uuid: version must be 4 or 7, got 5`.

## Use a Prisma ORM 6 MongoDB schema

Expand Down
4 changes: 4 additions & 0 deletions apps/docs/content/docs/cli/contract-emit.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ npx prisma db sign --db "$DATABASE_URL"

The script runs on any Node.js version Prisma ORM supports: 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer. 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 you upgrade to `8.0.0-rc.19`, run `contract emit` again, because the files an earlier release wrote do not work with this one. A SQL `contract.d.ts` from an earlier release no longer compiles. The CLI refuses every older MongoDB `contract.json`, and some older PostgreSQL ones, with `CONTRACT.VALIDATION_FAILED`.

Then look at the `storageHash` value in `contract.json`. It identifies the tables and columns your contract describes, and `db sign` records it in each database. If the new emit changed it, run [`db sign`](/cli/db-sign) against every database that uses the contract, such as development, staging, and production. Until you do, `db migrate` fails with `MIGRATION.MARKER_MISMATCH` and `db verify` fails with `CONTRACT.MARKER_MISMATCH`, because the database still records the old value. The [upgrade instructions](https://github.com/prisma/orm/blob/v8.0.0-rc.19/skills/prisma-8/upgrading/app/upgrades/8.0.0-rc.18-to-8.0.0-rc.19/) include scripts that update the copies of the contract stored under `migrations/`.

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`.
Expand Down
Loading
Loading