# Prisma Documentation - Full Content Feed This file contains the current Prisma documentation in machine-readable format. Legacy Prisma ORM v6 content is not included here; fetch any v6 page directly by appending `.md` to its URL (for example, https://www.prisma.io/docs/orm/v6/....md). For the documentation index, see https://www.prisma.io/docs/llms.txt. --- # Start a TypeScript app with Postgres on Prisma for free (/docs/full-stack-tutorial) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. From an empty directory to a live TypeScript app with a Prisma Postgres database on Prisma Compute, with the Free plan limits listed. Location: Start a TypeScript app with Postgres on Prisma for free The fastest path to a TypeScript app with a Postgres database on Prisma is the CLI: it scaffolds the app, runs it on a local [Prisma Postgres](https://www.prisma.io/docs/postgres) database with no account, and, once you sign in, deploys it to [Prisma Compute](https://www.prisma.io/docs/compute) and creates its database there. Everything runs on the [Free plan](#what-the-free-plan-includes) with no credit card. The steps below do it by hand, and the prompt under [Use with your agent](#use-with-your-agent) runs the same journey end to end. [Prisma Composer](https://www.prisma.io/docs/composer) declares your app: its services, its databases, and how they connect. [Prisma ORM](https://www.prisma.io/docs/orm) types your data. Prisma Postgres stores it, locally while you develop and on the platform when you deploy. One declaration drives everything: the same `module.ts` runs the app on your machine and deploys it to Prisma Compute. Plan on about 15 minutes. It uses the `hono` template so you get a small API you can verify with curl at every step. The same journey works for the other templates; the [framework guides](https://www.prisma.io/docs/guides) cover each one. ## Prerequisites [#prerequisites] * Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (24 recommended), or Bun * If you use npm, the npm that ships with your Node.js release (npm 10 on Node.js 22) * A [Prisma Data Platform account](https://pris.ly/pdp) for the deploy step, free to create, with no credit card needed on the [Free plan](#what-the-free-plan-includes) * No database needed: local runs provision a local Prisma Postgres, and deploys provision a real one, both from your Composer declaration > [!NOTE] > Already have an app? > > Keep your code and add a small Prisma Composer declaration next to it: a `service.ts` for each server and one `module.ts`. The server reads its port from `service.port()` and binds `0.0.0.0`; [Port an existing app](https://www.prisma.io/docs/composer/porting-an-app) lists the other changes and has an agent prompt that makes them, and [Bring an app you already have](https://www.prisma.io/docs/prisma-compute/deploy#bring-an-app-you-already-have) is a worked example. Once `npm run build` and `npx prisma dev module.ts` run it, continue at [step 5](#5-deploy-app-and-database-to-prisma-compute). SvelteKit and Deno apps do not deploy to Prisma Compute yet. ## What the Free plan includes [#what-the-free-plan-includes] Everything in this tutorial runs on the Free plan: $0 a month, no credit card, no time limit, and no usage billing. The limits below are the ones on the [pricing page](https://www.prisma.io/pricing), which also lists the paid plans. They apply to your whole [workspace](https://www.prisma.io/docs/console/concepts#workspace), the billing unit of your Prisma account that holds all of your projects and databases, so each number is shared across everything in the workspace, not granted to each database. Prisma ORM is optional on both products: Prisma Postgres works with any PostgreSQL client, and Compute runs apps that do not use Prisma ORM. See [Do I need Prisma ORM to use Compute?](https://www.prisma.io/docs/compute/faq#do-i-need-prisma-orm-to-use-compute). The scaffold in this tutorial uses Prisma ORM 8, which is a release candidate; see [Release status](https://www.prisma.io/docs/orm/release-status). ### Prisma Postgres [#prisma-postgres] | Limit | Free plan | | ------------------ | --------------- | | Operations | 200,000 a month | | Storage | 2 GB | | Databases | 50 | | Data transfer | Unlimited | | Pooled connections | 10 | | Direct connections | 10 | | Automated backups | None | Each query is one operation, whether it reads or writes. See [What is an operation?](https://www.prisma.io/docs/postgres/faq#what-is-an-operation). Direct connections are the ones PostgreSQL tools such as `pg_dump` open, and 5 of the 10 are reserved for platform operations; see [Connection limits](https://www.prisma.io/docs/postgres/database/connection-pooling#connection-limits). The Free plan has no automated backups, and Prisma Postgres cannot yet restore a database to an earlier point in time, so keep your own copies with `pg_dump`, the PostgreSQL command-line backup tool, over a direct connection. See [Backups](https://www.prisma.io/docs/postgres/database/backups). ### Prisma Compute [#prisma-compute] | Meter | Free plan | | ------------------ | -------------------- | | Requests | 1,000,000 a month | | Provisioned memory | 360 GB-hours a month | | Active CPU | 4 vCPU-hours a month | | Outbound bandwidth | 10 GB a month | A GB-hour is one gigabyte of memory allocated for one hour, and a vCPU-hour is one virtual CPU in use for one hour. Memory counts only while the app is running or you have chosen to keep it awake, because an idle app scales to zero: it stops running and costs nothing while idle. Outbound bandwidth is the data your app sends out to the internet, and incoming requests do not count. It is separate from the database's unlimited data transfer: Compute includes 10 GB of outbound bandwidth a month on Free. See [Compute pricing](https://www.prisma.io/docs/compute/pricing) for what each meter measures. ### Check your usage and upgrade [#check-your-usage-and-upgrade] The Free plan has no usage billing, so reaching a limit does not add a charge. Operations and the four Compute meters are monthly limits, while storage and the number of databases are not tied to a month. To see how much a database has used, run `npx prisma postgres usage `, or track usage in the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=%28index%29); see [Billing and limits](https://www.prisma.io/docs/postgres#billing-and-limits). Usage reporting for Compute in the CLI and API is still being built. To go past a limit, move the workspace to a paid plan. Paid plans include more operations, storage, and requests, then bill usage beyond those amounts. Compute memory, CPU, and bandwidth are billed per use on paid plans, and a spend limit can cap the charge. The [pricing page](https://www.prisma.io/pricing) has the rates. ## Use with your agent [#use-with-your-agent] If you would rather hand the work to a coding agent, this prompt runs the same journey as the one on the [getting started page](https://www.prisma.io/docs), with the `hono` template chosen for you: The prompt uses npm and Node.js. On Bun, replace `npm create prisma@latest --` with `bun create prisma@latest`, `npx` with `bunx`, and `npm run` with `bun run`. ```text Create a new Hono API composed with Prisma Composer and Prisma ORM, run it locally, and deploy it to Prisma Compute. Check the Node.js version first: `node --version` must print 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer. For the nuxt template, it must print 22.22.3 or newer on the 22 line, 24.15 or newer on the 24 line, or 26 or newer, because Nuxt requires it. The npm that ships with your Node.js release works; on Node.js 22 that is npm 10. 1. Scaffold: `npm create prisma@latest -- my-app --template hono --provider postgres --yes`. Then run `npx prisma@latest init` in `my-app` to confirm the Prisma agent skills the scaffold installed are current, and use them. 2. Read `module.ts` and `service.ts` first: the Composer module provisions the database and the service, and it is what `dev` and `deploy` operate on. 3. Build and run locally: `npm run build`, then start `npx prisma dev module.ts` in the background and wait until it prints the local URL. This provisions a local Prisma Postgres database and applies the contract; no DATABASE_URL is needed. Sample users are seeded on the app's first query. Verify with curl that the local URL's /users endpoint returns the seeded users, then stop `dev`. 4. Deploy: check `npx prisma auth whoami`; if I am not signed in, stop and ask me to run `npx prisma auth login` (it opens a browser). The scaffold already wrote the baseline migration under `migrations/app/`; note its directory name, which ends in `_init`. The `composer` section of the scaffold's `prisma.config.ts` already sets the deploy region, so do not add one. Then run `npx prisma deploy module.ts` and verify the live URL's /users endpoint with curl. The deploy creates the project and provisions the database from the module declaration; do not pass a DATABASE_URL. 5. Evolve the schema: add `role String @default("member")` to the User model in `src/prisma/contract.prisma`, run `npx prisma contract emit`, add `"role"` to the typed select and the returned object in `src/prisma/users.ts`, and plan the migration with `npx prisma migration plan --name add-user-role --from `. Then run `npm run build` and `npx prisma deploy module.ts` again, and verify the live /users now returns `role: "member"` with the same createdAt values as before. ``` ## 1. Scaffold the app [#1-scaffold-the-app] One command creates a Composer-declared app with Prisma ORM wired in: #### bun ```bash bun create prisma@latest my-app --template hono --provider postgres --no-deploy ``` #### pnpm ```bash pnpm create prisma@latest my-app --template hono --provider postgres --no-deploy ``` #### yarn ```bash yarn create prisma@latest my-app --template hono --provider postgres --no-deploy ``` #### npm ```bash npm create prisma@latest -- my-app --template hono --provider postgres --no-deploy ``` Answer the prompts for contract authoring style, package manager, and agent skills, or pass the `--authoring`, `--package-manager`, and `--skills` flags to skip them (see the [create-prisma reference](https://www.prisma.io/docs/prisma-orm/create-prisma); Deno projects cannot deploy to Prisma Compute yet, so this tutorial uses Node.js or Bun). Then enter the project: #### bun ```bash cd my-app ``` #### pnpm ```bash cd my-app ``` #### yarn ```bash cd my-app ``` #### npm ```bash cd my-app ``` If you work with a coding agent, run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once. The scaffold ran it already, so on a fresh project `init` confirms the setup and reports each step as already done: the [Prisma agent skills](https://www.prisma.io/docs/ai/tools/skills) that ship inside the Prisma packages are synced, and `package.json` has a `postinstall` hook (`prisma skills sync || exit 0`) that resyncs them on every install, plus a `skills:sync` script for refreshing them by hand. Running `init` again is what repairs the setup after you upgrade a Prisma package. See [`skills`](https://www.prisma.io/docs/cli/skills). ## 2. The Composer app [#2-the-composer-app] Start with the declaration, because it is what every later command operates on. The scaffold declares the whole app in two files, and `prisma.config.ts` holds the deploy config. `module.ts` is the app: it provisions a Prisma Postgres database and the service, and wires one to the other: ```ts title="module.ts" import { module } from "@prisma/composer"; import { postgres } from "@prisma/composer-prisma-cloud/orm"; import { appContract } from "./src/prisma/composer.ts"; import app from "./service.ts"; export default module("my-app", ({ provision }) => { const database = provision( postgres({ name: "database", contract: appContract, config: "./prisma.config.ts", }), { id: "database" }, ); provision(app, { deps: { database } }); }); ``` `service.ts` declares the service itself: its name, its dependency on the database, and how it is built: ```ts title="service.ts" import node from "@prisma/composer/node"; import { compute } from "@prisma/composer-prisma-cloud"; import { postgres } from "@prisma/composer-prisma-cloud/orm"; import { appContract } from "./src/prisma/composer.ts"; export default compute({ name: "app", deps: { database: postgres(appContract), }, build: node({ module: import.meta.url, entry: "./dist/server.mjs" }), }); ``` `prisma.config.ts` is this project's Prisma config: the Prisma CLI reads it, and your app never imports it. Its `orm` section points Prisma ORM at your contract and database, its `skills` section lists the agents you chose at the prompt, and the deploy config is its `composer` section: the extensions that deploy your app and the store that keeps deploy state. The `composer` section already names the region the first deploy needs to create your project, `us-east-1`, so there is nothing to set before you deploy. To deploy somewhere else, change the region here before the first deploy; the region ids are listed under [Limitations](https://www.prisma.io/docs/compute/limitations). The file below is shown for Prisma schema authoring with Claude Code picked for agent skills. Your `skills` list holds the agents you picked, and TypeScript authoring points `contract` at `contract.ts` and adds an `output` path. ```ts title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; import { defineConfig as composer } from "@prisma/composer/config"; import { nodeBuild } from "@prisma/composer/node/control"; import { prismaCloud, prismaState } from "@prisma/composer-prisma-cloud/control"; export default definePrismaConfig({ skills: { agents: ["claude"], }, orm: ormConfig({ contract: "./src/prisma/contract.prisma", db: { connection: process.env.DATABASE_URL!, }, }), composer: composer({ extensions: [prismaCloud({ region: "us-east-1" }), nodeBuild()], state: prismaState(), }), }); ``` Notice there is no connection string in the declaration. The database is a declared dependency, typed by your contract, and Composer injects the connection wherever the app runs. The `DATABASE_URL` in the `orm` section is used only by the ORM commands that connect to a database directly, such as the `db:*` scripts at the end of [step 4](#4-run-it-locally-on-prisma-postgres); `prisma dev` and `prisma deploy` do not need it. The declaration is ordinary TypeScript: `npx tsc --noEmit` checks the wiring, and mistakes fail the compile instead of a deploy. [Composer](https://www.prisma.io/docs/composer) covers the model in full: modules, services, dependencies, and the first-party building blocks. ## 3. The Prisma ORM data model [#3-the-prisma-orm-data-model] The data side lives under `src/prisma/`. Your schema is the starter contract in `src/prisma/contract.prisma`; `npm run contract:emit` compiles it into the contract artifacts your queries are type-checked against, and `src/prisma/db.ts` is the typed client the route handlers import. The `/users` route in `src/index.ts` is ordinary Hono code calling an ordinary Prisma ORM query. Change the contract when you are ready to model your own data, re-emit it, and the compiler walks you through every query the change touches. The [fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data) cover the query patterns. ## 4. Run it locally on Prisma Postgres [#4-run-it-locally-on-prisma-postgres] Build the server, then bring the whole declaration up on your machine: #### bun ```bash bun run build bunx prisma dev module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma dev module.ts ``` #### yarn ```bash yarn build yarn prisma dev module.ts ``` #### npm ```bash npm run build npx prisma dev module.ts ``` `dev` provisions a local Prisma Postgres database, applies the contract to it, starts the service, and prints the app's local URL when everything is ready. No account, credentials, or connection string are involved; see [Local development](https://www.prisma.io/docs/local-development) for how the local platform works. Sample users are seeded automatically the first time the app queries the database. Confirm the API serves the seeded rows. `dev` keeps running until you press Ctrl-C, so run the curl in a second terminal. `dev` also picks a free port and prints the URL, so use the one it printed if it differs from the `3000` shown here: ```bash curl http://localhost:3000/users ``` ```json no-copy [ { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "createdAt": "2026-08-24 13:51:34.797+00" }, { "id": "2", "email": "bob@prisma.io", "username": "bob", "name": "Bob", "createdAt": "2026-08-24 13:51:34.803+00" }, { "id": "3", "email": "carol@prisma.io", "username": "carol", "name": "Carol", "createdAt": "2026-08-24 13:51:34.804+00" } ] ``` If you prefer the framework's own dev server, `npm run dev` runs it directly. Composer does not manage that mode, so the app needs a database of your own in `DATABASE_URL`. Create one with the CLI, in a project named differently from the module so that it does not collide with the project the deploy in [step 5](#5-deploy-app-and-database-to-prisma-compute) creates: #### bun ```bash bunx prisma auth login bunx prisma project create my-app-local bunx prisma postgres create mydb ``` #### pnpm ```bash pnpm prisma auth login pnpm prisma project create my-app-local pnpm prisma postgres create mydb ``` #### yarn ```bash yarn prisma auth login yarn prisma project create my-app-local yarn prisma postgres create mydb ``` #### npm ```bash npx prisma auth login npx prisma project create my-app-local npx prisma postgres create mydb ``` `postgres create` prints the connection string once; export it as `DATABASE_URL`, and mint another later with `npx prisma postgres connection create mydb` if you need one. The `db:*` scripts in `package.json` initialize and verify that database. See [`postgres`](https://www.prisma.io/docs/cli/postgres). ## 5. Deploy app and database to Prisma Compute [#5-deploy-app-and-database-to-prisma-compute] Sign in once (it opens your browser): #### bun ```bash bunx prisma auth login ``` #### pnpm ```bash pnpm prisma auth login ``` #### yarn ```bash yarn prisma auth login ``` #### npm ```bash npx prisma auth login ``` Deployed databases evolve through the migrations you commit, so the first migration must create your starting schema. The scaffold already planned it for you as `migrations/app/_init`; commit it with your code. You will chain the next migration from it in [step 7](#7-evolve-the-data-model). Build and deploy the same declaration: #### bun ```bash bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn build yarn prisma deploy module.ts ``` #### npm ```bash npm run build npx prisma deploy module.ts ``` Composer creates a project named after your module, in the region from the `composer` section of `prisma.config.ts`, provisions the Prisma Postgres database declared in `module.ts`, connects it to the service, and starts the app. There is no environment file to pass, because the deployed database comes from the declaration exactly as the local one did. When everything is up, the deploy prints what it made, each part of your app next to the platform resource it became, along with the public URL: ```text no-copy my-app ├─ database postgres-database db_abc123 └─ app compute-service cps_abc123 https://xyz.ewr.prisma.build ``` > [!WARNING] > The module name must be unique in your workspace > > `deploy` looks the module name up in your workspace and reuses the project this module deployed before rather than creating another. If a `my-app` project exists whose hosted state the CLI cannot verify (one deployed from a different checkout, for example), the deploy stops with `HostedStateBootstrapError` and names a project id you did not choose. Deploy under a different name with [`--name`](https://www.prisma.io/docs/cli/deploy#flags), or rename the module in `module.ts`: > > > > > #### bun > ```bash > bunx prisma deploy module.ts --name my-app-tutorial > ``` > > > #### pnpm > ```bash > pnpm prisma deploy module.ts --name my-app-tutorial > ``` > > > #### yarn > ```bash > yarn prisma deploy module.ts --name my-app-tutorial > ``` > > > #### npm > ```bash > npx prisma deploy module.ts --name my-app-tutorial > ``` > > ## 6. Verify the live URL [#6-verify-the-live-url] ```bash curl https://xyz.ewr.prisma.build/users ``` The same three users come back, now served from production next to your database, seeded on the deployed app's first query. Re-deploying is safe to run more than once: build again, deploy again, and the platform applies only the difference. That includes removals, because your module is the source of truth: delete a provision from `module.ts`, deploy again, and the resource disappears from the platform. See [Removing resources](https://www.prisma.io/docs/composer/deploying#removing-resources). ## 7. Evolve the data model [#7-evolve-the-data-model] Live apps outgrow their starter schema, so give users a role. Add one line to the contract: ```prisma title="src/prisma/contract.prisma" // use prisma-8 model User { id Int @id @default(autoincrement()) email String @unique username String? name String? role String @default("member") posts Post[] createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } ``` Re-emit the contract, then plan the migration that carries the change, chaining from the baseline migration you committed in [step 5](#5-deploy-app-and-database-to-prisma-compute): #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add-user-role --from _init ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add-user-role --from _init ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add-user-role --from _init ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add-user-role --from _init ``` `--from` names the migration you are building on, and here it is required: a Composer deploy never sets the `db` ref that `migration plan` chains from by default, so a plan without it would describe every table again. The [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) and [`migration ref`](https://www.prisma.io/docs/cli/migration-ref) references cover the default, the ref, and how to keep plans chaining on their own. The plan is your change and nothing else. Review it like any other diff, with [`migration show`](https://www.prisma.io/docs/cli/migration-show) or by reading the generated package: ```text no-copy ALTER TABLE "public"."User" ADD COLUMN "role" text DEFAULT 'member' NOT NULL ``` Emitting also updated the query types, so include the new field in the route's typed select in `src/prisma/users.ts`, adding `"role"` to the `.select(...)` list and `role: user.role` to the returned object: ```ts title="src/prisma/users.ts" const users = await db.orm.public.User.select("id", "email", "username", "name", "role", "createdAt").limit(limit).all(); ``` This is the Prisma ORM loop: the contract is the source of truth, the emit step regenerates the types, and the compiler points at every query the change touches. If you want to see the change on your machine first, run `npm run build` and `npx prisma dev module.ts` again; the local database applies the new migration on start and `/users` returns the role there too. Then build and deploy again: #### bun ```bash bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn build yarn prisma deploy module.ts ``` #### npm ```bash npm run build npx prisma deploy module.ts ``` The deploy applies the committed migration to the deployed database in place. The same users come back with `role: "member"`, backfilled by the column default, and their `createdAt` timestamps unchanged; nothing was dropped or recreated: ```bash curl https://xyz.ewr.prisma.build/users ``` ```json no-copy [ { "id": "1", "email": "alice@prisma.io", "username": "alice", "name": "Alice", "role": "member", "createdAt": "2026-08-24 18:40:06.375+00" } ] ``` ## 8. Clean up (optional) [#8-clean-up-optional] The project keeps running until you remove it. Deleting it removes the service and the database, so the command asks you to repeat the project id (find it with `npx prisma project list`): #### bun ```bash bunx prisma project delete --confirm ``` #### pnpm ```bash pnpm prisma project delete --confirm ``` #### yarn ```bash yarn prisma project delete --confirm ``` #### npm ```bash npx prisma project delete --confirm ``` ## Next steps [#next-steps] * [Learn Composer](https://www.prisma.io/docs/composer/getting-started): typed contracts between services, databases, storage, and scheduled jobs. * [Pick your framework](https://www.prisma.io/docs/guides): the same journey for Next.js, Nuxt, Astro, NestJS, TanStack Start, and more. * [Branching and previews](https://www.prisma.io/docs/compute/branching): how each branch gets its own isolated environment. * [Learn the fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data): reading, writing, relations, and transactions. * [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push): connect GitHub and add a deploy workflow so every push deploys, with a preview environment per branch. ## Related pages - [`Choose a Prisma ORM setup path`](https://www.prisma.io/docs/getting-started): Choose the fastest path to try Prisma ORM in a new or existing project. - [`Console`](https://www.prisma.io/docs/console): Learn how to use the Console to manage and integrate Prisma products into your application. - [`Introduction to Prisma ORM`](https://www.prisma.io/docs/prisma-orm): Prisma ORM 8 is a release candidate. - [`Local development`](https://www.prisma.io/docs/local-development): Run the whole Prisma stack on your machine, with your app on Bun, a local Prisma Postgres database, and local object storage. - [`Overview`](https://www.prisma.io/docs/cli): Prisma CLI reference # Choose a Prisma ORM setup path (/docs/getting-started) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Choose the fastest path to try Prisma ORM in a new or existing project. Location: Choose a Prisma ORM setup path Start with a quickstart if you want [Prisma ORM](https://www.prisma.io/docs/orm) to create the app. Use the existing-project path if you already have an app and database. Coming from an earlier version? [Coming from Prisma ORM 7](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7) maps every Prisma ORM 7 API to its Prisma ORM 8 form, and [Release status](https://www.prisma.io/docs/orm/release-status) says where Prisma ORM 8 stands today. ## Prerequisites [#prerequisites] * **Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer.** Check with `node --version`. Node.js 24 is recommended. The Nuxt template needs 22.22.3 or newer on the 22 line, or 24.15 or newer on the 24 line, because Nuxt requires it. * **A package manager.** The npm that ships with your Node.js release works; on Node.js 22 that is npm 10. pnpm, Bun, and Yarn 4.10 or newer are also supported. ## Start a new project [#start-a-new-project] #### bun ```bash bun create prisma@latest ``` #### pnpm ```bash pnpm create prisma@latest ``` #### yarn ```bash yarn create prisma@latest ``` #### npm ```bash npm create prisma@latest ``` The [create-prisma reference](https://www.prisma.io/docs/prisma-orm/create-prisma) lists every template and flag. - [Start a TypeScript app with Postgres on Prisma for free](https://www.prisma.io/docs/full-stack-tutorial): The whole journey in one sitting: scaffold, Prisma Postgres, first query, and a Prisma Compute deploy. - [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql): Create the app, run it against a local Prisma Postgres from Composer or your own PostgreSQL, and run the first query. - [Create a new app with MongoDB](https://www.prisma.io/docs/prisma-orm/quickstart/mongodb): Create the app, connect a MongoDB deployment, apply the first migration, and run the first query. ## Add to an existing project [#add-to-an-existing-project] #### bun ```bash bunx prisma@latest orm init ``` #### pnpm ```bash pnpm dlx prisma@latest orm init ``` #### yarn ```bash yarn dlx prisma@latest orm init ``` #### npm ```bash npx prisma@latest orm init ``` - [Add to PostgreSQL](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql): Add Prisma ORM to an existing PostgreSQL app and infer a starter contract from the live schema. - [Add to MongoDB](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/mongodb): Add Prisma ORM to an existing MongoDB app and model the collections you want to query first. ## After setup [#after-setup] * Use the generated app scripts for the first run. * Open `prisma-8.md` or the installed Prisma ORM skills when you want agent-ready guidance inside the project. * Change the starter contract when you are ready to model your own data. * Open the [Prisma ORM overview](https://www.prisma.io/docs/orm) when you want the concepts behind the setup. ## Use with your agent [#use-with-your-agent] You can hand either setup path to your coding agent. Copy the prompt that matches your project and give it to your agent. Both prompts use PostgreSQL. To create a new app, use this prompt: ```text Create a new [framework] application with Prisma ORM, seed it, and run it locally. If I have not told you which framework, stop and ask before scaffolding. Valid --template values: minimal (the default), next, hono, nuxt, astro, nest, svelte, tanstack-start, elysia. Check the Node.js version first: `node --version` must print 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer. For the nuxt template, it must print 22.22.3 or newer on the 22 line, 24.15 or newer on the 24 line, or 26 or newer, because Nuxt requires it. The npm that ships with your Node.js release works; on Node.js 22 that is npm 10. 1. Scaffold the app: `npm create prisma@latest -- my-app --template [framework] --provider postgres --yes`. 2. Get a database connection string: use the one I give you, or create a Prisma Postgres database with `npx create-db@latest` and show me the claim URL it prints. Export it as `DATABASE_URL` in the shell; the generated scripts read the environment variable, not `.env`. 3. From the project directory, apply the starter contract: `npm run db:init`. Sample users are seeded automatically on the app's first query; there is no separate seed script. 4. Edit the starter contract under `src/prisma/` into a small schema for my use case, then run `npm run contract:emit` and plan and apply the migration: `npx prisma migration plan`, then `npx prisma db migrate --yes`. Migration planning diffs the emitted contract, so the emit step is required. 5. Update the seed script under `src/prisma/` and the app routes to query the new schema, start `npm run dev` in the background (with `DATABASE_URL` exported), and verify with a request against the running app. For the `nest` template, if routes return 500s with `reading 'findAll'` in the logs, add explicit `@Inject()` tokens as shown in https://www.prisma.io/docs/guides/frameworks/nestjs.md. Use the installed Prisma ORM skills and the current Prisma docs: https://www.prisma.io/docs/llms.txt (append `.md` to any docs URL for a markdown version). ``` To add Prisma ORM to an app you already have, use this prompt: ```text Add Prisma ORM to this existing project. This flow is for PostgreSQL. If the project uses MongoDB, follow https://www.prisma.io/docs/prisma-orm/add-to-existing-project/mongodb.md instead; for other databases, stop and tell me. 1. Run `npx prisma@latest orm init --yes --target postgres --authoring psl` (the flags are required when the CLI cannot prompt). It writes `prisma.config.ts`, a starter contract and `db.ts` under `src/prisma/`, and installs dependencies. Then run `npx prisma skills sync` to install the Prisma ORM skills. 2. Set `DATABASE_URL` in `.env` to my database. If I did not give you one, create a Prisma Postgres database with `npx create-db@latest`, put its connection string in `.env`, and show me the claim URL it prints so I can keep the database. 3. If the database already has tables, infer the contract from it: `npx prisma contract infer`, then `npx prisma contract emit`, then sign it with `npx prisma db sign`. If the database is empty, keep the starter contract and run `npx prisma db init`. 4. Write one query with the generated `db` client in an existing code path, run it, and show me the returned rows. Follow https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql.md and the installed Prisma ORM skills. ``` ## Related pages - [`Console`](https://www.prisma.io/docs/console): Learn how to use the Console to manage and integrate Prisma products into your application. - [`Introduction to Prisma ORM`](https://www.prisma.io/docs/prisma-orm): Prisma ORM 8 is a release candidate. - [`Local development`](https://www.prisma.io/docs/local-development): Run the whole Prisma stack on your machine, with your app on Bun, a local Prisma Postgres database, and local object storage. - [`Overview`](https://www.prisma.io/docs/cli): Prisma CLI reference - [`Overview`](https://www.prisma.io/docs/guides): Practical, step-by-step guides for building with Prisma ORM and deploying to Prisma Compute, each one run end to end before it ships. # Get started with Prisma (/docs) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Prisma is a complete TypeScript stack with one workflow: **build** your app and run all of it on your machine, then **deploy** it to the Prisma platform with one command. Check out the [full-stack tutorial](https://www.prisma.io/docs/full-stack-tutorial). ## Build Define the app in TypeScript and run everything locally before you ship. - **[Local development](https://www.prisma.io/docs/local-development)**: One command runs your whole app on your machine, with your services on Bun wired to local Postgres and local storage, and no cloud credentials. - **[Composer](https://www.prisma.io/docs/composer)** (Early Access): Declare your services and the resources they depend on: databases, jobs, buckets, secrets. - **[ORM](https://www.prisma.io/docs/orm)**: Model your data and query it with type safety, end to end. ## Deploy One deploy provisions everything on the Prisma platform and connects it. - **[Compute](https://www.prisma.io/docs/compute)**: Hosting for your services, next to your data. - **[Storage](https://www.prisma.io/docs/storage)**: S3-compatible object storage inside your project. - **[Postgres](https://www.prisma.io/docs/postgres)**: Managed PostgreSQL, provisioned during setup. - **[Deploy](https://www.prisma.io/docs/prisma-compute/deploy)**: Ship from your terminal with Composer, from the Console, or on every push from a connected repository with a deploy workflow. Every branch gets its own environment. One CLI serves the whole stack: `npx prisma@latest` drives the ORM and the Prisma platform, from migrations and local dev to deploys, databases, and buckets, for you and your coding agent. See the [CLI reference](https://www.prisma.io/docs/cli). ## Prisma ORM Prisma ORM 8 works without the rest of the Prisma platform, with your own PostgreSQL or MongoDB database. Pick the page that matches where you are starting from. Each page also links the MongoDB version of the same steps. - [I'm creating a new app](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql): Start from a template - [I have an app, but no database yet](https://www.prisma.io/docs/prisma-orm/quickstart/existing-app/postgresql): Add Prisma ORM, then create the tables - [I have a database already](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql): Start from the tables you have - [I have a Prisma 7 app](https://www.prisma.io/docs/guides/upgrade-prisma-orm/postgresql): Move to Prisma ORM 8 in steps ## Pick your framework Every guide runs the same journey with the same commands: scaffold, connect Prisma Postgres, run a real query, and deploy. SvelteKit and Deno don't deploy to Compute yet; their guides stop at a verified local run. - [Next.js](https://www.prisma.io/docs/guides/frameworks/nextjs) - [Hono](https://www.prisma.io/docs/guides/frameworks/hono) - [TanStack Start](https://www.prisma.io/docs/guides/frameworks/tanstack-start) - [NestJS](https://www.prisma.io/docs/guides/frameworks/nestjs) - [Astro](https://www.prisma.io/docs/guides/frameworks/astro) - [Nuxt](https://www.prisma.io/docs/guides/frameworks/nuxt) - [SvelteKit](https://www.prisma.io/docs/guides/frameworks/sveltekit) - [Bun](https://www.prisma.io/docs/guides/runtimes/bun) - [Elysia](https://www.prisma.io/docs/guides/frameworks/elysia) - [Deno](https://www.prisma.io/docs/guides/runtimes/deno) If you're using Express or another Node.js server, pick your starting point in the Prisma ORM section above. ## Prisma ORM 7 Prisma ORM 7 remains fully supported. Scaffold it with `npx create-prisma@stable`, or add it to an existing project with `npx prisma@prev init`. It pairs with Prisma Postgres and Prisma Compute the same way. When you're ready, Prisma ORM 8 is the upgrade path. - [Prisma ORM 7 setup paths](https://www.prisma.io/docs/v7/getting-started): All database quickstarts, plus the agent prompt - [Prisma ORM 7 quickstart](https://www.prisma.io/docs/v7/prisma-orm/quickstart/prisma-postgres): With Prisma Postgres, 5 min ## Other setups If you already have an app or a database, or need a single Prisma product on its own, each path below has a guide to follow and a prompt to hand to your agent. **Existing project, your own database, or a single product on its own** ### Add Prisma to an existing project Follow the guide: [PostgreSQL guide](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql) ```text Add Prisma ORM to this existing project. This flow is for PostgreSQL. If the project uses MongoDB, follow https://www.prisma.io/docs/prisma-orm/add-to-existing-project/mongodb.md instead; for other databases, stop and tell me. 1. Run `npx prisma@latest orm init --yes --target postgres --authoring psl` (the flags are required when the CLI cannot prompt). It writes `prisma.config.ts`, a starter contract and `db.ts` under `src/prisma/`, and installs dependencies. Then run `npx prisma skills sync` to install the Prisma ORM skills. 2. Set `DATABASE_URL` in `.env` to my database. If I did not give you one, create a Prisma Postgres database with `npx create-db@latest`, put its connection string in `.env`, and show me the claim URL it prints so I can keep the database. 3. If the database already has tables, infer the contract from it: `npx prisma contract infer`, then `npx prisma contract emit`, then sign it with `npx prisma db sign`. If the database is empty, keep the starter contract and run `npx prisma db init`. 4. Write one query with the generated `db` client in an existing code path, run it, and show me the returned rows. Follow https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql.md and the installed Prisma ORM skills. ``` ### Prisma ORM with your own PostgreSQL database Follow the guide: [Quickstart](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql) ```text Create a new [framework] application with Prisma ORM against my existing PostgreSQL database. If I have not given you a connection string, stop and ask; do not invent one. Valid --template values: minimal (the default), next, hono, nuxt, astro, nest, svelte, tanstack-start, elysia. Check the Node.js version first: `node --version` must print 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer. For the nuxt template, it must print 22.22.3 or newer on the 22 line, 24.15 or newer on the 24 line, or 26 or newer, because Nuxt requires it. The npm that ships with your Node.js release works; on Node.js 22 that is npm 10. 1. Scaffold: `npm create prisma@latest -- my-app --template [framework] --provider postgres --yes`. 2. Export my connection string as `DATABASE_URL` in the shell; the generated scripts read the environment variable, not `.env`. From the project directory: `npm run db:init`, then start `npm run dev` in the background and verify the sample query returns data. Sample users are seeded automatically on the app's first query; there is no separate seed script. 3. Evolve the starter contract under `src/prisma/` into my schema, then run `npm run contract:emit`, `npx prisma migration plan`, and `npx prisma db migrate --yes`. Do not provision any hosted database. Use the installed Prisma ORM skills and https://www.prisma.io/docs/llms.txt for current docs. ``` ### Prisma ORM only (Prisma ORM 7) Follow the guide: [Quickstart](https://www.prisma.io/docs/v7/prisma-postgres/quickstart/prisma-orm) ```text Add Prisma ORM 7 to this project with my existing database. If I have not given you a database connection string and none exists in the project, stop and ask. 1. Run `npx prisma@prev init` (Prisma ORM 7). For an existing database, set DATABASE_URL in `.env` and introspect it with `npx prisma@prev db pull`; for a new schema, define models in `prisma/schema.prisma` and run `npx prisma@prev migrate dev --name init`. If migrate dev asks to reset the database, stop and ask me first. 2. Install the driver adapter for the database (Prisma ORM 7 requires one), e.g. `npm install @prisma/adapter-pg` for PostgreSQL, and pass it to `new PrismaClient({ adapter })`. Generate the client with `npx prisma@prev generate` and write one query in an existing code path. 3. Run the query (e.g. with `npx tsx`) and show me the output, the schema, and the query you added. Current docs: https://www.prisma.io/docs/orm/v7.md and https://www.prisma.io/docs/llms.txt. ``` ### Prisma Postgres only Follow the guide: [create-db guide](https://www.prisma.io/docs/postgres/npx-create-db) ```text Create a Prisma Postgres database for this project. 1. Run `npx create-db@latest`. It creates a temporary Prisma Postgres database without an account and prints a connection string plus a claim URL. 2. Put the connection string in `.env` as DATABASE_URL and wire it into whichever of Prisma ORM, Kysely, Drizzle, TypeORM, or node-postgres the project already uses (detect it from package.json; if none, ask me). Verify the connection with one trivial query such as `select 1`. 3. Remind me to open the claim URL to keep the database in my Prisma workspace. Current docs: https://www.prisma.io/docs/postgres.md. ``` ### Prisma Compute only Follow the guide: [Deploy guide](https://www.prisma.io/docs/prisma-compute/deploy) ```text Deploy this app to Prisma Compute using `npx prisma@latest`. Compute runs apps declared with Prisma Composer: your server code plus a small TypeScript declaration. Port the app following https://www.prisma.io/docs/composer/porting-an-app.md. SvelteKit and Deno are not supported yet; if the app is one of those, stop and tell me. 1. Install `@prisma/composer` and `@prisma/composer-prisma-cloud`, and install `prisma` as a dev dependency. Do not pin `effect`: Composer pins it. Only if another dependency pins a different `effect` and `prisma.config.ts` fails with `CLI.CONFIG_UNREADABLE`, add the override from https://www.prisma.io/docs/composer/deploying#when-prismaconfigts-fails-on-an-effect-version-conflict. Then install the Composer skill with `npx prisma skills sync`, and use it. 2. Declare the app: a `service.ts` with `compute({ name, deps, build })`, a `module.ts` that provisions it, and a `prisma.config.ts` whose `composer` section lists the extensions and the state store (`definePrismaConfig` from `prisma/config`, `defineConfig as composer` from `@prisma/composer/config`). Read the port from `service.port()` and bind 0.0.0.0. Replace other `process.env` reads with the service input schema, binding credentials like DATABASE_URL with `envSecret`. Composer does not build: produce one self-contained entry file per service (for example `esbuild --bundle --platform=node --format=esm --outfile=dist/server.mjs`), or use the framework build adapter (Next.js needs the `nextjs` adapter and `output: "standalone"`). 3. Verify locally: run the build, then `npx prisma dev module.ts`, and curl the printed local URL. 4. Confirm I am signed in with `npx prisma auth whoami`; if not, stop and ask me to run `npx prisma auth login` (it opens a browser). 5. Deploy: run the build, then `npx prisma deploy module.ts` with the app's env values (like DATABASE_URL) exported in the shell; the first deploy copies input values up to the deployment. Verify the printed public URL with curl. Current docs: https://www.prisma.io/docs/composer/porting-an-app.md and https://www.prisma.io/docs/prisma-compute/deploy.md. ``` If you're using MongoDB, follow the [MongoDB quickstart](https://www.prisma.io/docs/prisma-orm/quickstart/mongodb) or [add Prisma ORM to an existing MongoDB app](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/mongodb). If you work with [Kysely](https://www.prisma.io/docs/prisma-postgres/quickstart/kysely), [Drizzle](https://www.prisma.io/docs/prisma-postgres/quickstart/drizzle-orm), or [TypeORM](https://www.prisma.io/docs/prisma-postgres/quickstart/typeorm), follow the Prisma Postgres quickstart for that tool. ## Browse the docs This page hides the full navigation to keep the first run focused. These links open the full docs for each product. - [Prisma ORM](https://www.prisma.io/docs/orm): Prisma ORM 8, with Prisma ORM 7 docs - [Prisma Postgres](https://www.prisma.io/docs/postgres): The managed database - [Prisma Compute](https://www.prisma.io/docs/compute): Hosting and branching - [CLI reference](https://www.prisma.io/docs/cli): Every command and flag - [Guides](https://www.prisma.io/docs/guides): Frameworks and workflows - [AI tools](https://www.prisma.io/docs/ai): Skills, MCP, and prompts # Prisma with AI coding tools (/docs/ai) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Use Prisma with AI coding tools like Cursor, Codex, and ChatGPT Location: Prisma with AI coding tools Prisma ORM and Prisma Postgres work with AI coding agents through installable skills, an MCP server, and editor integrations. This page collects the setup guides, prompts, and tutorials for using them together. ## Get started [#get-started] Run the following command to bootstrap your database with a prompt: #### bun ```bash bunx --bun prisma@prev init --prompt "Create a habit tracker application" ``` #### pnpm ```bash pnpm dlx prisma@prev init --prompt "Create a habit tracker application" ``` #### yarn ```bash yarn dlx prisma@prev init --prompt "Create a habit tracker application" ``` #### npm ```bash npx prisma@prev init --prompt "Create a habit tracker application" ``` ## AI Coding Tools [#ai-coding-tools] Each guide covers setup and prompts for using Prisma in a specific AI editor or agent. * [Cursor](https://www.prisma.io/docs/ai/tools/cursor) - Define project-specific rules and use your schema as context to generate accurate queries and code. * [Codex](https://www.prisma.io/docs/ai/tools/codex) - Install the Prisma Codex plugin with Prisma ORM skills and the remote Prisma MCP server. * [Windsurf](https://www.prisma.io/docs/ai/tools/windsurf) - Automate your database workflows by generating schemas, queries, and seed data in this AI-powered editor. * [Github Copilot](https://www.prisma.io/docs/ai/tools/github-copilot) - Get Prisma-aware code suggestions, run CLI commands from chat, and query the Prisma docs. * [ChatGPT](https://www.prisma.io/docs/ai/tools/chatgpt) - Learn how to connect the Prisma MCP server to ChatGPT to manage your databases with natural language. ## Agent Skills [#agent-skills] AI agents often generate outdated Prisma v6 code. Install Prisma Skills to give your agent accurate, up-to-date v7 knowledge: CLI commands, Client API, upgrade guides, database setup, and Prisma Postgres workflows. #### bun ```bash bunx skills add prisma/skills ``` #### pnpm ```bash pnpm dlx skills add prisma/skills ``` #### yarn ```bash yarn dlx skills add prisma/skills ``` #### npm ```bash npx skills add prisma/skills ``` * [Available skills and setup](https://www.prisma.io/docs/ai/tools/skills) - See all available skills and learn how to install them. ## MCP server [#mcp-server] With Prisma's MCP server, your AI tool can create Prisma Postgres databases, run SQL queries, restore automated backups, and manage Object Storage. It can also manage Prisma Compute deployments, read app logs, set environment variables, and inspect how your [Composer](https://www.prisma.io/docs/composer) services connect. ```json title="Integrate in AI tool" { "mcpServers": { "Prisma": { "url": "https://mcp.prisma.io/mcp" } } } ``` * [Available tools](https://www.prisma.io/docs/ai/mcp-tools) - The full list of tools the Prisma MCP server exposes. * [Integrating in AI tools](https://www.prisma.io/docs/ai/tools/mcp-server#integrating-in-ai-tools) - Configuration for Cursor, Claude, Warp, and other AI tools. * [How we built it](https://www.prisma.io/blog/about-mcp-servers-and-how-we-built-one-for-prisma) - How the MCP protocol works and how the Prisma MCP server was built. ## Vibe Coding Tutorials [#vibe-coding-tutorials] Tutorials that build a full application from scratch with an AI coding assistant. * [Build a Linktree Clone SaaS](https://www.prisma.io/docs/ai/tutorials/linktree-clone) - Build a Linktree clone SaaS with Next.js, Prisma Postgres, and Clerk auth using AI assistance. ## Resources [#resources] * [Vibe Coding with Limits](https://www.prisma.io/blog/vibe-coding-with-limits-how-to-build-apps-in-the-age-of-ai) - How to Build Apps in the Age of AI * [Vibe Coding an E-commerce App](https://www.prisma.io/blog/vibe-coding-with-prisma-mcp-and-nextjs) - with Prisma MCP and Next.js * [Integrating the Vercel AI SDK](https://www.prisma.io/docs/guides/integrations/ai-sdk) - in a Next.js application ## Integrations [#integrations] * [Automate with Pipedream](https://pipedream.com/apps/prisma-management-api) - Connect Prisma Postgres to 2,800+ apps in automated workflows * [Firebase Studio](https://www.prisma.io/docs/guides/postgres/idx) - Prompt your application with Firebase Studio & Prisma Postgres ## Related pages - [`Choose a Prisma ORM setup path`](https://www.prisma.io/docs/getting-started): Choose the fastest path to try Prisma ORM in a new or existing project. - [`Console`](https://www.prisma.io/docs/console): Learn how to use the Console to manage and integrate Prisma products into your application. - [`Introduction to Prisma ORM`](https://www.prisma.io/docs/prisma-orm): Prisma ORM 8 is a release candidate. - [`Local development`](https://www.prisma.io/docs/local-development): Run the whole Prisma stack on your machine, with your app on Bun, a local Prisma Postgres database, and local object storage. - [`Overview`](https://www.prisma.io/docs/cli): Prisma CLI reference # MCP tools (/docs/ai/mcp-tools) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Available Prisma MCP tools for databases, Compute deployments, Object Storage, and Composer projects. Location: AI > MCP tools These tools are available through the [Prisma MCP server](https://www.prisma.io/docs/ai/tools/mcp-server) in your selected workspace. Your AI client lists each tool's input parameters when it connects. > [!WARNING] > Tools that change or delete resources can cause data loss or interrupt live traffic. Check the target workspace and resources before approving an operation in your AI tool. ## Workspace and databases [#workspace-and-databases] * `fetch_workspace_details`: Fetch the details of a Prisma Postgres workspace. * `list_prisma_postgres_databases`: Fetch a list of available Prisma Postgres databases for the user's workspace. * `create_prisma_postgres_database`: Create a managed Prisma Postgres database and a new project. * `delete_prisma_postgres_database`: Permanently delete a Prisma Postgres database by ID. This tool cannot delete a project's default database. ## Connection strings [#connection-strings] * `list_prisma_postgres_connection_strings`: Fetch a list of available connection strings for the given database id. * `create_prisma_postgres_connection_string`: Create a new connection string for a Prisma Postgres database with the given id. * `delete_prisma_postgres_connection_string`: Delete a connection string with the given connection string id. ## Backups and recovery [#backups-and-recovery] * `list_prisma_postgres_backups`: List available automated backups for a Prisma Postgres database. * `create_prisma_postgres_recovery`: Restore a Prisma Postgres database to a new database with the given backup id. ## Schema and queries [#schema-and-queries] * `introspect_database_schema`: Read the schema of a Prisma Postgres database to show its structure. * `execute_sql_query`: Run SQL to read, modify, or delete data in a Prisma Postgres database, without changing its schema. * `execute_prisma_postgres_schema_update`: Execute a schema update on a Prisma Postgres database with the given id. ## Object Storage [#object-storage] * `list_object_store_buckets`: List object-store buckets in the workspace, optionally filtered by project id. * `create_object_store_bucket`: Create a new object-store bucket in the given project. * `delete_object_store_bucket`: Permanently delete an object-store bucket, all objects stored in it, and all its access keys. * `create_object_store_bucket_key`: Create an S3-compatible access key for an object-store bucket. * `delete_object_store_bucket_key`: Delete an object-store bucket access key. The key stops working immediately. ## Documentation [#documentation] * `search_prisma_documentation`: Answer a natural-language question about Prisma using the official Prisma documentation, returning a cited answer with links back to the docs. ## Compute apps and builds [#compute-apps-and-builds] * `list_prisma_compute_apps`: List Prisma Compute apps in the selected workspace, optionally filtered by project. * `list_prisma_compute_builds`: List builds, newest first, with optional project, branch, and state filters. * `list_prisma_compute_deployments`: List deployments for a Prisma Compute app, newest first. * `get_prisma_compute_deployment_logs`: Read runtime logs for a deployment, one page at a time. ## Compute deployment management [#compute-deployment-management] * `promote_prisma_compute_deployment`: Make a running deployment the live version of an app. * `rollback_prisma_compute_app`: Make an existing deployment live, starting it first if it is stopped. * `start_prisma_compute_deployment`: Start an existing deployment from an already uploaded build. This tool does not build or upload the app. * `stop_prisma_compute_deployment`: Stop a deployment, disconnecting it from the app's public URL if it is the live version. * `delete_prisma_compute_deployment`: Permanently delete a deployment, stopping it first if needed. * `delete_prisma_compute_app`: Permanently delete an app. To stop and delete its deployments too, set `deleteDeployments` to true; otherwise, active deployments block deletion. ## Compute environment variables [#compute-environment-variables] * `set_prisma_compute_env_var`: Set an environment variable for a project's production or preview environment, or override it for one preview branch. Values are encrypted and never returned. * `delete_prisma_compute_env_var`: Permanently delete an environment variable by ID. ## Composer projects [#composer-projects] [Composer](https://www.prisma.io/docs/composer) defines your application's services, databases, and other resources in TypeScript. * `get_prisma_composer_topology`: Show the saved connections between a project's Composer services and resources, with Console links and stored app URLs. The result may be out of date and does not check whether apps are running. The Compute tools manage existing apps and deployments; they do not build or upload your source code. To deploy a new app, follow the [Prisma Compute guide](https://www.prisma.io/docs/compute). # Apps and Modules (/docs/composer/apps-and-modules) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. Location: Composer > Apps and Modules A **Prisma App** is a tree of Modules composed in TypeScript. At the leaves are **services**, the units that run your code, and **resources**, stateful things like a Postgres database. A parent Module wires them together; your code never participates in the wiring, it only receives the results. For example, a store app might be one root module containing a `catalog` Module (an API service plus its own database), an `orders` Module, and a `storefront` Next.js service that calls both. Each piece declares what it needs, and the root wires the pieces together. This matters because the wiring is data the compiler can check. A dependency wired to the wrong producer, or a missing one, fails `tsc` and fails the deploy, instead of failing at runtime in production. ## The service declaration [#the-service-declaration] A service declares everything about itself in one `compute()` call. It is pure data, with no behavior: ```ts compute({ name: 'auth', // the service's name in the app graph deps: { db: rawPostgres() }, // what it needs -> read via service.load() input: authInput, // its incoming config -> read via service.input() build: node({ module: import.meta.url, entry: '../dist/server.mjs' }), expose: { rpc: authContract }, // what it offers to other services }); ``` Dependencies and input are two deliberately separate channels with two separate accessors. A dependency is a live connection to another node; input is data, plain values and credentials together, declared as one schema (see [Service input](https://www.prisma.io/docs/composer/service-input)). Your code contains no configuration reads and no URLs, which is what makes every environment (production, a stage, a test) a different set of injected values and nothing more. ## The root module [#the-root-module] The root module is the app. The Composer CLI loads its default export: ```ts title="module.ts" import { module } from '@prisma/composer'; import authModule from './src/auth/module.ts'; import storefrontService from './src/storefront/service.ts'; export default module('my-app', ({ provision }) => { const auth = provision(authModule); provision(storefrontService, { deps: { auth: auth.rpc } }); }); ``` `provision(node, opts?)` places a node in the graph and returns a ref carrying one port per exposed contract. Its options: * `id`: the node's name in the graph, defaulting to its own `name`. Set it explicitly when the default stutters; a service named `auth` inside a module named `auth` would read as `auth.auth`. * `deps`: a value for each declared dependency slot, either another provisioned ref or an exposed port. * `input`: the service's input binding, required exactly when the service declares an input schema. See [Service input](https://www.prisma.io/docs/composer/service-input). * `secrets`: for a Module boundary, a binding for each forwarded secret need. Two naming rules the platform enforces: provision names must be at least three characters (call a database `'database'`, not `'db'`; the wiring key on the service side can still be `db`), and ids must be unique within their module. ## Reusable Modules [#reusable-modules] When a service and its database belong together, package them as a **Module**: a unit that owns its internals and offers only typed ports. A consumer provisions the Module and wires its exposed contract; it never sees, and can never reach, the database inside. A Module declares its boundary (what it needs, what it offers) in the second argument, wires its internals in the builder function, and returns the ports it promised: ```ts title="src/auth/module.ts" import { module, secret } from '@prisma/composer'; import { rawPostgres } from '@prisma/composer-prisma-cloud'; import { authContract } from './contract.ts'; import authService from './service.ts'; export default module( 'auth', { secrets: { signingKey: secret() }, expose: { rpc: authContract } }, ({ secrets, provision }) => { const db = provision(rawPostgres({ name: 'database' })); const service = provision(authService, { id: 'service', deps: { db }, input: { signingKey: secrets.signingKey }, }); return { rpc: service.rpc }; }, ); ``` The boundary declares `secrets: { signingKey: secret() }`: a nameless need the Module forwards without ever learning the platform variable's name. The root binds it: ```ts title="module.ts" const auth = provision(authModule, { secrets: { signingKey: envSecret('AUTH_SIGNING_SECRET') }, }); provision(storefrontService, { deps: { auth: auth.rpc } }); ``` A Module can also declare boundary `deps`: inputs its parent must supply, wired exactly like a service's. The root module you already have is the outermost Module, with no boundary at all. ## When to reach for a Module [#when-to-reach-for-a-module] * **Compose before you write.** Ready-made Modules for scheduled jobs, blob storage, and event streams ship with the framework; wiring one in is a couple of lines. See [Building blocks](https://www.prisma.io/docs/composer/building-blocks). * **Package what belongs together.** A service that owns a database, or a pair of services that only make sense as a unit, is a Module. * **Keep the app graph readable.** The deploy prints the module tree with dotted addresses (`auth.service`), so the structure you author is the structure you operate. ## Next steps [#next-steps] * [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts): what happens on the wire between services. * [Service input](https://www.prisma.io/docs/composer/service-input): configuration and secrets as one schema. * [Building blocks](https://www.prisma.io/docs/composer/building-blocks): the Modules that ship with the framework. ## Related pages - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. - [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command. # Building blocks (/docs/composer/building-blocks) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. Location: Composer > Building blocks Building blocks are Modules you compose instead of implementing a capability yourself. Each owns its internals and hands you a typed port, so adding one is a couple of lines. The first-party set ships inside `@prisma/composer-prisma-cloud`. It is small, and growing: | Import | What you get | Exposes | | ------------------------------------------------------ | --------------------------------------------------------------- | --------- | | `cron` from `@prisma/composer-prisma-cloud/cron` | A scheduler that fires your jobs at your service on an interval | nothing | | `storage` from `@prisma/composer-prisma-cloud/storage` | An S3-backed blob store, credentials included | `store` | | `streams` from `@prisma/composer-prisma-cloud/streams` | Durable append-only event streams, backed by a `store` | `streams` | Before implementing a capability by hand, check whether a block already owns it. Wiring one in takes a few lines, while an integration you write yourself is code you maintain. ## Cron [#cron] Cron is the block most apps want first. You supply two things, a schedule and a runner service that exposes the `trigger` contract, and the Module does the rest. The schedule is the single source of truth for job ids and intervals: ```ts title="src/promotions/service.ts" import { defineSchedule, triggerContract } from '@prisma/composer-prisma-cloud/cron'; export const schedule = defineSchedule({ rotateSpecial: '30s' }); export default compute({ name: 'promotions', deps: { catalog: rpc(catalogContract) }, build: node({ module: import.meta.url, entry: '../dist/server.mjs' }), expose: { trigger: triggerContract }, }); ``` In the server, map each job id to work. `serveSchedule` checks the map against the schedule, so an unhandled job does not compile: ```ts title="src/promotions/server.ts" import { serveSchedule } from '@prisma/composer-prisma-cloud/cron'; const handler = serveSchedule(service, schedule, { rotateSpecial: (deps) => deps.catalog.rotateSpecial({}), }); ``` In the module, the cron Module's boundary deps mirror the runner's own, so the root wires it like any other edge: ```ts title="module.ts" provision(cron({ schedule, runner: promotionsService }), { deps: { catalog: catalog.rpc }, }); ``` A runner that declares an `input` schema (see [Service input](https://www.prisma.io/docs/composer/service-input)) takes its binding on `cron()` itself, with `envSecret(...)` where the schema expects a secret. It is required exactly when the runner declares a schema, the same rule `provision()` applies: ```ts title="module.ts" provision( cron({ schedule, runner: ingestService, input: { token: envSecret('INGEST_TOKEN') }, }), { deps: { catalog: catalog.rpc } }, ); ``` The scheduler is the one service in your app that never sleeps. Compute scales an idle service to zero, and the scheduler receives no requests of its own, so it holds the platform's keep-awake guard for its whole lifetime. One warm instance per app is the cost of the clock; the runner sleeps like any other service and wakes when the scheduler calls it. ## Storage and streams [#storage-and-streams] The `storage` Module provisions an S3-backed blob store, with its own Postgres and minted credentials, and exposes a `store` port. The `streams` Module builds durable append-only event streams on top of a `store`. For working versions, including the streams Module's secret binding, see [`examples/storage`](https://github.com/prisma/composer/tree/main/examples/storage) and [`examples/streams`](https://github.com/prisma/composer/tree/main/examples/streams) in the Composer repository. If you want the raw S3 surface without the Module, provision a [`bucket` resource](https://www.prisma.io/docs/composer/object-storage) instead; the two share a contract kind, so a service wired to `s3()` can be rewired to a `bucket` without changing its declaration. ## Where new blocks come from [#where-new-blocks-come-from] An **extension** is a package that brings its own Modules, resources, or deploy target, using the same mechanism `@prisma/composer-prisma-cloud` itself uses. The convention is an npm package named `prisma-composer-*`, which is how you and your agent find one. An extension is installed like any dependency and enters the deploy through the `extensions` array of the `composer` section in `prisma.config.ts`. The ecosystem is new: today the three Modules above plus the ones you write are the whole set. Treat `prisma-composer-*` as the place to look, and check that a package actually exists on npm before depending on it. ## Next steps [#next-steps] * [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules): write a reusable Module of your own. * [Object storage](https://www.prisma.io/docs/composer/object-storage): the raw bucket resource. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. - [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command. # Core concepts (/docs/composer/core-concepts) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. Location: Composer > Core concepts Prisma Composer is a TypeScript framework for applications made of several services. You touch two things: the `@prisma/composer` library, which you use to declare your services and what they depend on, and the `dev` and `deploy` commands in the Prisma CLI, which run and provision what you declared on [Prisma Compute](https://www.prisma.io/docs/compute) and [Prisma Postgres](https://www.prisma.io/docs/postgres). The declarations use a small vocabulary that repeats everywhere: in the authoring API, in deploy output, and in error messages. The sections below define each term in plain language, and each links to the page that goes deeper. ## Declarations are data [#declarations-are-data] Everything you author in Composer is a declaration: a plain TypeScript value that describes a piece of your application without running it. A service declaration says what the service is, what it depends on, and how it was built: ```ts title="src/storefront/service.ts" import nextjs from '@prisma/composer/nextjs'; import { rpc } from '@prisma/composer/service-rpc'; import { compute } from '@prisma/composer-prisma-cloud'; import { catalogContract } from '../catalog/contract.ts'; export default compute({ name: 'storefront', deps: { catalog: rpc(catalogContract) }, build: nextjs({ module: import.meta.url, appDir: '..' }), }); ``` The `deps` line says the storefront calls the catalog service's API, named by a [contract](#contracts) defined further down this page. Importing this file starts no service and provisions nothing: `compute()` returns a plain description. The declaration is data that three different consumers read: the TypeScript compiler checks the wiring, the deploy provisions what it describes, and the runtime injects the declared dependencies into your code. That is why a mistake, such as a dependency wired to a producer with a different contract, fails `tsc` in seconds instead of failing a deploy. Two rules follow from this design and shape everything else: 1. **Your code never reads its environment.** No `process.env`, no hardcoded URLs. Every dependency and config value arrives typed, through injection. 2. **Composer never bundles or transforms your code.** You build with your own tooling; the deploy assembles what you built. ## The three node kinds [#the-three-node-kinds] A Composer application is a tree of nodes. There are three kinds, and each answers a different question: | Kind | What it is | Declared with | | ------------ | ------------------------------------------------------------------------ | --------------------- | | **Service** | A unit that runs your code: an API, a web app, a worker | `compute()` | | **Resource** | A stateful dependency the platform manages for you: a database, a bucket | `postgres()`, storage | | **Module** | A boundary that groups services and resources and exposes typed ports | `module()` | A service is atomic: Composer sees its ports and nothing inside. A Module is the opposite: it runs no code of its own; its behaviour is the composition of what it wraps. A Module owns its internals, so a consumer can use its exposed port but can never reach the database inside it. The **App** is not a fourth kind. It is the outermost Module, the one whose file you hand to the CLI. It wires the top-level pieces together, and deploying it deploys everything it contains. On the platform, the App becomes one Prisma [project](https://www.prisma.io/docs/compute#the-model): services land on [Prisma Compute](https://www.prisma.io/docs/compute) and databases on [Prisma Postgres](https://www.prisma.io/docs/postgres). What is deliberately not a node: plain values. An API key, a region name, or a feature flag has no lifecycle to manage, so it is **configuration**, and it travels through a different channel ([below](#two-channels-dependencies-and-input)). The test for whether something is a node is whether the deploy can create, update, and destroy it. See [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules) for the full authoring surface. ## Ports and wiring [#ports-and-wiring] Every node has typed connection points, called ports. A `deps` entry is a port where the node requires something; an `expose` entry is a port where it offers something. Wiring means connecting a required port to an offered one that satisfies it, and it happens in exactly one place: a Module's builder function, using `provision()`: ```ts title="module.ts" import { module } from '@prisma/composer'; import catalogModule from './src/catalog/module.ts'; import ordersModule from './src/orders/module.ts'; import storefrontService from './src/storefront/service.ts'; export default module('store', ({ provision }) => { const catalog = provision(catalogModule); const orders = provision(ordersModule, { deps: { catalog: catalog.rpc } }); provision(storefrontService, { deps: { catalog: catalog.rpc, orders: orders.rpc } }); }); ``` `provision()` places a node in the graph and returns a ref carrying one port per exposed contract. Because ports are typed, the compiler verifies every wire: each declared dependency must be bound, and bound to a producer whose contract matches. The wired graph as a whole is the application's **topology**, and the deploy prints it as a tree with dotted addresses (`auth.service`), so the structure you author is the structure you operate. ## Two channels: dependencies and input [#two-channels-dependencies-and-input] A service receives things from outside through two deliberately separate channels, with two separate accessors in your code. **Dependencies** are live connections to other nodes: another service's API, a database. They are declared in `deps` and arrive in code through `service.load()`, already typed and already authenticated: ```ts title="src/storefront/data.ts" import service from './service.ts'; const { catalog } = service.load(); const { products } = await catalog.listProducts({}); ``` **Input** is configuration: plain values and credentials, declared together as one [Standard Schema](https://standardschema.dev) and read through `service.input()`. The service declares what shapes are legal; the place that provisions it decides where each value comes from. There are three bindings: 1. A **literal**: the value is written at the provision site and travels with the deployment. 2. **`envParam(NAME)`**: the value is read from a named platform variable per environment, so the same app reads different values in production and in a staging copy. 3. **`envSecret(NAME)`**: like `envParam`, but the value is a credential: redacted in output and never stored in deploy state. This is the only thing Composer calls a secret. The split is what makes environments cheap: production, a staging stage, a local run, and a test all execute the same code with different injected values. See [Service input](https://www.prisma.io/docs/composer/service-input). ## Contracts [#contracts] A **contract** is the typed interface through which services communicate. You write it once as a set of method signatures with Standard Schema message types (arktype, zod, and valibot all work), then both sides use it: the providing service serves it, and a consuming service declares `rpc(contract)` as a dependency and gets a typed client from `service.load()`. On the wire, calls are RPC over HTTP, authenticated with per-consumer service keys Composer provisions for you, and retried safely. See [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts). Databases have a contract story of their own: a `postgres()` resource is typed by a [Prisma ORM data contract](https://www.prisma.io/docs/orm/contract-authoring/the-data-contract), and its migrations run as part of the deploy. Every database belongs to exactly one Module, which is what makes migration ownership unambiguous: the Module that owns the database owns its schema. See [Databases](https://www.prisma.io/docs/composer/databases). ## The deploy model [#the-deploy-model] A deploy is not a script of steps you wrote; it is a comparison. `deploy` loads your root module, compares the topology it describes with the **deploy state** recorded for that environment, and applies only the difference: create what is new, update what changed, leave the rest alone. Re-running a deploy with nothing changed is a no-op, and re-running after a failure converges what the deploy state already tracks rather than starting over. One edge sits outside that guarantee: if an environment's very first deploy fails before its state is written, resources created up to that point are untracked, and neither a retry nor a destroy will manage them. #### bun ```bash bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm prisma deploy module.ts ``` #### yarn ```bash yarn prisma deploy module.ts ``` #### npm ```bash npx prisma deploy module.ts ``` Two consequences of convergence are worth naming. Removing a node from your module removes the deployed thing on the next deploy, because the topology is the source of truth. And tearing a whole environment down works through the same mechanism, via the `destroy` operation in `@prisma/composer/control`. See [Deploying](https://www.prisma.io/docs/composer/deploying) and the [`deploy` reference](https://www.prisma.io/docs/cli/deploy). ## Stages [#stages] A **stage** is an environment name chosen at deploy time, never written in the topology. The same graph deploys anywhere: #### bun ```bash bunx prisma deploy module.ts # production bunx prisma deploy module.ts --stage staging # a persistent staging environment bunx prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### pnpm ```bash pnpm prisma deploy module.ts # production pnpm prisma deploy module.ts --stage staging # a persistent staging environment pnpm prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### yarn ```bash yarn prisma deploy module.ts # production yarn prisma deploy module.ts --stage staging # a persistent staging environment yarn prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### npm ```bash npx prisma deploy module.ts # production npx prisma deploy module.ts --stage staging # a persistent staging environment npx prisma deploy module.ts --stage pr-42 # one environment per PR ``` Deploying with no `--stage` targets production, which lives at the project level. A named stage is a complete, isolated copy of the app, deployed as a [preview branch](https://www.prisma.io/docs/compute/branching) of the same project: its own services, its own databases, its own configuration. The only thing a stage shares with production is the code, which is exactly what the two-channel design promises: same topology, different injected values. ## Local development [#local-development] `dev` brings the whole app up on your machine by running the same deploy pipeline against local emulators standing in for Prisma Compute and Prisma Postgres. No credentials, no network. Your services, databases, and wiring are real; only the providers underneath are swapped, so behaviour like service-key authentication works locally exactly as it does deployed: #### bun ```bash bunx prisma dev module.ts ``` #### pnpm ```bash pnpm prisma dev module.ts ``` #### yarn ```bash yarn prisma dev module.ts ``` #### npm ```bash npx prisma dev module.ts ``` See [Local development](https://www.prisma.io/docs/composer/local-development) for what persists between runs and the [`dev` reference](https://www.prisma.io/docs/cli/dev). ## Building blocks and extensions [#building-blocks-and-extensions] A Module is also the unit of reuse. A **building block** is a ready-made Module that ships with the framework and composes in a couple of lines: `cron` for scheduled jobs, `storage` for S3-backed blobs, and `streams` for durable event streams. Because a block is an ordinary Module, wiring one in is the same `provision()` call you use for your own. An **extension** is a package that brings its own Modules, resources, or deploy target, published under the `prisma-composer-*` naming convention. The first-party blocks above are the whole catalogue today. See [Building blocks](https://www.prisma.io/docs/composer/building-blocks) and [Object storage](https://www.prisma.io/docs/composer/object-storage). ## Testing [#testing] Because your code receives everything through `service.load()` and `service.input()`, a test is another environment: one where you decide what those calls return. `mockService` substitutes a fake for a unit test; `bootstrapService` hydrates a service with real local dependencies for an integration test. Nothing is provisioned and no code changes. See [Testing](https://www.prisma.io/docs/composer/testing). ## Next steps [#next-steps] * [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules): the authoring surface behind the node kinds and `provision()`. * [Getting started](https://www.prisma.io/docs/composer/getting-started): build and run a two-service app from an empty directory. * [Limitations](https://www.prisma.io/docs/composer/limitations): what Composer does not do yet, before you commit to a design. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. - [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command. # Databases (/docs/composer/databases) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. Location: Composer > Databases There are two ways for a service to get a [Prisma Postgres](https://www.prisma.io/docs/postgres) database, depending on how much you want the framework to do. Both are dependencies: the service declares the need, the module provisions the resource, and your code receives the result from `service.load()`. > [!NOTE] > Two kinds of contract > > Composer's [service contracts](https://www.prisma.io/docs/composer/services-and-contracts) describe an RPC API between services. A Prisma ORM **data contract** describes a database schema. This page uses both: `postgres()` ties a database dependency to a Prisma ORM data contract. ## `rawPostgres()`: bring your own client [#rawpostgres-bring-your-own-client] Declare `rawPostgres()` when you want to build the client yourself. The dependency delivers connection config, `{ url }`, and nothing else. In your server entry, build the client you already know (`pg`, Bun's `SQL`, an ORM): ```ts title="src/auth/service.ts" import { compute, rawPostgres } from '@prisma/composer-prisma-cloud'; export default compute({ name: 'auth', deps: { db: rawPostgres() }, // ... }); ``` ```ts title="src/auth/server.ts" import { SQL } from 'bun'; const { db } = service.load(); const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10, prepare: false }); ``` Those pool settings matter. Prisma Compute scales to zero and closes idle connections, so keep the pool small and reconnect-friendly. `prepare: false` turns off Bun's named prepared statements: the local Postgres that `npx prisma dev` runs shares one session across client connections, so after the pool reconnects, the first query fails with `prepared statement "..." already exists` unless prepared statements are off. See [Deploying](https://www.prisma.io/docs/composer/deploying#production-behavior). The module that owns the database provisions it: ```ts title="module.ts" const db = provision(rawPostgres({ name: 'database' })); provision(authService, { deps: { db } }); ``` ## `postgres()`: a Prisma ORM-typed database [#postgres-a-prisma-orm-typed-database] If you want typed queries and managed migrations, make the database a [Prisma ORM](https://www.prisma.io/docs/prisma-orm) one. `load()` then returns `{ url, client }`: the raw connection string, plus a client generated from your data contract. Queries like `db.client.orm.public.Product.where({ id }).first()` are compile-time checked, with no SQL strings and no row mapping. The client is constructed only on first access, so a service that brings its own Postgres client can still read `db.url` and keep contract-checked wiring and deploy-time migrations. The schema workflow runs once per schema change, using the [Prisma ORM CLI commands](https://www.prisma.io/docs/cli): 1. Edit `contract.prisma`, your schema. 2. Run `contract emit` to regenerate `contract.json` and `contract.d.ts` from it. 3. Run `migration plan` to author the migration into `migrations/`. 4. Deploy. The deploy applies `migrations/` before the service starts. There is no `CREATE TABLE IF NOT EXISTS` anywhere in app code. In your app, wrap the emitted contract once. Both the resource and every service that queries it reference that one value: ```ts title="src/data.ts" import { dataContract } from '@prisma/composer-prisma-cloud/orm'; import type { Contract } from '../contract.d.ts'; import contractJson from '../contract.json' with { type: 'json' }; export const catalogData = dataContract(contractJson); ``` You call `postgres()` on both sides of the wiring, and what you pass it tells the two apart. Passing the contract alone declares the dependency, the service saying what it queries: ```ts deps: { db: postgres(catalogData) } ``` Passing an options object declares the resource. The module that owns the database provisions it, naming the `prisma.config.ts` path (relative to the module file) so the deploy can reload the emitted `contract.json` and find `migrations/`: ```ts const db = provision( postgres({ name: 'database', contract: catalogData, config: './prisma.config.ts' }), ); ``` Because both ends share the contract value, the deploy refuses to wire a service against a database whose schema does not match. Deploy state records only the contract's identity; the full emitted contract is reloaded through `prisma.config.ts` at deploy time, and if `contract.json` is missing, unreadable, or no longer matches the declared contract, the deploy fails before touching the database. For complete working versions, see [`examples/orm-demo`](https://github.com/prisma/composer/tree/main/examples/orm-demo) (one service, one typed database) and [`examples/store`](https://github.com/prisma/composer/tree/main/examples/store) (the full pattern inside a reusable Module) in the Composer repository. ## Local databases [#local-databases] `dev` provisions real local Postgres instances for both kinds, so you can migrate and query them exactly as you would in production, with no cloud credentials. The data persists across restarts until you pass `--fresh`. See [Local development](https://www.prisma.io/docs/composer/local-development). ## Next steps [#next-steps] * [Object storage](https://www.prisma.io/docs/composer/object-storage): the other stateful resource, an S3-compatible bucket. * [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules): keep a database private inside a Module. * [Prisma ORM CLI reference](https://www.prisma.io/docs/cli): the contract and migration commands in detail. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. - [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command. # Deploying (/docs/composer/deploying) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. Location: Composer > Deploying `deploy` takes your entry file (the one whose default export is the root module) and stands the whole app up on [Prisma Compute](https://www.prisma.io/docs/compute) and [Prisma Postgres](https://www.prisma.io/docs/postgres). You choose the target environment, production or an isolated **stage**, on the command line rather than in your code. Tearing an environment down is the `destroy` operation of the [control API](#driving-deploys-from-code). | You want to... | Run | | --------------------------------- | -------------------------------------------- | | Deploy to production | `npx prisma deploy module.ts` | | Deploy an isolated environment | `npx prisma deploy module.ts --stage ` | | Deploy under a different app name | `npx prisma deploy module.ts --name demo-42` | ## Credentials [#credentials] On your machine, `deploy` uses the session stored by `auth login`, the same sign-in every other CLI command uses: #### bun ```bash bunx prisma auth login ``` #### pnpm ```bash pnpm prisma auth login ``` #### yarn ```bash yarn prisma auth login ``` #### npm ```bash npx prisma auth login ``` In CI or any headless environment, use a service token instead. Set two environment variables: * `PRISMA_SERVICE_TOKEN`: create a service token for your workspace in the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=composer). * `PRISMA_WORKSPACE_ID`: in the workspace's settings. A fresh checkout with those two variables set deploys successfully once the app's project exists. The very first deploy creates that project, and a new project needs a region: set it in the deploy config or in the environment, as described in the [`deploy` reference](https://www.prisma.io/docs/cli/deploy). Once the project exists it keeps its region, and later deploys, stages included, inherit it. The CLI finds or creates everything else. Keep the values out of the repo (an `.env` you source at deploy time, or CI secrets). When `PRISMA_SERVICE_TOKEN` is set it takes priority over any stored session. ## Build first [#build-first] `deploy` does not build for you. It assembles what your build produced: #### bun ```bash bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn build yarn prisma deploy module.ts ``` #### npm ```bash npm run build npx prisma deploy module.ts ``` Deploy state records what is already provisioned, so a re-deploy applies the difference instead of recreating everything. The platform stores this state with the environment it describes, scoped to that environment's branch inside the app's project. It is not stored on your machine, so your laptop and CI see the same state. Concurrent deploys of the same environment take turns through a lease on that state. While one deploy holds the lease, a second deploy fails immediately with a message naming the holder. If a deploy crashes, its lease expires after about a minute, and the next deploy takes over. ## Production and stages [#production-and-stages] Deploying with no `--stage` targets **production**. In platform terms, the app is a project (named after your root module), and production lives at the project level. The name is looked up in your workspace: a project this module deployed before is reused, and redeploying converges it. A project with the same name whose hosted state the CLI cannot identify or verify (one created outside this module's deploys, for example) stops the command with `HostedStateBootstrapError`. Use `--name` to deploy under a different name, or rename the module. `--stage ` deploys a complete, isolated copy of the app as a [preview branch](https://www.prisma.io/docs/compute/branching) of that same project: its own services, its own databases, its own configuration. The only thing a stage shares with production is the code. Composer's *stage* and Compute's *preview branch* are the same object seen from two tools, which is why the stage's variables are the branch's [environment variables](https://www.prisma.io/docs/compute/environment-variables): #### bun ```bash bunx prisma deploy module.ts # production bunx prisma deploy module.ts --stage staging # a persistent staging environment bunx prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### pnpm ```bash pnpm prisma deploy module.ts # production pnpm prisma deploy module.ts --stage staging # a persistent staging environment pnpm prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### yarn ```bash yarn prisma deploy module.ts # production yarn prisma deploy module.ts --stage staging # a persistent staging environment yarn prisma deploy module.ts --stage pr-42 # one environment per PR ``` #### npm ```bash npx prisma deploy module.ts # production npx prisma deploy module.ts --stage staging # a persistent staging environment npx prisma deploy module.ts --stage pr-42 # one environment per PR ``` Re-deploying any environment is idempotent: it updates the resources in place. A stage name must be a valid git ref name. An invalid name fails the deploy rather than being renamed. ## What a deploy prints [#what-a-deploy-prints] A deploy ends by printing what it made, in your own terms: the names you authored, what each one became on the platform, and the public URLs: ```text no-copy storefront-auth ├─ auth │ └─ api compute-service cps_abc123 │ https://xyz.ewr.prisma.build ├─ db postgres-database db_def456 └─ web compute-service cps_ghi789 https://uvw.ewr.prisma.build ``` The tree is your module structure: `auth.api` is the `api` service inside the `auth` Module. Under each name is the platform resource it became and its ID, the thing to search for in the Console when you need it. URLs appear for publicly reachable endpoints. That is why a Compute service prints one and a database does not: a database has a connection string, not a public endpoint. Secrets are left out entirely, so a node whose only output is a keypair prints no resource line. A node that deployed but published nothing reportable still appears in the tree, marked `(no entities reported)`. ## Removing resources [#removing-resources] Your module is the source of truth for what exists. To remove a resource, delete its provision from the code and deploy again: the deploy compares the declaration against the environment's deploy state and deletes whatever is no longer declared, alongside any creates and updates the same deploy needs. ```text no-copy Plan: 3 to update, 4 to delete, 1 to noop [web2-deploy] deleted [COMPOSER_WEB2_PORT-var] deleted [COMPOSER_WEB2_ORIGIN-var] deleted [web2-svc] deleted ``` A removed service takes its deployment and its wiring with it, and the resources that remain are untouched: a two-service app whose second service is deleted from the module keeps serving from the first without interruption. Two things a deploy never removes: 1. **The last service.** An app is at least one service; a module that provisions nothing fails with `ASSEMBLE.SERVICE_MISSING` before anything runs, so you cannot empty an environment by emptying the module. 2. **The project and its branches.** Those are the containers an environment deploys into: created if absent on deploy, and never deleted by one. Tearing down a whole environment is the [`destroy` operation](#destroying); deleting an entire project is [`project delete`](https://www.prisma.io/docs/cli/project), which asks you to repeat the project id with `--confirm ` because it destroys the project's databases too. ## Destroying [#destroying] The unified CLI has no `destroy` command. Tear an environment down with the `destroy` operation from `@prisma/composer/control` (see [Driving deploys from code](#driving-deploys-from-code)). It requires an explicit target, `{ kind: 'stage', stage }` or `{ kind: 'production' }`, and there is no default, so a teardown always names what it destroys. Unlike the CLI, the control API does not read the session stored by `auth login`. Export `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` (see [Credentials](#credentials)) before running the script, or it stops with `environment variable PRISMA_WORKSPACE_ID is required`: ```ts title="destroy-staging.ts" import { destroy } from '@prisma/composer/control'; const result = await destroy({ entry: 'module.ts', target: { kind: 'stage', stage: 'staging' } }); if (!result.ok) throw new Error(`${result.failure.code}: ${result.failure.message}`); ``` If you deployed under a different name with `--name`, pass the same `name` here so the operation finds the right project. Destroying a stage removes its resources, then deletes its branch along with the stage's deploy state. Destroying production removes the resources and, if the project is empty afterwards, deletes the project too, so empty projects do not pile up in your workspace. A project still holding another stage's resources stays in place. Tearing down a stage that was never deployed fails with "nothing deployed". It does not provision one first. ## CI [#ci] On GitHub, connect the repository and add [prisma/cloud-deploy-action](https://github.com/prisma/cloud-deploy-action) to a workflow instead of scripting the deploy yourself. The action derives the target from the branch: the default branch deploys production, and every other branch deploys a stage named after it. Connected repositories authenticate through GitHub's OIDC tokens, without a stored secret. [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push) walks through the setup. Any other CI runs the same commands as your machine: set the two credential variables as CI secrets, build, and deploy. The per-PR environment pattern: #### bun ```bash bunx prisma deploy module.ts --stage "pr-$PR_NUMBER" # on push ``` #### pnpm ```bash pnpm prisma deploy module.ts --stage "pr-$PR_NUMBER" # on push ``` #### yarn ```bash yarn prisma deploy module.ts --stage "pr-$PR_NUMBER" # on push ``` #### npm ```bash npx prisma deploy module.ts --stage "pr-$PR_NUMBER" # on push ``` When the PR closes, tear the stage down with the `destroy` operation, targeting `{ kind: 'stage', stage: 'pr-' }` from a small script. One extra step applies if your app binds input fields with `envSecret` or `envParam` (see [Service input](https://www.prisma.io/docs/composer/service-input)). Each stage stores its own copy of those platform variables. The deploying shell only seeds them. A fresh stage, such as a new `pr-42`, starts with none of them, so CI must export the values alongside the two credential variables. The first deploy then copies any missing values from the shell up to the stage. If a name is absent from both the platform and the shell, the deploy fails early and names the missing variable. ## When prisma.config.ts fails on an effect version conflict [#when-prismaconfigts-fails-on-an-effect-version-conflict] The deploy engine Composer drives needs the exact `effect` version `@prisma/composer` pins. The extensions in your `composer` section import that engine, so when your installed tree gives it a different `effect`, evaluating `prisma.config.ts` fails before any command runs: ```text no-copy CLI.CONFIG_UNREADABLE /path/to/prisma.config.ts could not be evaluated: Cannot find module '.../node_modules/effect/dist/unstable/http/FetchHttpClient.js' ``` The message after the colon is the module error from inside the engine, and it varies with the versions involved: a missing `effect` module, or an `effect` export that is not a function. The command exits with code 2. Commands that do not read the config, such as `prisma --version`, still run. This happens when your app, or another dependency of it, pins a different `effect` than `@prisma/composer` does and your package manager hoists that copy where the deploy engine resolves it. A plain Composer app never hits it: `@prisma/composer` and `@prisma/composer-prisma-cloud` pin every `effect`-family package the engine would otherwise float, so a fresh install resolves a single `effect`. The fix is to use the same `effect` as Composer: match your own `effect` dependency to `@prisma/composer`'s exact pin (see its `dependencies.effect`), or, when a dependency you cannot change pins another version, force Composer's with your package manager's override setting and reinstall. Each one takes the version in `@prisma/composer`'s `dependencies.effect`. npm reads `overrides` in `package.json`: ```json title="package.json" "overrides": { "effect": "" } ``` pnpm 11 and later read `overrides` in `pnpm-workspace.yaml` and ignore the `"pnpm"` field of `package.json`: ```yaml title="pnpm-workspace.yaml" overrides: effect: "" ``` pnpm 10 and earlier read `overrides` under `"pnpm"` in `package.json`: ```json title="package.json" "pnpm": { "overrides": { "effect": "" } } ``` Yarn reads `resolutions` in `package.json`: ```json title="package.json" "resolutions": { "effect": "" } ``` The setting only takes effect when the tree is rebuilt, and it goes stale when you upgrade `@prisma/composer`, so update it with each upgrade. ## When a deploy stops on a missing connection value [#when-a-deploy-stops-on-a-missing-connection-value] A dependency's connection declares the values it needs, by name, and the node on the other end has to supply them. When one does not, the deploy stops and names the edge rather than standing the app up: ```text no-copy Connection input "auth.db" declares param "url", but its producer "db" did not supply it — the producer's outputs carry [host]. ``` Fix it at whichever end is wrong: add the name to the outputs the producer returns, or mark the param `optional` on the connection if absent is a valid value (the consumer then reads `undefined`). Do not make the param optional just to route around the error: that reinstates a silent `undefined`. You will only meet this error if you authored the connection or an extension on one side of the wire. Every block that ships with the framework supplies what it declares. ## Production behavior [#production-behavior] Behavior you will run into once the app is deployed, and what to do about it: * **Compute scales to zero, and idle database connections get closed.** A long-lived client that treats a dropped connection as fatal will crash-loop through 502s. Keep the pool small and reconnect-friendly, turn off named prepared statements so a reconnect against the local `prisma dev` database does not fail with `prepared statement "..." already exists`, and do not let an async error kill the process: ```ts const sql = new SQL({ url: db.url, max: 1, idleTimeout: 10, prepare: false }); process.on('uncaughtException', (err) => console.error('uncaughtException', err)); process.on('unhandledRejection', (err) => console.error('unhandledRejection', err)); ``` * **Bind `0.0.0.0`, not loopback.** Compute routes external HTTP to the VM; a `localhost` listener is unreachable from outside. * **A deployed `/rpc/` returns `401` to you.** Calls are [authenticated for you](https://www.prisma.io/docs/composer/services-and-contracts#calls-are-authenticated-for-you), and your `curl` is not one of the services the app connected. Reach it through a consumer instead. * **Calls into a sleeping service can get `ECONNRESET`** while it cold-starts. Retry them; the generated RPC client already does. * **The `cron` scheduler stays awake on purpose.** It holds Compute's keep-awake guard for its whole lifetime, so it never scales to zero: one warm instance per app is the cost of the clock. Every other service, the runner included, sleeps as usual. * **The `COMPOSER_*` variables in your project belong to the deploy.** Config, secret pointers, and service keys all land there, and every deploy rewrites them. An edit made by hand does not survive the next deploy. * **Streaming responses stream, but the first bytes have a 60-second deadline.** Compute's ingress forwards your response as your service sends it; it does not hold it back until the response completes. What it bounds is the wait for the response to start: send nothing for 60 seconds and the client gets `504 Gateway Time-out` and the request is cancelled. Send the first bytes early, keep data flowing rather than letting a started stream sit idle, and have a long-lived client reconnect instead of holding one connection open. See [Request timeout](https://www.prisma.io/docs/compute/request-timeout). * **Next.js pages that call `service.load()` need `export const dynamic = 'force-dynamic'`.** The runtime environment does not exist at build time, and Next.js will not re-read it for prerendered routes. ## Driving deploys from code [#driving-deploys-from-code] Everything the CLI does is also callable in-process, from `@prisma/composer/control`: typed `deploy`, `destroy`, `dev`, and `log` operations that return structured results instead of printing and exiting. The operations and their result shapes are documented in [the control API](https://www.prisma.io/docs/cli/deploy#the-control-api) on the `deploy` command reference. ## Next steps [#next-steps] * [Local development](https://www.prisma.io/docs/composer/local-development): the same pipeline, no credentials. * [Service input](https://www.prisma.io/docs/composer/service-input): how per-stage configuration and secrets travel. * [`deploy` command reference](https://www.prisma.io/docs/cli/deploy): every flag, plus the control API for destroy and log. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Getting started`](https://www.prisma.io/docs/composer/getting-started): Build a two-service Prisma App from an empty directory and run it on your machine with one command. # Getting started (/docs/composer/getting-started) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Build a two-service Prisma App from an empty directory and run it on your machine with one command. Location: Composer > Getting started By the end of this page you will have a working Prisma App: a `quotes` API and a public `gateway` that calls it. A typed contract wires the two together, and one command runs them on your machine. Along the way you will meet every core Composer idea once: a contract, a service, a root module, a build, and a local run. The app is deliberately tiny, with no database, so you can see the whole shape at once. Adding a Postgres is the natural next step. See [Databases](https://www.prisma.io/docs/composer/databases) when you are ready. > [!NOTE] > Working with a coding agent > > Add the Composer skill first, even if you plan to write every line yourself. It ships inside the `@prisma/composer` package, and one command, run after step 1 installs the packages, copies it into the directories your agent reads and stops the agent from inventing an API that does not exist: > > > > > #### bun > ```bash > bunx --bun prisma@latest init > ``` > > > #### pnpm > ```bash > pnpm dlx prisma@latest init > ``` > > > #### yarn > ```bash > yarn dlx prisma@latest init > ``` > > > #### npm > ```bash > npx prisma@latest init > ``` > > > > `init` also adds a `postinstall` hook that keeps the skill matching the installed version; see [`skills`](https://www.prisma.io/docs/cli/skills). ## Prerequisites [#prerequisites] * **Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer.** Check with `node --version`. Composer hands your TypeScript entry file straight to Node. Node runs `.ts` files directly only from 22.18.0, the release that turns type stripping on by default. On any older version, including 22.17, Composer commands stop at `ERR_UNKNOWN_FILE_EXTENSION` naming your own entry file. * **[Bun](https://bun.sh).** Prisma Compute runs Bun, so the server code targets it (`Bun.serve`). This guide also uses `bun build` to produce the built output. * **npm, pnpm, or Bun.** Bun workspaces need two extra direct dependencies; see the note in step 1. You do not need a Prisma account for this page. Local runs never talk to the platform. You only need credentials when you [deploy](https://www.prisma.io/docs/composer/deploying). ## 1. Set up the project [#1-set-up-the-project] Create a directory and install the two Composer packages, plus [arktype](https://arktype.io) for the contract schemas: #### bun ```bash bun init npm pkg set type module # couldn't auto-convert command bun add @prisma/composer @prisma/composer-prisma-cloud arktype bun add --dev typescript @types/bun prisma ``` #### pnpm ```bash pnpm init npm pkg set type=module # couldn't auto-convert command pnpm add @prisma/composer @prisma/composer-prisma-cloud arktype pnpm add -D typescript @types/bun prisma ``` #### yarn ```bash yarn init npm pkg set type=module # couldn't auto-convert command yarn add @prisma/composer @prisma/composer-prisma-cloud arktype yarn add --dev typescript @types/bun prisma ``` #### npm ```bash npm init npm pkg set type=module npm install @prisma/composer @prisma/composer-prisma-cloud arktype npm install -D typescript @types/bun prisma ``` `"type": "module"` matters: Composer loads your entry file as an ES module, and without it every Composer command stops at `COMPOSE.ENTRY_UNLOADABLE` ("Cannot use import statement outside a module"). You do not need to pin `effect` yourself. `@prisma/composer` and `@prisma/composer-prisma-cloud` pin every `effect`-family package their deploy engine needs, so a fresh install resolves a single copy. A conflict only appears when something in your dependency tree, your own `effect` pin or another dependency's, hoists a different `effect` where the deploy engine resolves it. The CLI then cannot evaluate `prisma.config.ts` and stops with `CLI.CONFIG_UNREADABLE`; see [When prisma.config.ts fails on an effect version conflict](https://www.prisma.io/docs/composer/deploying#when-prismaconfigts-fails-on-an-effect-version-conflict). > [!NOTE] > Bun workspaces > > In a Bun workspace (a monorepo with isolated installs), two of Composer's indirect dependencies must also be direct ones, because Bun does not expose transitive packages or their bins to your app: > > ```bash > bun add alchemy > bun add -d @prisma/dev > ``` > > Without `alchemy`, `dev` and `deploy` stop at `DEPLOY.ALCHEMY_BIN_MISSING`; without `@prisma/dev`, local dev stops with "local dev needs @prisma/dev". A single-package Bun project is not affected. Add a `tsconfig.json` so the compiler checks your wiring the same way the deploy will: ```json title="tsconfig.json" { "compilerOptions": { "target": "ES2022", "module": "Preserve", "moduleResolution": "bundler", "allowImportingTsExtensions": true, "noEmit": true, "strict": true, "skipLibCheck": true, "types": ["bun"] }, "include": ["module.ts", "src"] } ``` This is what you are about to create: ```text no-copy my-app/ ├── module.ts # the root module: the app itself ├── prisma.config.ts # deploy config: the composer section (read only by the CLI) └── src/ ├── quotes/ │ ├── contract.ts # the quotes service's public API, as types │ ├── service.ts # what quotes is: deps + build + what it exposes │ └── server.ts # the code that actually runs └── gateway/ ├── service.ts └── server.ts ``` ## 2. Create the quotes service [#2-create-the-quotes-service] A service is three files. First, the **contract**: the API other services will call, written as schemas. It lives with the service that owns it. Any [Standard Schema](https://standardschema.dev) validator works (arktype, zod, valibot); these examples use arktype: ```ts title="src/quotes/contract.ts" import { contract, rpc } from '@prisma/composer/service-rpc'; import { type } from 'arktype'; export const quotesContract = contract({ random: rpc({ input: type({}), output: type({ quote: 'string' }) }), }); ``` Second, the **service declaration**. This is pure data with no behavior. It says what the service is called, what it depends on (nothing yet), how it is built, and which contract it exposes: ```ts title="src/quotes/service.ts" import node from '@prisma/composer/node'; import { compute } from '@prisma/composer-prisma-cloud'; import { quotesContract } from './contract.ts'; export default compute({ name: 'quotes', deps: {}, build: node({ module: import.meta.url, entry: '../../dist/quotes/server.mjs' }), expose: { rpc: quotesContract }, }); ``` Third, the **server**: the code your build turns into `dist/quotes/server.mjs` and the platform boots. `serve()` generates the HTTP handler from the contract. If you forget a handler or return the wrong shape, the code does not compile: ```ts title="src/quotes/server.ts" import { serve } from '@prisma/composer/service-rpc'; import service from './service.ts'; const port = service.port(); // the reserved port, resolved (default 3000) const QUOTES = [ 'Simplicity is prerequisite for reliability.', 'Make it work, make it right, make it fast.', ]; const handler = serve(service, { rpc: { random: async () => ({ quote: QUOTES[Math.floor(Math.random() * QUOTES.length)]! }), }, }); export default handler; // Bind all interfaces. Compute routes external HTTP to the VM, so a // loopback-only listener would be unreachable. Bun.serve({ port, hostname: '0.0.0.0', fetch: handler }); ``` Notice what is missing: no URL of anything and no `process.env`. Every service gets a port for free, read through `service.port()`. Dependencies arrive through `service.load()`. Any configuration of your own arrives through `service.input()` (see [Service input](https://www.prisma.io/docs/composer/service-input)). Those three typed accessors are the whole framework contract with your code. ## 3. Create the gateway service [#3-create-the-gateway-service] The gateway depends on the quotes contract. Note the asymmetry with step 2: the quotes service *exposed* the bare contract (what it provides), while the gateway wraps it in `rpc()` (what it needs: a client of this contract). Declaring `deps: { quotes: rpc(quotesContract) }` means `service.load()` hands the server a ready-made typed client: ```ts title="src/gateway/service.ts" import node from '@prisma/composer/node'; import { rpc } from '@prisma/composer/service-rpc'; import { compute } from '@prisma/composer-prisma-cloud'; import { quotesContract } from '../quotes/contract.ts'; export default compute({ name: 'gateway', deps: { quotes: rpc(quotesContract) }, build: node({ module: import.meta.url, entry: '../../dist/gateway/server.mjs' }), }); ``` ```ts title="src/gateway/server.ts" import service from './service.ts'; const { quotes } = service.load(); const port = service.port(); Bun.serve({ port, hostname: '0.0.0.0', fetch: async () => { const { quote } = await quotes.random({}); return new Response(quote); }, }); ``` The gateway exposes no contract of its own, so there is no `serve()` here. It is an ordinary HTTP server that happens to receive a typed client, and calling `quotes.random({})` is an ordinary async function call. ## 4. Compose the app [#4-compose-the-app] The root module is the app. It provisions both services and wires the quotes service's exposed port into the gateway's dependency slot. `provision()` returns a ref carrying one port per exposed contract, so `quotes.rpc` exists because the service declared `expose: { rpc: ... }`: ```ts title="module.ts" import { module } from '@prisma/composer'; import gatewayService from './src/gateway/service.ts'; import quotesService from './src/quotes/service.ts'; export default module('my-app', ({ provision }) => { const quotes = provision(quotesService); provision(gatewayService, { deps: { quotes: quotes.rpc } }); }); ``` Next to it goes `prisma.config.ts`, the one config file the Prisma CLI reads. The deploy config is its `composer` section: the extensions that deploy your app and the store that keeps deploy state. Only the CLI reads this file; your app code never imports it. If you ran `prisma init` for the agent skill, `prisma.config.ts` already exists with a `skills` section. Add the `composer` key to its `definePrismaConfig` call and keep `skills`; the `postinstall` hook `init` added reads it. The file then looks like this, with your own agents in `skills`: ```ts title="prisma.config.ts" import { defineConfig as composer } from '@prisma/composer/config'; import { nodeBuild } from '@prisma/composer/node/control'; import { prismaCloud, prismaState } from '@prisma/composer-prisma-cloud/control'; import { definePrismaConfig } from 'prisma/config'; export default definePrismaConfig({ skills: { agents: ['claude'], }, composer: composer({ extensions: [prismaCloud(), nodeBuild()], state: prismaState(), }), }); ``` Otherwise create the file with the `composer` section alone: ```ts title="prisma.config.ts" import { defineConfig as composer } from '@prisma/composer/config'; import { nodeBuild } from '@prisma/composer/node/control'; import { prismaCloud, prismaState } from '@prisma/composer-prisma-cloud/control'; import { definePrismaConfig } from 'prisma/config'; export default definePrismaConfig({ composer: composer({ extensions: [prismaCloud(), nodeBuild()], state: prismaState(), }), }); ``` The commands find `prisma.config.ts` from the directory you run them in, so run them from the app's root. When you add a database, its `orm` section goes in this same file. ## 5. Build [#5-build] You own the build. The framework only assembles what you built, and it has one requirement: each entry must be a single self-contained file, with everything inlined except the runtime's own built-ins (`bun`, `bun:*`, `node:*`). Any bundler that produces such a file works. This guide uses Bun. Build the two services separately, because a multi-entry build would split the shared contract code into a chunk neither output contains: ```json title="package.json" { "scripts": { "build": "bun build src/quotes/server.ts --target=bun --outfile dist/quotes/server.mjs && bun build src/gateway/server.ts --target=bun --outfile dist/gateway/server.mjs" } } ``` #### bun ```bash bun run build ``` #### pnpm ```bash pnpm run build ``` #### yarn ```bash yarn build ``` #### npm ```bash npm run build ``` This is also a good moment to typecheck. The compiler checks the whole wiring, so a dependency wired to the wrong producer or a missing RPC handler fails here, in seconds: #### bun ```bash bunx tsc --noEmit ``` #### pnpm ```bash pnpm tsc --noEmit ``` #### yarn ```bash yarn tsc --noEmit ``` #### npm ```bash npx tsc --noEmit ``` ## 6. Run it locally [#6-run-it-locally] One command brings the whole app up on your machine, both services wired together, with no cloud credentials: #### bun ```bash bunx prisma dev module.ts ``` #### pnpm ```bash pnpm prisma dev module.ts ``` #### yarn ```bash yarn prisma dev module.ts ``` #### npm ```bash npx prisma dev module.ts ``` It runs the same pipeline a deploy runs, against local stand-ins for Prisma Compute and Prisma Postgres, and prints each service's local URL: ```text no-copy dev: ready gateway: http://localhost:3001 quotes: http://localhost:3000 ``` Verify it works by calling the gateway: ```bash curl localhost:3001 ``` ```text no-copy Make it work, make it right, make it fast. ``` That response traveled over one typed RPC hop: the gateway received your request, called `quotes.random({})` through its injected client, and returned the result. `dev` keeps running, watches your built output, and restarts a service when its build changes (run `npm run build` in another terminal and the affected service restarts). `Ctrl-C` stops it. Your local data stays, so the next start is warm. See [Local development](https://www.prisma.io/docs/composer/local-development) for logs, `--fresh`, and what persists. Now try the quotes service directly: ```bash curl -X POST localhost:3000/rpc/random -H 'content-type: application/json' -d '{}' ``` You get `401 Unauthorized`. That is deliberate: Composer mints a **service key** for each dependency edge and the quotes service accepts only calls from the gateway. Neither service's code mentions a key. See [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts#calls-are-authenticated-for-you) for how this works. ## 7. Deploy it (optional) [#7-deploy-it-optional] Local dev needed no account. Deploying the same app to [Prisma Compute](https://www.prisma.io/docs/compute) needs a signed-in session: #### bun ```bash bunx prisma auth login bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm prisma auth login pnpm prisma deploy module.ts ``` #### yarn ```bash yarn prisma auth login yarn prisma deploy module.ts ``` #### npm ```bash npx prisma auth login npx prisma deploy module.ts ``` (In CI, where the browser sign-in is not an option, set `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` from the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=composer) instead; see [Deploying](https://www.prisma.io/docs/composer/deploying#credentials).) A new project needs a region, so give it one before the first deploy, in the deploy config or in the environment. The [`deploy` reference](https://www.prisma.io/docs/cli/deploy) shows both ways. The CLI creates a project named `my-app` in your workspace (a project this module deployed before is reused; one whose hosted state the CLI cannot verify stops the deploy with `HostedStateBootstrapError`, so pass `--name ` in that case), provisions both services on Prisma Compute, points the gateway's `quotes` dependency at the deployed quotes service, and starts everything. The deploy finishes by printing what it made: your module names, the platform resource each became, and the public URLs. Open the gateway's URL and you get a quote. Re-deploying is idempotent. For an isolated copy of the whole app, deploy a stage: #### bun ```bash bunx prisma deploy module.ts --stage demo ``` #### pnpm ```bash pnpm prisma deploy module.ts --stage demo ``` #### yarn ```bash yarn prisma deploy module.ts --stage demo ``` #### npm ```bash npx prisma deploy module.ts --stage demo ``` Tearing a stage down is the `destroy` operation of the [control API](https://www.prisma.io/docs/cli/deploy#the-control-api). That operation does not read your `auth login` session: it needs `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` in the environment, the same service-token credentials CI uses. Without them, delete the stage's branch in the [Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=composer) instead. See [Deploying](https://www.prisma.io/docs/composer/deploying) for stages, CI, and teardown in full. ## Where to go next [#where-to-go-next] * [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules): package a service and its database as a reusable Module. * [Databases](https://www.prisma.io/docs/composer/databases): add a Postgres, plain or typed by a Prisma ORM contract. * [Building blocks](https://www.prisma.io/docs/composer/building-blocks): compose the ready-made cron, storage, and streams Modules. * [Testing](https://www.prisma.io/docs/composer/testing): unit tests with `mockService`, integration tests with `bootstrapService`. * [Porting an existing app](https://www.prisma.io/docs/composer/porting-an-app): keep your server code and declare the app around it. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Prisma Composer (/docs/composer) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. A TypeScript framework for defining applications composed of multiple services and resources, and deploying them to Prisma Compute and Prisma Postgres. Location: Prisma Composer With Prisma Composer, you describe your whole application in TypeScript: each service, plus whatever it depends on, such as other services, [Prisma Postgres](https://www.prisma.io/docs/postgres) databases, scheduled jobs, object storage, and secrets. One deploy command then provisions the services on [Prisma Compute](https://www.prisma.io/docs/compute), the databases on Prisma Postgres, and the authenticated connections between them. The Prisma CLI's `deploy` command, the GitHub deploy action, and the Deploy Button all deploy a Composer module. You do not need Composer to deploy to Prisma Compute through the [REST API](https://www.prisma.io/docs/rest-api), though: it creates a deployment for a service and returns an upload URL for your built artifact, and tools such as [Alchemy](https://www.prisma.io/docs/compute/alchemy) deploy that way. See [Deployments](https://www.prisma.io/docs/compute/deployments) for every path. > [!NOTE] > Early Access > > Prisma Composer is in [Early Access](https://www.prisma.io/docs/console/more/feature-maturity#early-access). APIs and commands can change between releases. Composer ships inside the [Prisma CLI](https://www.prisma.io/docs/cli/deploy) as the root-level `dev` and `deploy` commands: run them with `npx prisma dev module.ts` and `npx prisma deploy module.ts`. ## What a Composer application is [#what-a-composer-application-is] A Composer application is a tree of services and resources. Each service declares the resources it needs, such as a database. Services call each other through typed contracts. Step through how the pieces relate: ## A minimal example [#a-minimal-example] You declare a service as data instead of writing deployment scripts. The declaration below says the `storefront` service calls the `catalog` service's API and is built as a Next.js app: ```ts title="src/storefront/service.ts" import nextjs from '@prisma/composer/nextjs'; import { rpc } from '@prisma/composer/service-rpc'; import { compute } from '@prisma/composer-prisma-cloud'; import { catalogContract } from '../catalog/contract.ts'; export default compute({ name: 'storefront', deps: { catalog: rpc(catalogContract) }, build: nextjs({ module: import.meta.url, appDir: '..' }), }); ``` At runtime, the service receives a typed client for each declared dependency from `service.load()`. The contract determines which methods that client exposes and their TypeScript types, and calling one is an ordinary async function call: ```ts title="src/storefront/data.ts" import service from './service.ts'; const { catalog } = service.load(); const { products } = await catalog.listProducts({}); ``` The root module ties the application together, wiring each declared dependency to the service or resource that provides it: ```ts title="module.ts" import { module } from '@prisma/composer'; import catalogModule from './src/catalog/module.ts'; import ordersModule from './src/orders/module.ts'; import storefrontService from './src/storefront/service.ts'; export default module('store', ({ provision }) => { const catalog = provision(catalogModule); const orders = provision(ordersModule, { deps: { catalog: catalog.rpc } }); provision(storefrontService, { deps: { catalog: catalog.rpc, orders: orders.rpc } }); }); ``` TypeScript checks the wiring: a dependency bound to a producer with a different contract, a missing RPC handler, or a config value of the wrong shape is a compile error, not a failed deploy. ## From declaration to deployment [#from-declaration-to-deployment] `npx prisma deploy module.ts` loads the root module, compares it with the deploy state stored for that environment, and creates or updates the services on Prisma Compute and the databases on Prisma Postgres. Re-running a deploy applies only the difference: ## Core concepts [#core-concepts] * **App**: the complete deployable application, defined by the root module. Deployed as one Prisma [project](https://www.prisma.io/docs/compute#the-model). * **Service**: a running application component, declared with `compute()`. Deployed as one Prisma Compute service. * **Resource**: something a service depends on that is not a service: a Prisma Postgres database, a bucket, or a secret. * **Contract**: the typed interface through which services communicate, written as [Standard Schema](https://standardschema.dev) definitions. * **Module**: a reusable unit that provisions services and resources behind a typed boundary, such as a service packaged with its own database. [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules) covers each in depth. ## Design principles [#design-principles] Two choices shape the rest of the framework. First, application code does not read `process.env` or hardcode URLs. Dependencies arrive typed through `service.load()`, so production, an isolated stage, and a test all run the same code with different injected values. Second, Composer does not bundle or transform your code. You build with your own bundler, and the deploy assembles what you built. ## Get started [#get-started] - [Getting started](https://www.prisma.io/docs/composer/getting-started): Build and run a two-service application locally, then deploy it. - [Port an existing app](https://www.prisma.io/docs/composer/porting-an-app): Keep your server code and add the declarations around it. ## Use Composer with an AI coding agent [#use-composer-with-an-ai-coding-agent] Composer's declaration-based API works well with coding agents because mistakes fail the TypeScript compile instead of a deploy. The Composer skill ships inside the `@prisma/composer` package, so it always matches the version you installed. Before your agent writes Composer code, install it: #### bun ```bash bunx prisma skills sync ``` #### pnpm ```bash pnpm prisma skills sync ``` #### yarn ```bash yarn prisma skills sync ``` #### npm ```bash npx prisma skills sync ``` The `prisma-composer-core-concepts` skill documents the full authoring API, the CLI commands, and the first-party Modules for scheduled jobs, object storage, and event streams. It is one of several [Prisma agent skills](https://www.prisma.io/docs/ai/tools/skills). Review the generated declarations like any other TypeScript: `npx tsc --noEmit` checks the wiring before you deploy. ## What to read next [#what-to-read-next] Start with [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules) for how services, resources, and Modules compose into an app, then [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts) for the typed RPC between them. When you're ready to run something, [Local development](https://www.prisma.io/docs/composer/local-development) works with no cloud credentials, and [Deploying](https://www.prisma.io/docs/composer/deploying) covers production, stages, and CI. Check [Limitations](https://www.prisma.io/docs/composer/limitations) before committing to a design. ## Related pages - [`Choose a Prisma ORM setup path`](https://www.prisma.io/docs/getting-started): Choose the fastest path to try Prisma ORM in a new or existing project. - [`Console`](https://www.prisma.io/docs/console): Learn how to use the Console to manage and integrate Prisma products into your application. - [`Introduction to Prisma ORM`](https://www.prisma.io/docs/prisma-orm): Prisma ORM 8 is a release candidate. - [`Local development`](https://www.prisma.io/docs/local-development): Run the whole Prisma stack on your machine, with your app on Bun, a local Prisma Postgres database, and local object storage. - [`Overview`](https://www.prisma.io/docs/cli): Prisma CLI reference # Limitations (/docs/composer/limitations) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. What Prisma Composer does not do yet, so you can name the gap instead of working around it blind. Location: Composer > Limitations Prisma Composer is in [Early Access](https://www.prisma.io/docs/console/more/feature-maturity#early-access). This page lists what it does not do yet, so you can plan around a gap instead of discovering it mid-build. If something you need is missing here and below, check the [examples](https://github.com/prisma/composer/tree/main/examples) and design docs in the [Composer repository](https://github.com/prisma/composer), then file an issue there rather than guessing at an API. ## Contracts and communication [#contracts-and-communication] * **RPC over HTTP is the only contract kind.** No gRPC, WebSocket, or streaming contracts. * **No in-memory contract bindings.** A dependency cannot yet be wired to a co-located handler without HTTP; use [`bootstrapService`](https://www.prisma.io/docs/composer/testing#integration-tests-bootstrapservice) with a loopback fake in tests. ## Deploys and auth [#deploys-and-auth] * **No interactive login for deploys.** Deploys authenticate only via a static `PRISMA_SERVICE_TOKEN`; there is no browser flow. * **Deploys do not build.** `deploy` assembles your built output; the build is always yours to run first. * **The `effect` family must be pinned** in your app's `package.json` until an upstream dependency fixes its version ranges; see [Getting started](https://www.prisma.io/docs/composer/getting-started#1-set-up-the-project). Every Composer command checks this and stops with a fix-naming error when the pin is missing. ## Platforms [#platforms] * **Windows is not supported yet** for local development (`dev`). * **Composer needs Node.js 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer.** Composer hands your TypeScript entry file straight to Node, and earlier versions cannot run `.ts` files directly. ## Runtime behavior to design around [#runtime-behavior-to-design-around] These are properties of running on [Prisma Compute](https://www.prisma.io/docs/compute) rather than missing features; [Deploying](https://www.prisma.io/docs/composer/deploying#production-behavior) covers each with the fix: * Compute scales to zero, so idle database connections get closed and cold calls can see `ECONNRESET`. * A deployed RPC endpoint answers only its wired consumers; hand-rolled probes get `401`. * Environment variables prefixed `COMPOSER_*` belong to the deploy and are rewritten on every deploy. * Streaming responses are delivered as your service sends them, but the service has 60 seconds to send the first bytes before the ingress returns `504 Gateway Time-out`; see [Request timeout](https://www.prisma.io/docs/compute/request-timeout). ## Next steps [#next-steps] * [Deploying](https://www.prisma.io/docs/composer/deploying): operational behavior in full. * [Building blocks](https://www.prisma.io/docs/composer/building-blocks): what ships today, and how extensions arrive. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Local development (/docs/composer/local-development) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Run the whole app on your machine with prisma dev, with no cloud credentials, and tail its logs through the control API. Location: Composer > Local development One command brings your whole app up on your machine, every service, its databases and buckets, wired together, with no cloud credentials. `dev` runs the same pipeline a deploy runs, but against local stand-ins for Prisma Compute and Prisma Postgres, so what you run locally is what you ship. Logs are read separately, through the control API's `log` operation. | You want to... | Run | | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | Bring the app up locally | `npx prisma dev module.ts` | | Start clean (wipe local data first) | `npx prisma dev module.ts --fresh` (fails for apps with a database in this release; see [the reset steps](#whats-local-vs-whats-real)) | `module.ts` is your entry file, the one whose default export is the root module, the same file you pass to `deploy`. No `PRISMA_*` variables are needed. Local dev never talks to the platform. ## Bring it up [#bring-it-up] Like deploy, `dev` runs your *built* output, so build first: #### bun ```bash bun run build bunx prisma dev module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma dev module.ts ``` #### yarn ```bash yarn build yarn prisma dev module.ts ``` #### npm ```bash npm run build npx prisma dev module.ts ``` It stands the app up and prints each service's local URL: ```text no-copy dev: ready gateway: http://localhost:3001 quotes: http://localhost:3000 ``` From here `dev` keeps running: it watches your built output and, when a service's build changes, restarts only that service. Rebuild in another terminal and the affected service picks up the change. `Ctrl-C` stops your app's service processes and exits. The local databases, buckets, and their data stay up, so the next `dev` is a warm start: same ports, same data. `--fresh` wipes this app's local instances and data before starting. `dev` does not print service logs. With several services running, streaming them all inline would bury the URL list and the restart notices, so logs are read separately. ## Logs [#logs] The unified CLI has no `log` command. Tail the merged logs of the already-running app with the `log` operation from `@prisma/composer/control`, the same in-process API that backs `deploy` and `dev` (see [the control API](https://www.prisma.io/docs/cli/deploy#the-control-api)): ```ts title="logs.ts" import { log } from '@prisma/composer/control'; const result = await log({ entry: 'module.ts', tail: 20, signal: AbortSignal.timeout(60_000) }); if (!result.ok) throw new Error(result.failure.message); for await (const { service, line } of result.value.lines) { console.log(`[${service}] ${line}`); } ``` Run it with `node logs.ts` next to a running `dev`. `log` attaches to the running app and hands back the merged stream as `lines`, an async iterable, so nothing prints until you iterate it. The `signal` ends the stream, here after a minute, so the script exits on its own; leave it out and stop with `Ctrl-C`. Each line carries the service it came from: ```text no-copy [quotes] serving a quote [quotes] serving a quote ``` It follows live, like `tail -f`. It only reads the running app. It never builds, provisions, starts, or stops anything, so you can start and stop it freely alongside a running `dev`. * **One service:** pass its dotted address, for example `cron.runner`, exactly as the startup output prints it. * **How much history:** a tail option sets how many recent lines to show before going live (default 20; `0` for live-only). Each service's log is cleared when it starts fresh, so you are never scrolling back through past sessions. * **No running app:** if you have not run `dev` (or you stopped it), the operation says so and points you at `dev`. ## What's local vs. what's real [#whats-local-vs-whats-real] Everything above the cloud boundary is real: your actual service code, real databases you can migrate and query, real object storage, and the same wiring a deploy creates, including [service keys](https://www.prisma.io/docs/composer/services-and-contracts#calls-are-authenticated-for-you). What is swapped are the providers underneath: local emulators stand in for Prisma Compute and Prisma Postgres, so no token, workspace, or network is involved. Three consequences worth knowing: * **Direct RPC probes get `401` here too.** `dev` runs the deploy pipeline, so providers only accept their wired consumers, exactly as in production. Exercise a provider through a consumer, or in [tests](https://www.prisma.io/docs/composer/testing), where nothing is provisioned and every call passes through. * **Unset secrets do not stop the app.** A secret you have not set in your shell gets a local placeholder and a one-line warning. The app boots and serves. Only the code path that actually uses that secret fails, at the real external service it calls. Set the secret in your shell to exercise that path. * **The emulators outlive a session.** They are shared, machine-wide daemons, so your data survives `Ctrl-C` and even a reboot until you pass `--fresh`. That is what makes restarts warm. Because they are shared, `dev` currently lists every app the emulators know about, including apps you started earlier and stopped, so ignore endpoints whose names are not in your `module.ts`. * **`--fresh` currently fails for an app with a database.** The Postgres emulator declines to close the app's local server, the recreate step cannot connect, and a plain `dev` afterwards waits forever on the missing database. To recover, stop the stuck `dev`, remove the app's local server with `npx prisma dev rm pcdev--` (run it from a directory without a Prisma ORM config), delete `.prisma-composer/dev` and `.alchemy` in the project, and run `dev` again. It re-provisions from scratch. The same reset fixes a `dev` that reports ready with none of its services listed, which happens when the emulators were restarted underneath a project. Windows is not supported yet; see [Limitations](https://www.prisma.io/docs/composer/limitations). ## Next steps [#next-steps] * [Deploying](https://www.prisma.io/docs/composer/deploying): the same app, on Prisma Compute. * [Testing](https://www.prisma.io/docs/composer/testing): drive a service in-process instead of running the whole app. * [Local development across the Prisma stack](https://www.prisma.io/docs/local-development): the app runtime, local Postgres, and local storage. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Object storage (/docs/composer/object-storage) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Provision an S3-compatible object-store bucket and use it from a service with any S3 client. Location: Composer > Object storage `bucket` is a raw S3-compatible object-store bucket: a flat key-value store with no higher-level abstractions attached. Like [`postgres()`](https://www.prisma.io/docs/composer/databases), it is a resource with two ends: the service declares the need, and the module that owns the bucket provisions it. ```ts title="src/uploads/service.ts" import { bucket, compute } from '@prisma/composer-prisma-cloud'; export default compute({ name: 'uploads', deps: { store: bucket() }, // dependency end: receives credentials // ... }); ``` ```ts title="module.ts" const store = provision(bucket({ name: 'uploads' })); // resource end: provisions + mints a keypair provision(uploadsService, { deps: { store } }); ``` Inside the service entry, `load()` hands back `{ url, bucket, accessKeyId, secretAccessKey }`, the standard S3 config set. Use any S3-compatible client (`@aws-sdk/client-s3`, `minio`, Bun's S3 API): ```ts title="src/uploads/server.ts" import { S3Client } from '@aws-sdk/client-s3'; const { store } = service.load(); const s3 = new S3Client({ endpoint: store.url, bucket: store.bucket, credentials: { accessKeyId: store.accessKeyId, secretAccessKey: store.secretAccessKey }, }); ``` ## Buckets and the storage Module [#buckets-and-the-storage-module] A bucket resource uses the same S3 contract kind as the [`storage` Module's](https://www.prisma.io/docs/composer/building-blocks) blob store. A service declared with `deps: { store: s3() }`, where `s3` is imported from `@prisma/composer-prisma-cloud/storage`, can be rewired to a `bucket` resource without changing the service declaration. Reach for the `storage` Module when you want a managed blob store with its own internals; reach for `bucket` when you want the raw S3 surface. `dev` provisions a local stand-in bucket with working credentials, so upload code runs locally without an account. See [Local development](https://www.prisma.io/docs/composer/local-development). ## Next steps [#next-steps] * [Building blocks](https://www.prisma.io/docs/composer/building-blocks): the storage and streams Modules built on the same contract. * [Service input](https://www.prisma.io/docs/composer/service-input): when a credential should be input instead of a dependency. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Port an existing app (/docs/composer/porting-an-app) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Bring an app you already have to Prisma Composer by declaring it, keeping your server code and your build. Location: Composer > Port an existing app Bringing an existing app to Composer does not mean rewriting it. The server code you already have stays the server, and you add a service declaration around it. This page covers the three common cases. ## Use with your agent [#use-with-your-agent] To delegate the port, run this from your project's root and hand the prompt to your coding agent: ```text Port this app to Prisma Composer. 1. Install `@prisma/composer` and `@prisma/composer-prisma-cloud`, and install `prisma` as a dev dependency (the deploy config in `prisma.config.ts` imports `prisma/config`), then install the Composer skill with `npx prisma@latest skills sync` (the skill ships inside the package; run `npx prisma@latest skills list` to confirm). Then read https://www.prisma.io/docs/composer/porting-an-app.md and follow it for this repository. 2. Survey the repo before changing anything and report back: every deployable server, its runtime (Node, Bun, or Next.js), its build command and entry file, every `process.env` read, and any database it connects to. Ask me about anything ambiguous. 3. Declare each server as a service with `compute()`, using the `node` or `nextjs` build adapter as appropriate, and compose them in a single `module.ts`. 4. Replace the `process.env` reads: the listen port becomes `service.port()` bound to `0.0.0.0`, config and credentials become fields of the service input schema, and anything another service provides becomes a typed dependency. Replace the URLs services use to reach each other with contracts. 5. If the app talks to Postgres, declare `rawPostgres()` (from `@prisma/composer-prisma-cloud`) as a dependency and build the existing client from the injected `db.url` instead of a connection-string variable. 6. Verify with `npx tsc --noEmit`, then build and run `npx prisma dev module.ts` and confirm every service boots with a local URL. Do not deploy; show me the diff first. ``` ## A Node.js or Bun service [#a-nodejs-or-bun-service] Add a `service.ts` with `compute({ name, deps, build: node({ module, entry }) })` pointing `entry` at your built server file, then make three changes to the server itself: 1. **Read the port from `service.port()`**, never `process.env`, and bind `0.0.0.0`: ```ts Bun.serve({ port: service.port(), hostname: '0.0.0.0', fetch: handler }); ``` 2. **Replace every other `process.env` read.** Config and credentials become fields of the service's [input schema](https://www.prisma.io/docs/composer/service-input). Anything another service provides becomes a [dependency](https://www.prisma.io/docs/composer/services-and-contracts). If a value differs per stage (an app origin, an external URL), bind it with `envParam`. Bind a credential with `envSecret`. 3. **If it talks to Postgres**: declare `deps: { db: rawPostgres() }`, imported from `@prisma/composer-prisma-cloud`, and build your existing client (`pg`, Bun's `SQL`, whatever you use today) from the injected `db.url` instead of a connection-string env var. See [Databases](https://www.prisma.io/docs/composer/databases). Your build must produce a self-contained entry file, with everything inlined except runtime built-ins. Keep your own build if it already does this. Otherwise, a one-line `bun build --target=bun` produces one. See [Getting started](https://www.prisma.io/docs/composer/getting-started#5-build). ## A Next.js app [#a-nextjs-app] Use the `nextjs` build adapter instead of `node`. With `output: 'standalone'` set, `next build` is the whole build: ```ts title="src/service.ts" import nextjs from '@prisma/composer/nextjs'; import { rpc } from '@prisma/composer/service-rpc'; import { compute } from '@prisma/composer-prisma-cloud'; import { apiContract } from '../api/contract.ts'; export default compute({ name: 'web', deps: { api: rpc(apiContract) }, build: nextjs({ module: import.meta.url, appDir: '..' }), }); ``` ```ts title="next.config.ts" export default { output: 'standalone' }; ``` `prisma.config.ts` imports `prisma/config`, which resolves from your project's `node_modules`, so install the Prisma CLI as a dev dependency if the project does not have it yet: #### bun ```bash bun add --dev prisma ``` #### pnpm ```bash pnpm add -D prisma ``` #### yarn ```bash yarn add --dev prisma ``` #### npm ```bash npm install -D prisma ``` Then add `nextjsBuild()` from `@prisma/composer/nextjs/control` to the `extensions` of the `composer` section in `prisma.config.ts`: ```ts title="prisma.config.ts" import { defineConfig as composer } from '@prisma/composer/config'; import { nextjsBuild } from '@prisma/composer/nextjs/control'; import { nodeBuild } from '@prisma/composer/node/control'; import { prismaCloud, prismaState } from '@prisma/composer-prisma-cloud/control'; import { definePrismaConfig } from 'prisma/config'; export default definePrismaConfig({ composer: composer({ extensions: [prismaCloud(), nodeBuild(), nextjsBuild()], state: prismaState(), }), }); ``` Add `export const dynamic = 'force-dynamic'` to any page or server action that calls `service.load()`. The runtime environment does not exist at build time, and Next.js will not re-read it for prerendered routes. [`examples/storefront-auth`](https://github.com/prisma/composer/tree/main/examples/storefront-auth) in the Composer repository is a complete example of a ported app: a Next.js frontend calling a Bun API service that owns a Postgres. ## More than one service [#more-than-one-service] Port them into one `module.ts` and replace the URLs they used to reach each other with [contracts](https://www.prisma.io/docs/composer/services-and-contracts). The payoff is that the calls between services become typed, and every environment (production, stages, tests) gets the wiring for free. ## Verify the port [#verify-the-port] Build, then run the app locally before you deploy: #### bun ```bash bun run build bunx prisma dev module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma dev module.ts ``` #### yarn ```bash yarn build yarn prisma dev module.ts ``` #### npm ```bash npm run build npx prisma dev module.ts ``` Every service should appear in the startup output with a local URL. If a service fails at boot with a module-resolution error, its build left something un-inlined. Check that the entry file is self-contained. ## Next steps [#next-steps] * [Getting started](https://www.prisma.io/docs/composer/getting-started): the same shape, built from scratch. * [Deploying](https://www.prisma.io/docs/composer/deploying): take the ported app to production. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Service input (/docs/composer/service-input) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Declare a service's configuration and credentials as one schema, bind values at provision time, and read them back typed. Location: Composer > Service input Everything a running service receives arrives through one of two channels, and choosing the channel is most of the decision: | The value is... | Declare it as | Provide it | Read it | | ---------------------------------------------------------- | ------------------------------------------- | ------------------------------------------------------------------- | ----------------- | | Produced by another node (a database, another service) | a dependency: `deps: { db: rawPostgres() }` | wire it at `provision()` | `service.load()` | | Anything else (a region, a flag, a job list, a credential) | one field of the service's `input` schema | bind it at `provision()`: a literal, `envParam()`, or `envSecret()` | `service.input()` | Dependencies are covered in [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts) and [Databases](https://www.prisma.io/docs/composer/databases). This page is the second row. ## Declare the input schema [#declare-the-input-schema] A service declares its whole incoming configuration, plain values and credentials together, as one [Standard Schema](https://standardschema.dev). The schema gives you the TypeScript type and validates the assembled input at deploy and again at boot, so garbage config is a loud failure, not a runtime surprise. A credential is a field typed as the redacting `SecretString` box: ```ts title="src/scheduler/service.ts" import { secretString } from '@prisma/composer/arktype'; import { type } from 'arktype'; const schedulerInput = type({ jobs: type({ jobId: 'string', every: 'string' }).array(), 'region?': 'string', apiKey: secretString(), }); export default compute({ name: 'scheduler', input: schedulerInput /* ... */ }); ``` Because the input is a real schema, legality can be conditional. "No Stripe key unless billing is on" is an ordinary union, not a framework feature, and `service.input()` narrows it like any other TypeScript union: ```ts const chatInput = type({ stripeEnabled: 'false' }).or( type({ stripeEnabled: 'true', stripeSecretKey: secretString() }), ); ``` ## Bind values at provision [#bind-values-at-provision] The service declares what shapes are legal; the place that provisions it decides where each value comes from. The binding is a plain object mirroring the schema's shape, whose leaves are literals, `envParam(...)`, or `envSecret(...)`: ```ts title="module.ts" import { envParam, envSecret } from '@prisma/composer-prisma-cloud'; provision(scheduler, { input: { jobs: [{ jobId: 'tick', every: '60s' }], // a literal region: envParam('REGION'), // a per-stage platform variable apiKey: envSecret('SCHEDULER_API_KEY'), // a credential: name only, never the value }, }); ``` Secretness is enforced by validation, not annotation: binding a plain literal where the schema expects a `SecretString` fails the deploy (a credential almost landed in plain config), and binding `envSecret` where the schema expects a plain string fails the same way. Neither side can silently misclassify a credential. ### `envParam`: plain values the code cannot know [#envparam-plain-values-the-code-cannot-know] An app origin is the canonical case: it differs between production and every preview stage, and a stage's public URL does not exist until that stage first deploys. Each stage keeps its own copy of the variable, so one topology serves them all. How the value travels: **the stage's platform variable is the store; the deploying shell only seeds it.** At deploy, preflight checks the name exists for the target stage, and a name the stage is missing is copied up from the deploying shell's environment. Once the stage has it, your shell no longer matters. To change the value later, set it on the platform and redeploy; a running instance's environment is frozen when the instance is created. `envParam` values arrive as raw strings, so bind them to string fields. ### `envSecret`: credentials [#envsecret-credentials] Seeded exactly the same way, with one rule: **the value never enters framework config**. The framework carries only the variable's name; the platform injects the value straight into the running instance. What your code gets is a `SecretString` box that redacts on logging, rendering, and JSON serialization. Leaking it takes a deliberate `.expose()`: ```ts const input = service.input(); if (input.stripeEnabled) stripe(input.stripeSecretKey.expose()); ``` A reusable Module forwards a secret need to its parent without ever learning the platform name (see [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules#reusable-modules)), which is what lets a Module require credentials without dictating your naming. ## Absence is the schema's call [#absence-is-the-schemas-call] An env-bound field whose variable is unset (or empty) resolves to *key omitted*. Whether that is legal is the schema's call: an optional field, a union arm, or a validation error that fails the deploy. A credential for an off-by-default feature is an ordinary optional `SecretString` field, not a framework flag. Because an omitted key can also be a typo'd variable name, the deploy report prints every key that resolved absent; check it when a value goes missing. ## The reserved port [#the-reserved-port] Every service also gets a `port` (default 3000), outside the input schema and read through its own typed accessor: ```ts Bun.serve({ port: service.port(), hostname: '0.0.0.0', fetch: handler }); ``` `service.port()` is a sibling of `service.origin()`. Never read `process.env.PORT` yourself; the framework exports `PORT` only for a server it does not write and cannot call, Next.js's standalone `server.js`, which binds it itself. ## What travels [#what-travels] The deploy validates the resolved binding, applies the schema's defaults, and serializes the result into one document, with each secret as a pointer naming the platform variable that holds the value, never the value itself: ```json no-copy { "stripeEnabled": true, "stripeSecretKey": { "$secret": "STRIPE_SECRET_KEY" } } ``` At boot the framework swaps each pointer for a redacting box over the named variable, validates against the schema again, and `service.input()` returns the typed object. The document is secret-free by construction, which is why the deploy report can print it verbatim. ## Next steps [#next-steps] * [Deploying](https://www.prisma.io/docs/composer/deploying): how a fresh stage gets its variables in CI. * [Testing](https://www.prisma.io/docs/composer/testing): supply an input double under the reserved `input` key. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Services and contracts (/docs/composer/services-and-contracts) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Define a service's API as a typed contract, serve it, and call it from other services with authentication and retries handled for you. Location: Composer > Services and contracts A **contract** is a service's API written as schemas, and it types both ends of the edge: the producer's handlers and the consumer's client. Define it once, in the package of the service that owns it. For example, an `auth` service that verifies tokens declares one `verify` method: ```ts title="src/auth/contract.ts" import { contract, rpc } from '@prisma/composer/service-rpc'; import { type } from 'arktype'; export const authContract = contract({ verify: rpc({ input: type({ token: 'string' }), output: type({ ok: 'boolean' }) }), }); ``` Any [Standard Schema](https://standardschema.dev) validator types the messages; arktype, zod, and valibot all work. The only contract kind today is RPC over HTTP; there are no gRPC, WebSocket, or streaming contracts yet (see [Limitations](https://www.prisma.io/docs/composer/limitations)). ## Serve a contract [#serve-a-contract] On the producer, declare the contract under `expose` and turn it into an HTTP handler with `serve()`. The handler map must cover every method; a missing or wrong-shaped handler does not compile: ```ts title="src/auth/server.ts" import { serve } from '@prisma/composer/service-rpc'; import service from './service.ts'; const handler = serve(service, { rpc: { verify: async ({ token }) => ({ ok: await check(token) }), }, }); export default handler; Bun.serve({ port: service.port(), hostname: '0.0.0.0', fetch: handler }); ``` Each handler receives the validated input, the service's own loaded deps as a second argument, and an optional third argument carrying the call's [idempotency key](#calls-retry-safely-for-you). Most handlers only use the first. ## Call a contract [#call-a-contract] On the consumer, declare the dependency as `rpc(contract)`. `service.load()` then returns a ready-made typed client, and calling it is an async function call: ```ts title="src/storefront/service.ts" import { rpc } from '@prisma/composer/service-rpc'; export default compute({ name: 'storefront', deps: { auth: rpc(authContract) }, // ... }); ``` ```ts const { auth } = service.load(); const { ok } = await auth.verify({ token }); ``` Note the asymmetry: the producer exposes the bare contract (its offer), while the consumer wraps it in `rpc()` (its need, "a client of this contract"). Inputs and outputs are validated at runtime at the boundary, against the same schemas that typed them. ## Calls are authenticated for you [#calls-are-authenticated-for-you] You do not write anything for this. When you deploy, the framework mints a distinct, unguessable **service key** for each consumer-to-provider edge you declared, gives it to the consumer's client, and tells the provider to accept it. Every call carries the key, and `serve()` answers anything else with `401` before your handler runs. A deployed service has a public URL; the key is what makes it answer only the services your app connected to it. Two rules follow: * **Do not build your own service-to-service auth** on top of this. * **Do not `curl` a deployed `/rpc/` to check it works.** You are not one of the services wired to it, so you always get `401`. That looks like a broken deploy and is not. Debug through a consumer instead. `dev` runs the same pipeline as a deploy, so local runs enforce the keys too: a direct `curl` to a provider's RPC endpoint returns `401` even on `localhost`. Only bare runs and [tests](https://www.prisma.io/docs/composer/testing) skip enforcement, because nothing is provisioned there. The details that matter operationally: | | | | -------- | ------------------------------------------------------------------------------------------------------------------- | | Scope | Service-level: any valid key reaches every method that service exposes. Split into two services to gate separately. | | Per edge | Two consumers of one provider hold different keys, so one leaking cannot impersonate the other. | | Rotation | Remove the dependency (or destroy the stack) and redeploy. A plain redeploy keeps the same keys. | | Storage | `COMPOSER_*` variables the deploy owns and rewrites. Never hand-edit one. | ## Calls retry safely for you [#calls-retry-safely-for-you] You do not write anything for this either. A provider that has scaled to zero has to boot before it answers, and a first call can be dropped while it does. The generated client absorbs that: every call carries an idempotency key, a dropped call is retried with a backoff, and `serve()` runs one call per key. A retry that arrives after the first call already ran gets the first answer back instead of running your handler twice. So `await auth.verify(...)` works across a cold start, whether or not the call changes state. The deduplication rides on the key, so a hand-rolled request without one is served once without deduplication rather than rejected. Do not add an "is this idempotent" flag to a contract; the framework does not have one. If a handler needs exactly-once guarantees beyond one instance's memory (surviving a crash mid-call), its optional third argument carries the key to write into its own transaction: ```ts verify: async (input, deps, ctx) => { // ctx.idempotencyKey: string | undefined (absent for keyless callers) }, ``` ## Next steps [#next-steps] * [Service input](https://www.prisma.io/docs/composer/service-input): the other channel, for configuration and credentials. * [Testing](https://www.prisma.io/docs/composer/testing): fake a contract in unit tests, or boot the real entry against a stand-in. * [Apps and Modules](https://www.prisma.io/docs/composer/apps-and-modules): wire contracts across Module boundaries. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # Testing (/docs/composer/testing) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Test composed services by deciding what service.load() returns, with mockService for unit tests and bootstrapService for integration tests. Location: Composer > Testing Your app's code gets every dependency from one call, `service.load()`, and nothing from arguments or globals. That makes testing a matter of deciding what `load()` returns; the code under test is never modified. There are two tools, and you pick by how much of the real path you want to exercise: | You want to... | Use | From | | ------------------------------------------------------------ | ------------------ | --------------------------------------- | | Call a page, action, or handler directly, dependencies faked | `mockService` | `@prisma/composer/testing` | | Boot the real built entry and drive it over real HTTP | `bootstrapService` | `@prisma/composer-prisma-cloud/testing` | Most tests are the first kind. Reach for the second when the thing you are proving is the wiring itself: that the service boots, reads its config, builds its clients, and answers requests. ## Unit tests: `mockService` [#unit-tests-mockservice] `mockService(service, overrides)` returns a copy of the service whose `load()` yields your fakes and whose `input()` yields the object you pass under the reserved `input` key. Everything goes in one flat object: dependency names route to `load()`, and `input` routes to `input()` (required exactly when the service declares an input schema; the input double is handed over as-is, not validated). The fakes are type-checked against the service's declared dependencies, so a double with the wrong shape does not compile. Substituting the mocked service for the real one is your test runner's job: `vi.mock` in Vitest, `mock.module` in `bun test`. A Vitest example, testing a Next.js page: ```tsx title="app/page.test.tsx" import { renderToString } from 'react-dom/server'; import { mockService } from '@prisma/composer/testing'; import realService from '../src/service.ts'; vi.mock('../src/service.ts', () => ({ default: mockService(realService, { auth: { verify: async () => ({ ok: true }) }, // wrong shape = compile error }), })); import Page from './page.tsx'; it('renders the verified state', async () => { expect(renderToString(await Page())).toContain('Signed in: true'); }); ``` No server, no database, no environment; the page renders against the fake. ## Integration tests: `bootstrapService` [#integration-tests-bootstrapservice] `bootstrapService` boots the service's real built entry in-process, fed the same way a deployed boot is fed, except you choose the values. Point a dependency at a stand-in you run on a loopback port, then make real HTTP requests. Run these under `bun test`: ```ts title="service.integration.test.ts" import { bootstrapService } from '@prisma/composer-prisma-cloud/testing'; import fakeAuth from '@my-app/auth/fake'; // an in-memory handler, no db import storefront from '../src/service.ts'; const fake = Bun.serve({ port: 0, fetch: fakeAuth }); const app = await bootstrapService(storefront, { service: { port: 4310 }, inputs: { auth: { url: fake.url.href } }, }); const res = await app.fetch(new Request(app.url)); expect(await res.text()).toContain('Signed in: true'); ``` Five things to know: * **Build first.** It boots the *built* entry, so the test task must depend on the build. * **Pass a concrete `service.port`.** The entry listens itself; no OS-assigned port is reported back. * **There is no `close()`.** Run each integration-test file in its own process (`bun test` does this per file) and the server dies with it. * **The service's code is untouched.** If you find yourself editing `server.ts` to make it testable, something upstream is wrong. * **You do not need a service key.** Only a deploy (or `dev`) provisions keys; in a test nothing checks, every call reaches your handler, and there is nothing to put in `inputs`. A service with an input schema takes `input` in the config, a binding exactly like `provision()`'s, run through the real serialize and read path, so `input()` in the booted entry sees what a deploy would produce. **Next.js services need a third argument**, a boot function, because the built entry lives inside Next's standalone output. Resolve it with `standaloneServerPath`; `bootstrapService` exports the resolved port as `process.env.PORT` before booting, which is exactly what Next's standalone server binds: ```ts import { pathToFileURL } from 'node:url'; import { standaloneServerPath } from '@prisma/composer/nextjs/control'; await bootstrapService(storefront, config, async () => { await import(pathToFileURL(standaloneServerPath(storefront.build)).href); }); ``` The complete working version is [`examples/storefront-auth`](https://github.com/prisma/composer/tree/main/examples/storefront-auth) in the Composer repository. ## Writing good fakes [#writing-good-fakes] A dependency's type *is* its contract, so anything of that shape is a valid fake, and the compiler holds it to that. In increasing order of realism: * **A bare object**: `{ verify: async () => ({ ok: true }) }`. Right for most unit tests. * **The real client over an in-memory handler**: exercises JSON encoding and schema validation, still no socket. * **A real local server**: the fake served over actual HTTP, which is what `bootstrapService` drives. One habit pays for all of this. Ship each service's fake from its own package as a `/fake` entry point, outside `src/`, so it cannot reach production code. Fake and service then share one contract; when the contract changes, both stop compiling at once, and every consumer's tests find out immediately. ## Next steps [#next-steps] * [Services and contracts](https://www.prisma.io/docs/composer/services-and-contracts): what the contract types on each end. * [Local development](https://www.prisma.io/docs/composer/local-development): run the real app instead of booting one service. ## Related pages - [`Apps and Modules`](https://www.prisma.io/docs/composer/apps-and-modules): How services, resources, and Modules compose into a Prisma App, and how provision() wires them together. - [`Building blocks`](https://www.prisma.io/docs/composer/building-blocks): Compose the ready-made cron, storage, and streams Modules instead of building scheduled jobs, blob storage, or event streams yourself. - [`Core concepts`](https://www.prisma.io/docs/composer/core-concepts): The ideas every Composer declaration and command builds on: services, resources, Modules, ports, contracts, stages, and the deploy model. - [`Databases`](https://www.prisma.io/docs/composer/databases): Give a service a Postgres database, either as a plain connection or typed by a Prisma ORM contract with managed migrations. - [`Deploying`](https://www.prisma.io/docs/composer/deploying): Deploy a Prisma App to production or an isolated stage, run it in CI, and tear environments down safely. # auth (/docs/cli/auth) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. Location: CLI > auth Use `auth` commands to manage authentication for the platform commands. Signing in opens a browser flow and stores a session. Every platform command in that environment reuses that session. ## Usage [#usage] #### bun ```bash bunx prisma auth login bunx prisma auth whoami ``` #### pnpm ```bash pnpm prisma auth login pnpm prisma auth whoami ``` #### yarn ```bash yarn prisma auth login yarn prisma auth whoami ``` #### npm ```bash npx prisma auth login npx prisma auth whoami ``` ## Commands [#commands] | Command | Description | | ------------------------------------ | ----------------------------------------------------- | | `auth login` | Log in to your Prisma platform account (browser flow) | | `auth logout` | Clear stored authentication credentials | | `auth whoami` | Show the authenticated user and accessible workspace | | `auth workspace list` | List your workspace sessions | | `auth workspace use [id-or-name]` | Make one of your workspace sessions current | | `auth workspace logout ` | End one workspace session | `auth login` signs you in through the browser. Afterward, anything running in that environment inherits the session, including coding agents. For CI, set [`PRISMA_SERVICE_TOKEN`](https://www.prisma.io/docs/cli/environment-variables) instead. ## Related pages - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. - [`contract infer`](https://www.prisma.io/docs/cli/contract-infer): Infer a starter contract from an existing database. # branch (/docs/cli/branch) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. List platform branches for a project. Location: CLI > branch Use `branch` commands to inspect the platform branches of a project. The commands do not create anything on the platform. See [Branching](https://www.prisma.io/docs/compute/branching) for how platform branches work. ## Usage [#usage] #### bun ```bash bunx prisma branch list ``` #### pnpm ```bash pnpm prisma branch list ``` #### yarn ```bash yarn prisma branch list ``` #### npm ```bash npx prisma branch list ``` ## Commands [#commands] | Command | Description | | ------------- | --------------------------------------------- | | `branch list` | List platform branches for the linked project | ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. - [`contract infer`](https://www.prisma.io/docs/cli/contract-infer): Infer a starter contract from an existing database. # bucket (/docs/cli/bucket) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Create and manage object-store buckets. Location: CLI > bucket Use `bucket` commands to manage object-store buckets in a project. ## Usage [#usage] #### bun ```bash bunx prisma bucket list bunx prisma bucket create --name my-bucket ``` #### pnpm ```bash pnpm prisma bucket list pnpm prisma bucket create --name my-bucket ``` #### yarn ```bash yarn prisma bucket list yarn prisma bucket create --name my-bucket ``` #### npm ```bash npx prisma bucket list npx prisma bucket create --name my-bucket ``` ## Commands [#commands] | Command | Description | | ---------------------------------------- | ------------------------------------------------------------------------------------------------ | | `bucket list` | List object-store buckets | | `bucket create` | Create an object-store bucket (`--name ` sets the display name, auto-generated if omitted) | | `bucket delete ` | Delete a bucket and all its access keys | | `bucket key list ` | List access keys for a bucket | | `bucket key create ` | Create a bucket access key and print its one-time credentials | | `bucket key delete ` | Revoke and delete a bucket access key | ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. - [`contract infer`](https://www.prisma.io/docs/cli/contract-infer): Infer a starter contract from an existing database. # Configuration (/docs/cli/configuration) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Configure Prisma ORM CLI commands with prisma.config.ts and global flags. Location: CLI > Configuration Prisma ORM CLI commands read `prisma.config.ts`, starting in the directory you run them from. The file has one section per part of the CLI. The `orm` section holds your schema, database, and migration settings, which the `contract`, `db`, and `migration` commands read. The `skills` section holds the settings for the [agent skills commands](https://www.prisma.io/docs/cli/skills), which copy Prisma's instructions for AI coding agents into your project. The `composer` section holds [Prisma Composer](https://www.prisma.io/docs/composer)'s deploy config, the extensions and the deploy-state store that [`deploy`](https://www.prisma.io/docs/cli/deploy) and [`dev`](https://www.prisma.io/docs/cli/dev) read; [Composer getting started](https://www.prisma.io/docs/composer/getting-started) shows how to write it. ## Config file [#config-file] The outer `definePrismaConfig` comes from `prisma/config` and marks the file as a Prisma ORM 8 CLI config. A Prisma ORM 7 `prisma.config.ts` without that marker is rejected rather than misread. The import resolves from your project's `node_modules`, so `prisma` must be a local dependency. The `orm` section uses the config helper for your database. For PostgreSQL: ```typescript title="prisma.config.ts" import "dotenv/config"; import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/contract.prisma", db: { connection: process.env["DATABASE_URL"]!, }, }), }); ``` For MongoDB projects, import the section helper from `@prisma/orm-mongo/config` instead. Both packages export this helper as `defineConfig`, and the examples on this page rename it to `ormConfig` when they import it. This paragraph is only for a config written for a Prisma ORM 8 release candidate before `8.0.0-rc.12`. Such a config may import `defineConfig` from `@prisma/cli-engine`. That import no longer exists, so the config fails to load. Replace it with `definePrismaConfig` from `prisma/config`, and rename the call to match: it is the same function under its new name. Leave the `defineConfig` import from `@prisma/orm-postgres/config` or `@prisma/orm-mongo/config` as it is. That one is the helper for the `orm` section, and its name has not changed. [`orm init`](https://www.prisma.io/docs/cli/orm-init) writes this file for you. Without `--config`, the CLI looks for `prisma.config.ts` in the directory you run the command from and in the directories above it, as [How the CLI finds the config](#how-the-cli-finds-the-config) explains. Pass `--config` to use a config file in another place, such as a subdirectory: #### bun ```bash bunx prisma contract emit --config ./config/prisma.config.ts ``` #### pnpm ```bash pnpm prisma contract emit --config ./config/prisma.config.ts ``` #### yarn ```bash yarn prisma contract emit --config ./config/prisma.config.ts ``` #### npm ```bash npx prisma contract emit --config ./config/prisma.config.ts ``` ## How the CLI finds the config [#how-the-cli-finds-the-config] The CLI starts with `prisma.config.ts` in the current directory, or with the file you pass to `--config`. It then also reads the `prisma.config.ts` in each parent directory that has one, up to the root of the repository, which is the first directory that has a `.git` entry. If neither the starting directory nor any directory above it has a `.git` entry, the CLI reads only the file it started with. The CLI combines the files it finds one section at a time. Inside a section such as `orm`, it takes each top-level key, such as `contract`, `db`, or `migrations`, from the nearest file that sets it, and it takes the whole value of that key from that one file. So if a config higher up sets `db.connection` and your project's config sets only `contract`, the CLI uses your `contract` and the other file's `db`. The `composer` section is not combined this way. The CLI takes it whole from the nearest file that declares a `composer` section and ignores the `composer` sections of files higher up. Extensions are the exception: if your project config has its own `orm` section, it never inherits `extensions` from a config higher up, even when it lists none. List the extensions your contract uses, as [Extension packs](#extension-packs) shows, in each project's own config. > [!WARNING] > More than one project in a repository > > If a `prisma.config.ts` higher up sets `db` or `migrations`, a project config that leaves them out uses that file's database and migrations folder. `db update`, `db migrate` and `db verify` then connect to the other project's database, and `migration plan` writes into the other project's migrations folder, without a warning. Set `db` and `migrations`, such as `migrations: { dir: "./migrations" }`, in every project's own config, or add `parent: false` to it. To stop a project's config from inheriting anything, add `parent: false` to it. The search then ends at that file: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ parent: false, orm: ormConfig({ contract: "./prisma/contract.prisma", }), }); ``` `parent` can also be a path to another config file, which the CLI reads next in place of the one in the parent directory. The path is relative to the directory of the config file that sets it, and it may point outside the repository. After reading that file, the CLI goes on to search the directories above that file in the usual way, unless that file sets `parent` too: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ parent: "../shared/prisma.config.ts", orm: ormConfig({ contract: "./prisma/contract.prisma", }), }); ``` A relative path in a config file, such as `contract`, `output`, or `migrations.dir`, resolves from the directory of the config file that sets it, not from the directory you run the command in. So `--config ./config/prisma.config.ts` with `contract: "./prisma/contract.prisma"` reads `./config/prisma/contract.prisma`. ## Split the schema across several files [#split-the-schema-across-several-files] `contract` accepts a glob, a path pattern where `*` matches any file name and `**` matches any number of folders, so one schema can span several `.prisma` files: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/**/*.prisma", }), }); ``` `contract emit` builds one contract from every matched file whose first line is `// use prisma-8`. The same rule applies when `contract` names a single file: that file must start with the line too. `contract emit` skips a matched file without that line and does not warn, so the line is what makes a file part of the schema. The Prisma editor extension uses the same rule, so the editor and `contract emit` agree on which files make up the schema. If the glob matches files but none of them starts with the line, `contract emit` fails with `CONTRACT.SOURCE_LOAD_FAILED`, and the error's details name the problem `PSL_NO_OPTED_IN_SCHEMA_FILES`. If the glob matches no file at all, for example because of a typo in the pattern, they name it `PSL_NO_SCHEMA_FILES_MATCHED` instead. [What `contract emit` creates](https://www.prisma.io/docs/cli/contract-emit#what-it-creates) says where these details appear in the output. A new file joins the schema the next time you run `contract emit`, with no change to the config. The emitted files go in the folder before the first wildcard, here `./prisma/contract.json` and `./prisma/contract.d.ts`. ## Use a Prisma ORM 7 schema [#use-a-prisma-orm-7-schema] On PostgreSQL, Prisma ORM 8 can read a Prisma ORM 7 `schema.prisma` directly as its contract source, so the two versions can run side by side on the database that Prisma ORM 7 migrates. Wrap the path in `prisma7Schema`: ```typescript title="prisma.config.ts" import "dotenv/config"; import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig, prisma7Schema } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ orm: ormConfig({ contract: prisma7Schema("prisma/schema.prisma"), db: { connection: process.env["DATABASE_URL"]!, }, }), }); ``` `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`](https://www.prisma.io/docs/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`](https://www.prisma.io/docs/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. ## Use a Prisma ORM 6 MongoDB schema [#use-a-prisma-orm-6-mongodb-schema] On MongoDB, Prisma ORM 8 can read a Prisma ORM 6 `schema.prisma` directly as its contract source. Wrap the path in `prisma6Schema`: ```typescript title="prisma.config.ts" import "dotenv/config"; import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig, prisma6Schema } from "@prisma/orm-mongo/config"; export default definePrismaConfig({ orm: ormConfig({ contract: prisma6Schema("prisma/schema.prisma"), db: { connection: process.env["DATABASE_URL"]!, }, }), }); ``` The schema file does not need the `// use prisma-8` first line. Run `npx prisma contract emit` and then [`npx prisma db sign`](https://www.prisma.io/docs/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](https://www.prisma.io/docs/orm/reference/error-reference) lists each code and what to change. ## Write your own contract source [#write-your-own-contract-source] A config that builds `contract.source` itself, as an object with a `load` function, must give the object a `format`: `"psl"` when its inputs are PSL text, and `"typescript"` when it builds the contract in TypeScript. Without it, every command that reads the config fails with `CONFIG.VALIDATION_FAILED`. A path, a glob, `prisma7Schema` and `prisma6Schema` set the format for you. ## Emit-only config [#emit-only-config] `contract emit` does not connect to a database, so the `orm` section can omit `db.connection`: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/contract.prisma", }), }); ``` Add `db.connection` before running commands such as `db verify`, `db sign`, `db init`, `db update`, `db schema`, `contract infer`, or `db migrate`. ## Extension packs [#extension-packs] Add extension control descriptors to the `orm` section when your contract uses extension-provided types: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; import { defineConfig as ormConfig } from "@prisma/orm-postgres/config"; import pgvector from "@prisma/orm-extension-pgvector/control"; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/contract.prisma", extensions: [pgvector], db: { connection: process.env["DATABASE_URL"]!, }, }), }); ``` Re-run `contract emit` after changing extension packs, then update the matching runtime client. ## Agent skills [#agent-skills] The `skills` section controls the [agent skills commands](https://www.prisma.io/docs/cli/skills) and the staleness check: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; export default definePrismaConfig({ skills: { agents: ["claude", "cursor"], check: true, }, }); ``` | Field | What it does | | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `agents` | The agent harnesses `skills sync` writes and `skills list` reports: `claude`, `cursor`, `agents`, `devin`. An empty array records that no agent skills are wanted, and the next `skills sync` removes the copies already on disk. Default: all of them. | | `check` | Set `false` to stop commands reporting out-of-date skills, for everyone working in the project. Default: `true`. | [`init`](https://www.prisma.io/docs/cli/init) scaffolds this section for you. ## Database URLs [#database-urls] Database commands accept `--db `. If you omit it, Prisma ORM uses the database connection from `prisma.config.ts`. #### bun ```bash bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db verify --db "$DATABASE_URL" ``` ## Environment variables [#environment-variables] The variables the CLI reads, such as `PRISMA_DISABLE_TELEMETRY`, `PRISMA_SKILLS_CHECK`, and the `PRISMA_SERVICE_TOKEN` pair for CI, are listed on [Environment variables](https://www.prisma.io/docs/cli/environment-variables). `DATABASE_URL` is not one of them: your config file reads it, as in the example above. ## Output modes [#output-modes] Use the default text output when running commands locally. Use `--json` in CI or automation: #### bun ```bash bunx prisma db verify --db "$DATABASE_URL" --json ``` #### pnpm ```bash pnpm prisma db verify --db "$DATABASE_URL" --json ``` #### yarn ```bash yarn prisma db verify --db "$DATABASE_URL" --json ``` #### npm ```bash npx prisma db verify --db "$DATABASE_URL" --json ``` For an AI agent that reads the output as text rather than parsing JSON, use `--format markdown`; see [Global flags](https://www.prisma.io/docs/cli/global-flags#output-formats). Use `--no-interactive` for scripts that must never pause for user input. Use `--confirm ` to grant a consent prompt non-interactively. For example, [`db update`](https://www.prisma.io/docs/cli/db-update) asks for the database name before a destructive change. ## JSON output [#json-output] In `--json` mode, commands emit newline-delimited JSON events. Progress events have `kind: "step-finished"`. The final event has `kind: "result"` and carries the `envelope` object your script branches on: * `envelope.ok`: `true` or `false`. * `envelope.result`: the command's data, when `ok` is `true`. * `envelope.error.code`: a dotted `NAMESPACE.SUBCODE`, for example `PROJECT.NOT_FOUND` or `SERVICE.PROJECT_SETUP_REQUIRED`. * `envelope.error.summary` and `envelope.error.why`: what failed and why it was rejected. * `envelope.nextActions`: machine-readable follow-up commands, so agents can drive the CLI. Branch on `envelope.error.code`, not the message text: codes are a stable contract, while message wording can change between releases. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. - [`contract infer`](https://www.prisma.io/docs/cli/contract-infer): Infer a starter contract from an existing database. # contract emit (/docs/cli/contract-emit) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Emit Prisma ORM contract artifacts. Location: CLI > contract emit `contract emit` reads your contract source (TypeScript or Prisma schema) and writes the generated artifacts used by the runtime, verification, and migration tooling. The command is offline. It does not need a database connection. A Prisma schema source can be one `.prisma` file or a glob that matches several files; see [Split the schema across several files](https://www.prisma.io/docs/cli/configuration#split-the-schema-across-several-files). `contract emit` reads only the `.prisma` files whose first line is `// use prisma-8`, and skips any other file without a warning. When no file starts with that line, it fails with `CONTRACT.SOURCE_LOAD_FAILED` and reports the problem with the code `PSL_NO_OPTED_IN_SCHEMA_FILES`. ## Usage [#usage] #### bun ```bash bunx prisma contract emit ``` #### pnpm ```bash pnpm prisma contract emit ``` #### yarn ```bash yarn prisma contract emit ``` #### npm ```bash npx prisma contract emit ``` ## Options [#options] | Option | What it does | | --------------------- | --------------------------------------------------------------------- | | `--output-path ` | Writes `contract.json` and `contract.d.ts` into a specific directory. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ## What it creates [#what-it-creates] 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.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.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`](https://www.prisma.io/docs/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 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 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`. ## Run it automatically [#run-it-automatically] In development, a Vite plugin runs `contract emit` for you: once when the dev server starts, and again whenever the contract source or `prisma.config.ts` changes. It needs Vite 7 or 8, and comes with your database package, so there is nothing to install: ```typescript title="vite.config.ts" import { defineConfig } from "vite"; import { prismaVitePlugin } from "@prisma/orm-postgres/vite-plugin-contract-emit"; export default defineConfig({ plugins: [prismaVitePlugin()], }); ``` On MongoDB, import it from `@prisma/orm-mongo/vite-plugin-contract-emit`. The plugin reads `prisma.config.ts` from the Vite root; pass another path as the first argument, `prismaVitePlugin("config/prisma.config.ts")`. A second argument takes `debounceMs` (how long to wait after a change before emitting, 150 by default) and `logLevel` (`"silent"`, `"info"`, or `"debug"`). The dev server prints one line per emit: ```text [prisma-vite-plugin-contract-emit] Emitted contract (storageHash: ab5014a0...) ``` An emit that fails shows in Vite's error overlay. The plugin does nothing in a production build, so builds and CI still run the command themselves. With any other bundler, or for a build, add the command as a `prebuild` script, which npm, pnpm, and yarn run before `build`: ```json title="package.json (excerpt)" { "scripts": { "prebuild": "prisma contract emit", "build": "next build" } } ``` ## Examples [#examples] #### bun ```bash bunx prisma contract emit bunx prisma contract emit --output-path ./generated bunx prisma contract emit --json ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma contract emit --output-path ./generated pnpm prisma contract emit --json ``` #### yarn ```bash yarn prisma contract emit yarn prisma contract emit --output-path ./generated yarn prisma contract emit --json ``` #### npm ```bash npx prisma contract emit npx prisma contract emit --output-path ./generated npx prisma contract emit --json ``` ## Next steps [#next-steps] After emitting, choose the database workflow: * use [`db init`](https://www.prisma.io/docs/cli/db-init) for first-time bootstrap * use [`db update`](https://www.prisma.io/docs/cli/db-update) for direct reconciliation * use [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) for checked-in migrations * use [`db verify`](https://www.prisma.io/docs/cli/db-verify) to check drift ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract infer`](https://www.prisma.io/docs/cli/contract-infer): Infer a starter contract from an existing database. # contract infer (/docs/cli/contract-infer) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Infer a starter contract from an existing database. Location: CLI > contract infer `contract infer` inspects a live database and writes a starter PSL contract. An existing file at the output path is overwritten, with a warning. Use it when you are adding Prisma ORM to an existing database and want an initial contract to review and edit. ## Usage [#usage] #### bun ```bash bunx prisma contract infer [--db "$DATABASE_URL"] ``` #### pnpm ```bash pnpm prisma contract infer [--db "$DATABASE_URL"] ``` #### yarn ```bash yarn prisma contract infer [--db "$DATABASE_URL"] ``` #### npm ```bash npx prisma contract infer [--db "$DATABASE_URL"] ``` ## Options [#options] | Option | What it does | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | `--db ` | Connects to the database. Optional: without it the command uses `db.connection` from `prisma.config.ts`, and fails only when neither is set. | | `--output ` | Writes the inferred PSL contract to a specific path. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ## Examples [#examples] #### bun ```bash bunx prisma contract infer --db "$DATABASE_URL" bunx prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma bunx prisma contract infer --db "$DATABASE_URL" --json ``` #### pnpm ```bash pnpm prisma contract infer --db "$DATABASE_URL" pnpm prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma pnpm prisma contract infer --db "$DATABASE_URL" --json ``` #### yarn ```bash yarn prisma contract infer --db "$DATABASE_URL" yarn prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma yarn prisma contract infer --db "$DATABASE_URL" --json ``` #### npm ```bash npx prisma contract infer --db "$DATABASE_URL" npx prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma npx prisma contract infer --db "$DATABASE_URL" --json ``` ## What to review [#what-to-review] Inference gives you a starting point, not a finished design. Review: * model and field names * relation names * mapped database names * defaults, indexes, and constraints * extension-backed column types The file starts with `// use prisma-8`, because `contract emit` reads only `.prisma` files that start with that line. Each model is named after its table, and gets `@@map` only when the table name differs from the model name: a table `User` becomes `model User`, and a table `user_profile` becomes `model UserProfile` with `@@map("user_profile")`. Every column default is written in a form `contract emit` accepts, so you can emit the file without editing it. [Default values](https://www.prisma.io/docs/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`](https://www.prisma.io/docs/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: #### bun ```bash bunx prisma contract emit bunx prisma db sign --db "$DATABASE_URL" bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma db sign --db "$DATABASE_URL" pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract emit yarn prisma db sign --db "$DATABASE_URL" yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract emit npx prisma db sign --db "$DATABASE_URL" npx prisma db verify --db "$DATABASE_URL" ``` `db sign` is the handoff point where you record that the existing database matches the reviewed contract. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # contract print (/docs/cli/contract-print) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Write the configured contract as a Prisma ORM 8 PSL contract. Location: CLI > contract print `contract print` loads the contract that `contract` names in `prisma.config.ts` and writes it as a Prisma ORM 8 PSL contract. The source can be a Prisma ORM 7 schema, a TypeScript contract, or a PSL contract. Use it to move from a Prisma ORM 7 `schema.prisma`, read through [`prisma7Schema`](https://www.prisma.io/docs/cli/configuration#use-a-prisma-orm-7-schema), to a contract file written for Prisma ORM 8. The command is offline. It does not need a database connection. ## Usage [#usage] #### bun ```bash bunx prisma contract print [--output ] ``` #### pnpm ```bash pnpm prisma contract print [--output ] ``` #### yarn ```bash yarn prisma contract print [--output ] ``` #### npm ```bash npx prisma contract print [--output ] ``` ## Options [#options] | Option | What it does | | ----------------- | --------------------------------------------------------------------------------------- | | `--output ` | Writes the PSL to this file. Without it, the command prints the PSL and writes no file. | | `--format human` | Prints the PSL to standard output, so you can redirect it to a file. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result, with the PSL in `psl.text`. | ## Examples [#examples] #### bun ```bash bunx prisma contract print bunx prisma contract print --output ./src/prisma/contract.prisma bunx prisma contract print --json ``` #### pnpm ```bash pnpm prisma contract print pnpm prisma contract print --output ./src/prisma/contract.prisma pnpm prisma contract print --json ``` #### yarn ```bash yarn prisma contract print yarn prisma contract print --output ./src/prisma/contract.prisma yarn prisma contract print --json ``` #### npm ```bash npx prisma contract print npx prisma contract print --output ./src/prisma/contract.prisma npx prisma contract print --json ``` ## What it writes [#what-it-writes] The file starts with `// use prisma-8`. When you point `contract` at the file and run `contract emit`, you get the same contract as before, with the same hashes, so the database needs no migration and no new `db sign`. When a part of the contract has no PSL form, the command names that part, fails, and writes nothing. It also refuses an `--output` path that is one of the files it reads, `prisma.config.ts`, or a file `contract emit` writes. A warning in the contract source, such as `PSL_DEPRECATED_SCALAR_NAME`, does not stop the command. It is reported as a diagnostic whose `severity` is `warn`. In `--json` output, the warning is an entry of the result's `diagnostics`, with `code` set to `CONTRACT.SOURCE_DIAGNOSTIC` and the warning's own code in `meta.code`. ## Next steps [#next-steps] * Set `contract` in `prisma.config.ts` to the file you wrote, as [Configuration](https://www.prisma.io/docs/cli/configuration) shows. * Run [`contract emit`](https://www.prisma.io/docs/cli/contract-emit) to write `contract.json` and `contract.d.ts` from it. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db init (/docs/cli/db-init) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Initialize a database from the current Prisma ORM contract. Location: CLI > db init `db init` bootstraps a database to match the current emitted contract and signs it. It creates everything the contract declares and the database does not have yet, using additive operations only. Structures already in place and compatible are left alone. A conflict that would need a destructive change stops the run. ## Usage [#usage] #### bun ```bash bunx prisma db init --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db init --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db init --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db init --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--db ` | Connects to the database. | | `--dry-run` | Shows planned operations without applying them. | | `--advance-ref ` | Advances the named [ref](https://www.prisma.io/docs/cli/migration-ref) to the post-command contract hash. Without it, `db init` advances `db` when `--db` is omitted, and advances nothing when `--db` is passed. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ## Behavior [#behavior] `db init` is intended for bootstrap work. It creates missing structures needed by the contract and writes the contract marker after the database matches. ### How the db ref moves [#how-the-db-ref-moves] Run without `--db`, `db init` takes the connection from `db.connection` in `prisma.config.ts` and advances the [ref](https://www.prisma.io/docs/cli/migration-ref) named `db` to the contract it just applied. Pass `--db` and that advancement is suppressed, even when the URL is the same one the config holds. Pass `--advance-ref db` alongside `--db` to get it back. The `db` ref is what [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) uses as its starting point when you do not pass `--from`. Passing `--db` only suppresses the advancement; it never removes a `db` ref that already exists. So a loop that always passes `--db` without `--advance-ref db` leaves the ref wherever it was last set, and the next `migration plan` starts from that stale contract. If the ref was never created, `migration plan` plans from an empty database while `migrations/app/` is empty, and refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` once migrations exist on disk. Run a dry run first when you are not working with a disposable local database: #### bun ```bash bunx prisma db init --db "$DATABASE_URL" --dry-run ``` #### pnpm ```bash pnpm prisma db init --db "$DATABASE_URL" --dry-run ``` #### yarn ```bash yarn prisma db init --db "$DATABASE_URL" --dry-run ``` #### npm ```bash npx prisma db init --db "$DATABASE_URL" --dry-run ``` ## Examples [#examples] #### bun ```bash bunx prisma contract emit bunx prisma db init --db "$DATABASE_URL" --advance-ref db bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma db init --db "$DATABASE_URL" --advance-ref db pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract emit yarn prisma db init --db "$DATABASE_URL" --advance-ref db yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract emit npx prisma db init --db "$DATABASE_URL" --advance-ref db npx prisma db verify --db "$DATABASE_URL" ``` #### bun ```bash bunx prisma db init --db "$DATABASE_URL" --dry-run --json ``` #### pnpm ```bash pnpm prisma db init --db "$DATABASE_URL" --dry-run --json ``` #### yarn ```bash yarn prisma db init --db "$DATABASE_URL" --dry-run --json ``` #### npm ```bash npx prisma db init --db "$DATABASE_URL" --dry-run --json ``` ## When to use db update instead [#when-to-use-db-update-instead] Use [`db update`](https://www.prisma.io/docs/cli/db-update) when the database already exists and you want Prisma ORM to reconcile it with a changed contract. Use [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) when you want a reviewable migration package in version control. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db migrate (/docs/cli/db-migrate) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Apply pending Prisma ORM migrations. Location: CLI > db migrate `db migrate` applies pending on-disk migrations to advance the database. It walks every contract space and applies migrations in canonical order: extensions alphabetically, then the app. It applies only the migrations that exist on disk and never generates new operations. Prisma ORM keeps one migration history for your own models, called the `app` space, in `migrations/app/`, and one for each [extension package](https://www.prisma.io/docs/orm/extensions/using-extensions) that ships its own migrations, such as pgvector. Each of these histories is a [contract space](https://www.prisma.io/docs/orm/migrations/the-migration-graph#terms-used-on-this-page). Use it from a controlled deployment step after reviewing migration packages. ## Usage [#usage] #### bun ```bash bunx prisma db migrate --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db migrate --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db migrate --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db migrate --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--db ` | Connects to the database. | | `--to ` | Applies migrations up to a target contract state. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, [`^`](#contract-references), [`@contract`](#contract-references), [`@db`](#contract-references), or [`@empty`](#contract-references). | | `--advance-ref ` | Advances the named [ref](https://www.prisma.io/docs/cli/migration-ref) to the post-apply marker after success. | | `--show` | Previews the migration route without applying (read-only). | | `--from ` | Use it with `--show`, where it sets the starting state for the preview. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, [`^`](#contract-references), [`@contract`](#contract-references), [`@db`](#contract-references), or [`@empty`](#contract-references). | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ### Contract references [#contract-references] A ref name is a name you gave a contract state with [`migration ref set`](https://www.prisma.io/docs/cli/migration-ref), such as `prod`. `^` is a migration directory name followed by `^`, and it names the contract state before that migration, while the directory name alone names the state after it. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms for every command. `@contract` names the contract in `contract.json`, which `contract emit` writes. The database stores a marker, a row that says which contract state it matches. `@db` names that state, or the empty database when the database has no marker. `@empty` names the empty database, before any migration. ## Recommended flow [#recommended-flow] #### bun ```bash bunx prisma migration status --db "$DATABASE_URL" bunx prisma db migrate --db "$DATABASE_URL" bunx prisma migration status --db "$DATABASE_URL" bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma migration status --db "$DATABASE_URL" pnpm prisma db migrate --db "$DATABASE_URL" pnpm prisma migration status --db "$DATABASE_URL" pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma migration status --db "$DATABASE_URL" yarn prisma db migrate --db "$DATABASE_URL" yarn prisma migration status --db "$DATABASE_URL" yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma migration status --db "$DATABASE_URL" npx prisma db migrate --db "$DATABASE_URL" npx prisma migration status --db "$DATABASE_URL" npx prisma db verify --db "$DATABASE_URL" ``` Run `migration status` before and after applying migrations to see what changed. ## Previewing the route [#previewing-the-route] `--show` prints the route `db migrate` would take without touching the database: #### bun ```bash bunx prisma db migrate --show bunx prisma db migrate --show --from @contract --to production ``` #### pnpm ```bash pnpm prisma db migrate --show pnpm prisma db migrate --show --from @contract --to production ``` #### yarn ```bash yarn prisma db migrate --show yarn prisma db migrate --show --from @contract --to production ``` #### npm ```bash npx prisma db migrate --show npx prisma db migrate --show --from @contract --to production ``` ## Applying to a target [#applying-to-a-target] If your project uses named [refs](https://www.prisma.io/docs/cli/migration-ref), apply up to a target ref: #### bun ```bash bunx prisma db migrate --db "$DATABASE_URL" --to production ``` #### pnpm ```bash pnpm prisma db migrate --db "$DATABASE_URL" --to production ``` #### yarn ```bash yarn prisma db migrate --db "$DATABASE_URL" --to production ``` #### npm ```bash npx prisma db migrate --db "$DATABASE_URL" --to production ``` Manage refs with [`migration ref`](https://www.prisma.io/docs/cli/migration-ref). ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db schema (/docs/cli/db-schema) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Inspect a live database schema. Location: CLI > db schema `db schema` reads the live database schema and prints it as a tree, or as the result document with `--json`. The command is always read-only: it never writes a file and never changes the database. Use it when you need to inspect what Prisma ORM sees in the database before inferring, signing, updating, or debugging drift. ## Usage [#usage] #### bun ```bash bunx prisma db schema --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db schema --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db schema --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db schema --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ----------------- | ------------------------------------------------------ | | `--db ` | Connects to the database. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints machine-readable schema output. | ## Examples [#examples] #### bun ```bash bunx prisma db schema --db "$DATABASE_URL" bunx prisma db schema --db "$DATABASE_URL" --json > schema.json ``` #### pnpm ```bash pnpm prisma db schema --db "$DATABASE_URL" pnpm prisma db schema --db "$DATABASE_URL" --json > schema.json ``` #### yarn ```bash yarn prisma db schema --db "$DATABASE_URL" yarn prisma db schema --db "$DATABASE_URL" --json > schema.json ``` #### npm ```bash npx prisma db schema --db "$DATABASE_URL" npx prisma db schema --db "$DATABASE_URL" --json > schema.json ``` ## Related commands [#related-commands] Use [`contract infer`](https://www.prisma.io/docs/cli/contract-infer) when you want to turn a live schema into a starter PSL contract. Use [`db verify`](https://www.prisma.io/docs/cli/db-verify) when you want to compare the live schema with the emitted contract. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db sign (/docs/cli/db-sign) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Sign a database with the current Prisma ORM contract. Location: CLI > db sign `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](https://www.prisma.io/docs/cli/migration-ref) named `db` of each signed space at it, so the next [`migration plan`](https://www.prisma.io/docs/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 [#usage] #### bun ```bash bunx prisma db sign --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db sign --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db sign --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db sign --db "$DATABASE_URL" ``` ## Options [#options] | Argument or option | What it does | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `[contract]` | Signs against a specific contract state instead of the emitted contract. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, or [`^`](#contract-references). It refuses `@contract`, `@db`, and `@empty` with the error code `MIGRATION.REF_WRONG_GRAMMAR`. To sign against the contract in `contract.json`, leave the argument out. | | `--db ` | Connects to the database. | | `--contract ` | The same value as `[contract]`, passed as an option in place of the argument. | | `--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, with one outcome per contract space. See [JSON output](#json-output). | ### Contract references [#contract-references] A ref name is a name you gave a contract state with [`migration ref set`](https://www.prisma.io/docs/cli/migration-ref), such as `prod`. `^` is a migration directory name followed by `^`, and it names the contract state before that migration, while the directory name alone names the state after it. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms for every command. ## Exit codes [#exit-codes] | Code | Meaning | | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `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 [#example] #### bun ```bash bunx prisma contract emit bunx prisma db sign --db "$DATABASE_URL" bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma db sign --db "$DATABASE_URL" pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract emit yarn prisma db sign --db "$DATABASE_URL" yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract emit npx prisma db sign --db "$DATABASE_URL" npx prisma db verify --db "$DATABASE_URL" ``` ## Adopting an existing database [#adopting-an-existing-database] After [`contract infer`](https://www.prisma.io/docs/cli/contract-infer) and `contract emit`, one `db sign` is enough to hand the database over to Prisma ORM: #### bun ```bash bunx prisma db sign --db "$DATABASE_URL" bunx prisma migration plan --name add-users-bio ``` #### pnpm ```bash pnpm prisma db sign --db "$DATABASE_URL" pnpm prisma migration plan --name add-users-bio ``` #### yarn ```bash yarn prisma db sign --db "$DATABASE_URL" yarn prisma migration plan --name add-users-bio ``` #### npm ```bash npx prisma db sign --db "$DATABASE_URL" npx prisma migration plan --name add-users-bio ``` The plan starts from the signed contract, so it contains only the change you made after signing. Because `migrations/app/` is still empty, that first plan also writes a baseline package that records the schema you adopted; see [the automatic baseline](https://www.prisma.io/docs/cli/migration-plan#the-automatic-baseline). ## When to use it [#when-to-use-it] Use `db sign` only after you believe the live database already matches the emitted contract. It is common after: * `contract infer` for an existing database * a manually reviewed migration flow * a database restore that you need to mark as matching the current contract Do not use `db sign` to hide drift. If verification fails, `db sign` writes no signature and offers two ways out. One changes the database: run `prisma db update` to make the database match the contract, then sign again. The other leaves the database as it is: change the contract source to describe the database, run `contract emit` again, then sign again. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db update (/docs/cli/db-update) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Update a database to match the current Prisma ORM contract. Location: CLI > db update `db update` compares the live database with the emitted contract and applies the changes that close the gap, whether or not the database was bootstrapped with `db init`. Use it for direct reconciliation when you do not need a checked-in migration package. ## Usage [#usage] #### bun ```bash bunx prisma db update --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db update --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db update --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db update --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--db ` | Connects to the database. | | `--dry-run` | Shows planned operations without applying them. | | `--to ` | Updates to a specific contract state. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, or [`^`](#contract-references). It refuses `@contract`, `@db`, and `@empty` with the error code `MIGRATION.REF_WRONG_GRAMMAR`. To update to the contract in `contract.json`, leave out `--to`. | | `--advance-ref ` | Advances the named [ref](https://www.prisma.io/docs/cli/migration-ref) to the post-command contract hash. Without it, `db update` advances `db` when `--db` is omitted, and advances nothing when `--db` is passed. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ### Contract references [#contract-references] A ref name is a name you gave a contract state with [`migration ref set`](https://www.prisma.io/docs/cli/migration-ref), such as `prod`. `^` is a migration directory name followed by `^`, and it names the contract state before that migration, while the directory name alone names the state after it. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms for every command. ## How the db ref moves [#how-the-db-ref-moves] Run without `--db`, `db update` takes the connection from `db.connection` in `prisma.config.ts` and advances the [ref](https://www.prisma.io/docs/cli/migration-ref) named `db` to the contract it just applied. Pass `--db` and that advancement is suppressed, even when the URL is the same one the config holds. Pass `--advance-ref db` alongside `--db` to get it back. The `db` ref is what [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) uses as its starting point when you do not pass `--from`. Passing `--db` only suppresses the advancement; it never removes a `db` ref that already exists. So a loop that always passes `--db` without `--advance-ref db` leaves the ref wherever it was last set, and the next `migration plan` starts from that stale contract. If the ref was never created, `migration plan` plans from an empty database while `migrations/app/` is empty, and refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` once migrations exist on disk. ## Destructive changes need consent [#destructive-changes-need-consent] An operation that would destroy data is applied only with your consent: the command asks you to type the database name. In a CI job or a run with `--no-interactive`, where the command cannot ask, pass the consent as `--confirm ` instead: #### bun ```bash bunx prisma db update --db "$DATABASE_URL" --no-interactive --confirm appdb ``` #### pnpm ```bash pnpm prisma db update --db "$DATABASE_URL" --no-interactive --confirm appdb ``` #### yarn ```bash yarn prisma db update --db "$DATABASE_URL" --no-interactive --confirm appdb ``` #### npm ```bash npx prisma db update --db "$DATABASE_URL" --no-interactive --confirm appdb ``` ## Recommended flow [#recommended-flow] #### bun ```bash bunx prisma contract emit bunx prisma db update --db "$DATABASE_URL" --dry-run bunx prisma db update --db "$DATABASE_URL" --advance-ref db bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma db update --db "$DATABASE_URL" --dry-run pnpm prisma db update --db "$DATABASE_URL" --advance-ref db pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract emit yarn prisma db update --db "$DATABASE_URL" --dry-run yarn prisma db update --db "$DATABASE_URL" --advance-ref db yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract emit npx prisma db update --db "$DATABASE_URL" --dry-run npx prisma db update --db "$DATABASE_URL" --advance-ref db npx prisma db verify --db "$DATABASE_URL" ``` Use `--dry-run` before applying changes in shared environments. ## When to use migrations instead [#when-to-use-migrations-instead] For reviewable database changes in version control, use [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) and [`db migrate`](https://www.prisma.io/docs/cli/db-migrate). Use `db update` for local development, preview environments, and workflows where direct reconciliation is acceptable. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # db verify (/docs/cli/db-verify) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Verify a database against the current Prisma ORM contract. Location: CLI > db verify `db verify` checks whether the database marker and live schema match your emitted contract. It verifies the marker first, then checks the schema. Use it in CI and deployment checks before application code that depends on a contract reaches users. ## Usage [#usage] #### bun ```bash bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db verify --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ----------------- | --------------------------------------------------------------------------- | | `--db ` | Connects to the database. | | `--marker-only` | Checks only the database marker. | | `--schema-only` | Checks only whether the live schema satisfies the contract. | | `--strict` | Fails if the database includes schema elements not present in the contract. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints machine-readable output. | ## Exit codes [#exit-codes] | Code | Meaning | | ---- | ---------------------------------------------------------------------------------------------- | | `0` | The database matches the contract. | | `2` | The check could not run: conflicting mode flags, no emitted contract, or unreachable database. | | `4` | Drift or a marker finding. | ## Examples [#examples] #### bun ```bash bunx prisma db verify --db "$DATABASE_URL" bunx prisma db verify --db "$DATABASE_URL" --strict bunx prisma db verify --db "$DATABASE_URL" --schema-only bunx prisma db verify --db "$DATABASE_URL" --marker-only ``` #### pnpm ```bash pnpm prisma db verify --db "$DATABASE_URL" pnpm prisma db verify --db "$DATABASE_URL" --strict pnpm prisma db verify --db "$DATABASE_URL" --schema-only pnpm prisma db verify --db "$DATABASE_URL" --marker-only ``` #### yarn ```bash yarn prisma db verify --db "$DATABASE_URL" yarn prisma db verify --db "$DATABASE_URL" --strict yarn prisma db verify --db "$DATABASE_URL" --schema-only yarn prisma db verify --db "$DATABASE_URL" --marker-only ``` #### npm ```bash npx prisma db verify --db "$DATABASE_URL" npx prisma db verify --db "$DATABASE_URL" --strict npx prisma db verify --db "$DATABASE_URL" --schema-only npx prisma db verify --db "$DATABASE_URL" --marker-only ``` Use JSON output in automation: #### bun ```bash bunx prisma db verify --db "$DATABASE_URL" --json ``` #### pnpm ```bash pnpm prisma db verify --db "$DATABASE_URL" --json ``` #### yarn ```bash yarn prisma db verify --db "$DATABASE_URL" --json ``` #### npm ```bash npx prisma db verify --db "$DATABASE_URL" --json ``` ## What failures mean [#what-failures-mean] | Failure | Meaning | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 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. 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. ## How PostgreSQL defaults are compared [#how-postgresql-defaults-are-compared] When a column default is a literal value, `db verify` reads it as a value before it compares it with your contract, so the casts PostgreSQL adds when it prints the default do not cause a difference. This covers numbers such as `'-1'::integer` and `(5)::smallint`, text and enum values, including an enum value cast to a type in another PostgreSQL schema, `timestamp` values, `true`, `false`, `NULL`, JSON, and lists written as `'{...}'` or `ARRAY[...]`. `db verify` also recognizes `now()`, `clock_timestamp()`, `gen_random_uuid()`, and a sequence default, which it treats as `autoincrement()`. Any other default is a SQL expression, which you write in your contract as a `sql` default, such as ``@default(sql`(now() + '00:03:00'::interval)`)``. [Default values](https://www.prisma.io/docs/orm/contract-authoring/psl-syntax#default-values) shows how to write one. `db verify` cannot work out whether two SQL expressions give the same value, so it compares their text, ignoring letter case and spaces. PostgreSQL often rewrites an expression when it stores it, for example by adding casts, so write a `sql` default in your contract the way PostgreSQL stores it. To see that form, run `npx prisma contract infer --output ./inferred.prisma` and copy the column's `@default` from that file. Check constraints and the `WHERE` clause of a partial index are compared more strictly than `sql` defaults: their text must match exactly, including letter case and spaces. If `db verify` reports a difference in a `timestamptz` value inside a check constraint or a partial index's `WHERE` clause, check whether your contract was inferred before `8.0.0-rc.12` from a server that was not set to UTC. `db verify` reads the database with the time zone set to UTC and dates in ISO format, whatever the server or your database role sets, so a value that your contract has in another time zone no longer matches as text. To fix it, copy the new text from a fresh `npx prisma contract infer --output ./inferred.prisma` into your contract, emit the contract, and run `db sign`. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # deploy (/docs/cli/deploy) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Deploy a Composer application to Prisma Compute, to production or an isolated stage. Location: CLI > deploy `deploy` deploys a [Prisma Composer](https://www.prisma.io/docs/composer) application to [Prisma Compute](https://www.prisma.io/docs/compute). It takes an `` argument: the module whose default export is the application root, typically `module.ts`. The command streams the deploy pipeline's own output to the terminal. `deploy` and [`dev`](https://www.prisma.io/docs/cli/dev) are root-level commands of the unified Prisma CLI. There is no `composer` command group. By default, the command uses the credentials from your existing `auth login` session. In CI or other headless environments, set `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` instead. See [Deploying](https://www.prisma.io/docs/composer/deploying#credentials) for details. The first deploy of an app creates its project in your workspace, and a new project needs a region. Set it once, either as `prismaCloud({ region: 'us-east-1' })` in your deploy config or as the `PRISMA_REGION` environment variable; without one, the deploy stops with `project "" does not exist yet and no deploy region is configured`. A project that already exists keeps its region and needs neither. The region ids are listed under [Limitations](https://www.prisma.io/docs/compute/limitations). The `deploy` command does not build your application. Run your build command before deploying. ## Usage [#usage] #### bun ```bash bunx prisma deploy module.ts bunx prisma deploy module.ts --stage feat-auth ``` #### pnpm ```bash pnpm prisma deploy module.ts pnpm prisma deploy module.ts --stage feat-auth ``` #### yarn ```bash yarn prisma deploy module.ts yarn prisma deploy module.ts --stage feat-auth ``` #### npm ```bash npx prisma deploy module.ts npx prisma deploy module.ts --stage feat-auth ``` ## Flags [#flags] | Flag | Description | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--name ` | Override the application name for this deploy. It defaults to the name of the exported application, and that name selects the project in your workspace: a project this module deployed before is reused, and a same-name project whose hosted state cannot be verified stops the deploy with `HostedStateBootstrapError`, so pass `--name` when the default would land in a project you did not mean to deploy into | | `--stage ` | Deploy scope to target; omit for production | | `--report ` | Write the deploy's outcome as JSON to this path: resources, preview URLs, and the failure cause. Also settable as `PRISMA_COMPOSER_REPORT_FILE` | | `--build-id ` | Join the deploy record your CI already created rather than letting the target create one | ## Global flags [#global-flags] The Prisma CLI's global flags also apply: `--format`, `--json`, `--log-level`, `--verbose`, `--quiet`, `--yes`, `--confirm`, `--interactive`, `--color`, and `--config`. ## Environment variables [#environment-variables] | Variable | Description | | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PRISMA_SERVICE_TOKEN` | A workspace service token from the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=cli), for CI and other headless environments. When unset, the command uses your stored `auth login` session | | `PRISMA_WORKSPACE_ID` | The workspace ID from the workspace's settings; pair it with the service token | | `PRISMA_REGION` | The region for a project this deploy creates; `prismaCloud({ region })` in the deploy config wins when both are set. Ignored once the project exists | | `PRISMA_COMPOSER_REPORT_FILE` | Path to write the deploy's JSON outcome report; the `--report` flag wins when both are set | ## Tearing down and log tailing [#tearing-down-and-log-tailing] The unified CLI has no `destroy` or `log` command. Both operations are available in-process through the control API below: `destroy` takes an explicit target (`{ kind: 'production' }` or `{ kind: 'stage', stage }`), and `log` tails a locally running application. ## The control API [#the-control-api] Everything the CLI does is also callable in-process, from `@prisma/composer/control`: typed `deploy`, `destroy`, `dev`, and `log` operations that return structured results instead of printing and exiting: ```ts import { deploy } from '@prisma/composer/control'; const result = await deploy({ entry: 'module.ts', stage: 'pr-42' }); if (!result.ok) console.error(result.failure.message); ``` Operations return `{ ok: true, value }` or `{ ok: false, failure }`. Failures come back as structured errors with a dotted `failure.code` and the same fix-naming `message` the CLI renders. The deploy engine's live output still streams to your process's stdio; the operations do not capture it. The operations authenticate with `PRISMA_SERVICE_TOKEN` and `PRISMA_WORKSPACE_ID` only. They do not read the session stored by `auth login`, so a script that runs fine next to the CLI's `deploy` fails under the control API with `environment variable PRISMA_WORKSPACE_ID is required` until both variables are exported. ## Next steps [#next-steps] * [`dev`](https://www.prisma.io/docs/cli/dev): run the same application locally, with no credentials. * [Getting started](https://www.prisma.io/docs/composer/getting-started): the commands in a working flow. * [Deploying](https://www.prisma.io/docs/composer/deploying): stages, CI, and what a deploy prints. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # dev (/docs/cli/dev) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Bring a Composer application up entirely on this machine, with no cloud credentials. Location: CLI > dev `dev` brings a [Prisma Composer](https://www.prisma.io/docs/composer) application up entirely on this machine. It takes an `` argument: the module whose default export is the application root, typically `module.ts`. The command runs credential-free against local emulators standing in for Prisma Compute and Prisma Postgres, watches the built output, and restarts a service when its build changes. See [Local development](https://www.prisma.io/docs/composer/local-development). `dev` and [`deploy`](https://www.prisma.io/docs/cli/deploy) are root-level commands of the unified Prisma CLI. There is no `composer` command group. Like `deploy`, `dev` runs your *built* output, so build first. ## Usage [#usage] #### bun ```bash bunx prisma dev module.ts bunx prisma dev module.ts --fresh ``` #### pnpm ```bash pnpm prisma dev module.ts pnpm prisma dev module.ts --fresh ``` #### yarn ```bash yarn prisma dev module.ts yarn prisma dev module.ts --fresh ``` #### npm ```bash npx prisma dev module.ts npx prisma dev module.ts --fresh ``` ## Flags [#flags] | Flag | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--name ` | Override the application name for the local dev instance | | `--fresh` | Destroy the dev stack and wipe the dev state directory before starting. In this release the flag fails for apps with a database; see [Local development](https://www.prisma.io/docs/composer/local-development#whats-local-vs-whats-real) for the manual reset | `dev` needs no credentials or environment variables; local runs never talk to the platform. To tail the running application's merged logs, use the `log` operation of the [control API](https://www.prisma.io/docs/cli/deploy#the-control-api); the unified CLI has no `log` command. ## Next steps [#next-steps] * [Local development](https://www.prisma.io/docs/composer/local-development): warm restarts, `--fresh`, and what persists. * [`deploy`](https://www.prisma.io/docs/cli/deploy): the same application, on Prisma Compute. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # Environment variables (/docs/cli/environment-variables) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. The environment variables the Prisma CLI reads, what each one does, and the release that added it. Location: CLI > Environment variables The `prisma` command reads these environment variables. Set them in your shell profile or in your CI configuration. For one command, put the variable in front of it, as in this [`contract emit`](https://www.prisma.io/docs/cli/contract-emit) run: ```bash title="Terminal" PRISMA_DISABLE_TELEMETRY=1 npx prisma contract emit ``` In PowerShell, set it for the session: ```powershell title="PowerShell" $env:PRISMA_DISABLE_TELEMETRY = "1" npx prisma contract emit ``` The CLI does not read `.env` files or `DATABASE_URL` itself. Your `prisma.config.ts` does that, and the one [`orm init`](https://www.prisma.io/docs/cli/orm-init) writes already loads `.env`; see [Configuration](https://www.prisma.io/docs/cli/configuration#config-file). > [!NOTE] > If you used the > > `prisma-next` > > preview command > > The `prisma` command ignores every `PRISMA_NEXT_*` variable, including `PRISMA_NEXT_DISABLE_TELEMETRY`. Drop the `NEXT_` part in shell profiles and CI: `PRISMA_NEXT_DISABLE_TELEMETRY` becomes `PRISMA_DISABLE_TELEMETRY`. > > Your `prisma-next` telemetry opt-out does not carry over either, so the one-time telemetry notice prints again. Run `prisma telemetry disable` to opt out. The `prisma` command stores that choice in `~/.config/prisma/config.json` (Windows: `%APPDATA%\prisma\config.json`). "Since" is the first `prisma` release that reads the variable. | Variable | What it does | Since | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------- | | `PRISMA_DISABLE_TELEMETRY` | Set to `1` to turn off anonymous [CLI telemetry](https://www.prisma.io/docs/cli/telemetry). Empty, `0`, and `false` leave it on. | 8.0.0-rc.1 | | `DO_NOT_TRACK` | Set to `1` to turn off telemetry. Other values do nothing. | 8.0.0-rc.1 | | `PRISMA_DEBUG` | Set to `1` to print the CLI's debug output to stderr. | 8.0.0-rc.1 | | `PRISMA_SKILLS_CHECK` | Set to `0` to silence the notice that your project's [agent skills](https://www.prisma.io/docs/cli/skills) are out of date. | 8.0.0-rc.9 | | `PRISMA_SERVICE_TOKEN` | A service token for a Prisma Platform workspace, created in the [Prisma Console](https://console.prisma.io/?utm_source=docs\&utm_medium=content\&utm_content=cli). Only the [Platform commands](https://www.prisma.io/docs/cli#platform-commands) such as `auth`, `project`, and `deploy` use it, in place of a browser login, even if you are also logged in. | 8.0.0-rc.1 | | `PRISMA_WORKSPACE_ID` | The ID of the workspace the service token belongs to, from the workspace's settings in the Prisma Console. Set it alongside the token. | 8.0.0-rc.1 | | `NO_COLOR` | Set to any value to turn off colored output. The [`--color` and `--no-color` flags](https://www.prisma.io/docs/cli/global-flags) override it. | 8.0.0-rc.1 | | `CI` | Set to any value except `false` to mark the run as CI: no telemetry, no skills notice, and no interactive prompts. `CI=false` forces the non-CI behavior even when a CI system set it. | 8.0.0-rc.1 | Agent skills are files in your project, in `.claude/skills` and similar folders, that teach AI coding agents about Prisma. Silencing the notice does not update them; `prisma skills sync` does. You rarely need to set `CI` yourself: the CLI also detects the variables that CI providers such as GitHub Actions and GitLab CI set. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # Error reference (/docs/cli/error-reference) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. The structured error codes of the Prisma CLI platform commands, by namespace, with the condition that raises each one. Location: CLI > Error reference Every user-facing error the unified Prisma CLI emits is a structured envelope identified by a dotted `NAMESPACE.SUBCODE` code (see [Error Conventions](https://github.com/prisma/prisma-cli/blob/main/docs/product/error-conventions.md) and [ADR 0003](https://github.com/prisma/prisma-cli/blob/main/docs/architecture/adrs/0003-structured-output-and-errors.md)). This page lists the codes from every `prisma` command except the Prisma ORM ones. The codes from `prisma contract`, `prisma db`, `prisma migration`, and `prisma orm`, and the codes your app can raise at runtime, are on the [Prisma ORM error reference](https://www.prisma.io/docs/orm/reference/error-reference). When an error prints a `docsUrl`, that link opens the code's entry on this page. This page is generated from the `prisma/prisma-cli` repository. One exception: the `CLI.*` codes on this page come from the command-line layer that every `prisma` command runs through, for its arguments, for loading `prisma.config.ts`, for prompts, and for consent. They are on this page even when you ran a Prisma ORM command. `CLI.CONFIG_SECTION_INVALID` from `prisma contract emit` is one of them. The Prisma ORM commands also raise `CLI.*` codes of their own, and those are on the [Prisma ORM error reference](https://www.prisma.io/docs/orm/reference/error-reference). If a `CLI.*` code is not on this page, it is on the other. Recognize an error programmatically by running with `--json` and matching on `error.code` in the emitted envelope — never on message text. Envelopes carry `message`, and optionally `why`, `fix`, `where`, `meta`, `cause`, `nextActions`, and `docsUrl`. Most codes on this page are expected failures. Some are warn-severity diagnostics that ride a successful run: the command completes and exits `0`, and the diagnostic carries the code (the entry says so where it applies). The process exit-code contract is in [Error Conventions](https://github.com/prisma/prisma-cli/blob/main/docs/product/error-conventions.md). Every code on this page is assigned where the error is raised. No boundary invents a code from a server response or rewrites one on the way out, so an error's code never depends on which command you reached the failure through. When a REST API request fails, the CLI raises the domain's registered `*.API_ERROR` code and carries the API's own code and HTTP status in `meta.apiCode` and `meta.status`, where they are data you can read rather than a code you have to guess at. Namespaces on this page: * [`AUTH`](#auth): Workspace authentication and sessions (`prisma auth`, credential resolution) * [`BRANCH`](#branch): Branch listing (`prisma branch`) * [`BUCKET`](#bucket): Bucket and bucket-key management (`prisma bucket`) * [`CLI`](#cli): Engine-level invocation: arguments, config loading, prompts, consent, credentials, child processes * [`FEEDBACK`](#feedback): Sending product feedback (`prisma feedback`) * [`GIT`](#git): Git repository connections (`prisma git`) * [`INIT`](#init): Project initialization diagnostics (`prisma init`) * [`POSTGRES`](#postgres): Database management (`prisma postgres`) * [`PROJECT`](#project): Project and environment management (`prisma project`) * [`SERVICE`](#service): Deployed service management (`prisma service`) * [`SKILLS`](#skills): Agent-skill delivery (`prisma skills`) ## AUTH [#auth] ### AUTH.CREDENTIAL_WORKSPACE_MISMATCH [#AUTH.CREDENTIAL_WORKSPACE_MISMATCH] A credential's `workspace_id` claim disagrees with the workspace it is being stored under — raised by every CredentialManager (the CLI's file-backed manager and the engine's in-memory one) both when `createSession` receives a token claiming a different workspace and when a rotated token written back during refresh would re-scope an existing session. The fix is to run `prisma auth login` again and pick the intended workspace. Meta: none. ### AUTH.LOGIN_WORKSPACE_UNKNOWN [#AUTH.LOGIN_WORKSPACE_UNKNOWN] `prisma auth login` completed the browser sign-in but the minted credential carries no `workspace_id` claim, so no workspace session can be keyed by it. The fix is to sign in again and pick a workspace in the browser. Meta: none. ### AUTH.LOGIN_DENIED [#AUTH.LOGIN_DENIED] The OAuth callback reported `access_denied`. Authorization was not granted; this is an expected refusal, not a CLI crash. No session is created or cleared. Run `prisma auth login` again only to grant access intentionally. The callback description is not echoed. Meta: none. ### AUTH.NO_SESSION_FOR_WORKSPACE [#AUTH.NO_SESSION_FOR_WORKSPACE] A workspace reference matched none of the stored workspace sessions — raised by the command-side ref resolver behind `prisma auth workspace use` and `prisma auth workspace logout` (exact id match first, then case-insensitive name match), and by the credential managers when a session operation names a workspace with no stored record. Sessions are created only by `prisma auth login`, so the suggested fix is to sign in and pick that workspace in the browser; the workspace reference appears in the message, not in meta. Meta: none. ### AUTH.NO_WORKSPACE_SESSIONS [#AUTH.NO_WORKSPACE_SESSIONS] `prisma auth workspace use` was run with zero stored workspace sessions, so there is nothing to select among — the command only selects, it never creates a session or opens a browser. The fix is to run `prisma auth login` first. Meta: none. ### AUTH.SERVICE_TOKEN_EMPTY [#AUTH.SERVICE_TOKEN_EMPTY] The `PRISMA_SERVICE_TOKEN` environment variable is set but blank; a blank token authenticates nothing while still overriding stored workspace sessions, so the CLI surfaces it instead of silently ignoring it. It is raised identically wherever the environment credential is read — `activeCredential()`, the command needs check, and the engine's request path, including at the start of `prisma auth login` before a browser opens. The suggested actions are to unset the variable or set it to a valid service token. Meta: none. ### AUTH.SERVICE_TOKEN_REJECTED [#AUTH.SERVICE_TOKEN_REJECTED] The management API rejected (401) the service token supplied through `PRISMA_SERVICE_TOKEN`; such a token carries no refresh token and can never be renewed, and nothing stored is cleared. Built only through the shared `credentialRejectedError` dispatcher in the engine's API request path — the one place wording differs by credential origin (a stored session with the same failure gets `CLI.CREDENTIALS_REQUIRED` instead). `prisma auth whoami` fails with this code too rather than reporting the rejected token as signed in. The suggested action is to replace the variable with a valid service token or unset it to fall back to stored sessions. Meta: none. ### AUTH.SESSIONS_UNSUPPORTED [#AUTH.SESSIONS_UNSUPPORTED] A session mutation (`createSession`, `selectSession`, `endSession`, `endAllSessions`) was attempted on a host that uses the environment-only credential manager, whose sole credential source is `PRISMA_SERVICE_TOKEN` (plus `PRISMA_WORKSPACE_ID`) — such hosts, like composer's rebuilt CLI, hold no stored sessions, so there is nothing to create, select, or end. The suggested action is to set or change the environment variable instead. Meta: none. ### AUTH.USAGE_ERROR [#AUTH.USAGE_ERROR] A command that needs an active workspace found an authenticated credential that names no workspace — raised by the resource commands' `resolveActiveWorkspace` and the service commands' `requireWorkspace`, which read the engine's pinned credential; an environment token whose claims carry no workspace id is the usual cause. The suggested fix is to run `prisma auth login` and choose a workspace. Meta: none. ### AUTH.WORKSPACE_AMBIGUOUS [#AUTH.WORKSPACE_AMBIGUOUS] A user-typed workspace name matched more than one workspace, from two raise sites with different meta: the session-ref resolver behind `prisma auth workspace use`/`logout` when several stored sessions share the name (meta carries `workspaceIds` and `sessions`, each session holding `workspaceId` and a nullable `user`), and `prisma project transfer` when a `--to-workspace` reference matches several authenticated workspaces (meta carries `workspaceRef` and `matches`, each match holding `id`, `name`, `credentialWorkspaceId`). Both point the user at `prisma auth workspace list` to retry with an exact workspace id. Meta: `workspaceIds`, `sessions` (workspace commands) or `workspaceRef`, `matches` (project transfer). ### AUTH.WORKSPACE_NOT_AUTHENTICATED [#AUTH.WORKSPACE_NOT_AUTHENTICATED] `prisma project transfer` could not resolve the transfer recipient: the `--to-workspace` reference matched no stored OAuth session, or the matched recipient session proved invalid. The suggested fix is to run `prisma auth login` and authorize that workspace, after checking `prisma auth workspace list`. Meta: `workspaceRef`. ## BRANCH [#branch] ### BRANCH.API_ERROR [#BRANCH.API_ERROR] A REST API call made by `prisma branch list` failed. The `why` carries the API's message or, failing that, the HTTP status, and the fix suggests rerunning with `--log-level verbose` for the response details. Meta: `status`, `apiCode` (the API's own error code, when the response supplied one). ## BUCKET [#bucket] ### BUCKET.API_ERROR [#BUCKET.API_ERROR] A bucket REST API request (listing, creating, or deleting buckets or bucket keys) failed — raised by the provider (`lib/bucket/provider.ts`). The `why` carries the API's message or HTTP status, and the fix is the API's hint when it sent one. An HTTP 401 or 403 raises this code too, with a `why` saying the API rejected the request as unauthorized and a `prisma auth login` next action. Meta: `status`, `apiCode` (the API's own error code, when the response supplied one). ### BUCKET.KEY_SECRET_MISSING [#BUCKET.KEY_SECRET_MISSING] `prisma bucket key create` created the key, but the REST API response omitted part of the one-time credential payload (secret access key, access key id, endpoint, or bucket name), so the CLI cannot show credentials it will never see again. The fix is to create another key and store the returned credentials immediately. Meta: none. ### BUCKET.USAGE_ERROR [#BUCKET.USAGE_ERROR] A bucket subcommand was called without its required id argument: `bucket delete` and `bucket key create`/`bucket key list` need a bucket id, and `bucket key delete` needs both a bucket id and a key id. The nextActions point at `bucket list` (or `bucket key list`) to find the ids. Meta: none. ## CLI [#cli] ### CLI.ABORTED [#CLI.ABORTED] The run's abort signal fired before the command completed — a thrown abort error is recognized in settlement and reported as this code rather than as a bug. When the abort came from a delivered SIGINT/SIGTERM the run exits 130/143; an abort with no recorded signal (an engine-internal abort) exits 3. Meta: none. ### CLI.AUTH_SERVICE_ERROR [#CLI.AUTH_SERVICE_ERROR] The authentication service failed transiently while refreshing a stored OAuth session; the stored credentials are left untouched, and the guidance is to retry rather than sign in again, because the credentials themselves were not rejected. `prisma auth whoami` does not fail with this code: it answers from the credential's own claims and reports `verified: false`. Meta: none. ### CLI.BROWSER_WAIT_TIMEOUT [#CLI.BROWSER_WAIT_TIMEOUT] A `ctx.prompt.browserWait` flow (the command opened a URL and polled for the user to finish there) reached its timeout before the poll succeeded. `prisma git connect` catches this code from its GitHub-app install wait and rethrows a command-specific error, so consumers usually see it from other browserWait flows. Meta: `url`, `timeoutMs`. ### CLI.CHILD_PROCESS_FAILED [#CLI.CHILD_PROCESS_FAILED] Emitted only as a json-mode error envelope when a command that handed the terminal to a child process (`exitWithChildStatus`) saw that child exit non-zero or die on a signal; the run's exit code is the child's own status verbatim, not the CLI's usual 2. Meta: `exitCode`, `signal`. ### CLI.COMMAND_MOVED [#CLI.COMMAND_MOVED] The user typed a retired command path, or a retired flag on a surviving command, that the redirect table claims; instead of failing as unknown, the run names the replacement invocation as a run-command next action, with the retirement reason in `why` when one is recorded. Exits 2. Meta: none. ### CLI.CONFIG_MISSING_MARKER [#CLI.CONFIG_MISSING_MARKER] The evaluated `prisma.config.ts` default export carries no `$prismaConfig` version marker — most likely a Prisma ORM 7 config, which uses the same filename — so the loader stops rather than misread it; the fix is to wrap the exported object in `definePrismaConfig`. Raised by the config loader for any command with a `needs.config` section. The file's absolute path is in `where.path`. Meta: none. ### CLI.CONFIG_NOT_FOUND [#CLI.CONFIG_NOT_FOUND] The file `--config` named does not exist. Only an explicitly named file is an error — an absent `prisma.config.ts` found by discovery is fine, because section validators own absence and supply defaults. The path is in `where.path`. Meta: none. ### CLI.CONFIG_PARENT_CYCLE [#CLI.CONFIG_PARENT_CYCLE] A config file's explicit `parent` names a file that is already on the config chain, which would loop forever. Raised by the config loader while following the chain; the declaring file's absolute path is in `where.path` and the summary names both files. The fix is to point `parent` elsewhere or set it to `false`. Meta: none. ### CLI.CONFIG_PARENT_INVALID [#CLI.CONFIG_PARENT_INVALID] A config file declares `parent` with a value that is neither `false` nor a path string. Raised by the config loader while following the chain; the declaring file's absolute path is in `where.path`. Meta: none. ### CLI.CONFIG_PARENT_NOT_FOUND [#CLI.CONFIG_PARENT_NOT_FOUND] A config file's explicit `parent` names a file that does not exist. Naming a parent is deliberate, so its absence is an error — unlike discovery, where finding no file is fine. Raised by the config loader while following the chain; the declaring file's absolute path is in `where.path` and the summary names the missing target. Meta: none. ### CLI.CONFIG_FIELD_INVALID [#CLI.CONFIG_FIELD_INVALID] One field of a config section declared by schema failed that schema: the wrong type, a missing required field, or a value outside the declared set. The summary names the section and the field with arktype's description of the problem; `where.path` is the config file that declared the field's top-level key (so the file to fix on a chain), and `meta.section` and `meta.field` carry the names. Travels as an accompanying diagnostic under `CLI.CONFIG_SECTION_INVALID`. Raised by the engine's schema validation before the handler runs. Meta: `section`, `field`. ### CLI.CONFIG_SECTION_INVALID [#CLI.CONFIG_SECTION_INVALID] The config section a command declared in `needs.config` failed its validator; the individual problems travel as accompanying diagnostics on the envelope, and the summary names the section and the config file actually read (respecting `--config`). Raised by the engine's needs check before the handler runs. Meta: none. ### CLI.CONFIG_UNKNOWN_SECTION [#CLI.CONFIG_UNKNOWN_SECTION] The config file has a top-level key that is not a section any mounted command or command family declares; the set of section names is closed, so an unrecognized key is a typo or leftover the CLI refuses to silently ignore. The `why` lists the recognized section names, and the file path is in `where.path`. Raised by the engine's needs check, deliberately outside the host-replaceable loader. Meta: none. ### CLI.CONFIG_UNREADABLE [#CLI.CONFIG_UNREADABLE] Evaluating `prisma.config.ts` threw. Two variants share the code: when the error chain shows the `prisma/config` entry point could not be resolved (the prisma package missing from the project, or too old), the guidance is to install the prisma package matching the CLI's version; otherwise the summary carries the first line of the evaluation error and the fix is to repair the file. The path is in `where.path`. Meta: none. ### CLI.CONFIG_VERSION_UNSUPPORTED [#CLI.CONFIG_VERSION_UNSUPPORTED] The config file's `$prismaConfig` marker declares a version other than the one this CLI supports; the fix is to regenerate the config with a matching `definePrismaConfig` or update the CLI. The path is in `where.path`. Meta: none. ### CLI.CONSENT_REQUIRED [#CLI.CONSENT_REQUIRED] A consent prompt was reached under `--yes` or in a non-interactive session. Consent has no default answer and `--yes` does not grant it, so there is nothing for the run to assume. When the consent declares a token, the message and next action say to pass `--confirm `, and the token travels in meta; without a token, the only path is running the command interactively. Meta: `consentToken` (only when the consent declares a token). A statement prompt (`ctx.prompt.statement` or `ctx.prompt.statements`) raises the same code when no statement flag on the command line answers it. One error lists every question still unanswered: the summary names the subjects, `why` carries the questions, and the next actions give one flag to pass per verb, such as `--delete Legacy` or `--rename 'Legacy:'`. `why` also lists every given statement flag that names none of the questions' subjects, and any `--confirm` token, which answers no statement, so a misspelled flag is reported before anyone answers a prompt. Meta: `unanswered` (a list of `{ subject, verbs }`), plus `subject` and `verbs` when exactly one question is unanswered; `unmatched` (a list of `{ verb, values }`) and `confirm` (the tokens) when any. ### CLI.CONSENT_UNUSED [#CLI.CONSENT_UNUSED] A consent flag was given but answered nothing: a statement flag such as `--delete Legacy`, or a `--confirm` token. The summary says why for each flag: no statement prompt asked about that subject and no `ctx.statements.take` took it (usually a mistyped subject), the question about it was already answered by another flag (a flag given twice, or two verbs for one subject), or no consent in the run asked for that `--confirm` token. The `--confirm` case applies only to a command that declares statements, where such a token is certainly a mistake; elsewhere a leftover `--confirm` is still ignored until [prisma/prisma-cli#339](https://github.com/prisma/prisma-cli/issues/339) lands. `why` lists the subjects the run asked about. Raised when the handler returns from a run that would otherwise have succeeded, so a run that failed for another reason reports that reason only; or earlier, as soon as `ctx.prompt.statements(questions, { last: true })` has answered its questions, before the command acts. Exits 2. Meta: `unused` (a list of `{ verb, values }`), `confirm` (the unused `--confirm` tokens, when any), `asked` (the subjects asked about, when any). ### CLI.CREDENTIALS_LOCKED [#CLI.CREDENTIALS_LOCKED] The advisory lock on the stored-credentials file was held by another prisma process for longer than the wait timeout, so this run's credential mutation gave up; the fix is to wait for the other command and retry. Raised by the auth state file's lock helper in `packages/cli`. Meta: none. ### CLI.CREDENTIALS_REQUIRED [#CLI.CREDENTIALS_REQUIRED] The command needs a signed-in credential and none is usable. One constructor covers five reasons: not signed in at all, an expired session, a session expiring too soon for a command that hands credentials to a child process (which cannot refresh them), a workspace session that ended mid-run, and workspace sessions held with none selected as current. Raised identically by the engine's needs check, `ctx.activeCredential`, and the request path; next actions point at signing in or `prisma auth workspace use`. When `prisma auth whoami` meets this code during its identity lookup it reports `authenticated: false` with exit `0` instead of failing. Meta: none. ### CLI.CREDENTIALS_UNREADABLE [#CLI.CREDENTIALS_UNREADABLE] The stored-credentials file exists but could not be read (any read failure other than the file being absent, which yields empty state instead); the guidance is to check the file's permissions. The underlying error is attached as `cause`. Meta: none. ### CLI.INTERACTION_REQUIRED [#CLI.INTERACTION_REQUIRED] The session is not interactive (no TTY stdin, CI, or `--no-interactive`) and the command cannot proceed without a person. Two raise sites: the engine's needs check for a command declaring `needs.interaction`, and `ctx.prompt.browserWait`, which refuses to start a browser wait it could never finish — in that case the URL travels in the error so the user can finish there manually. Meta: `url` (browserWait raise only; none from the needs check). ### CLI.INTERNAL_ERROR [#CLI.INTERNAL_ERROR] A bug, not a user error: a non-structured throw from a handler, an engine invariant violation (undocumented exit code, malformed result), or a stricli internal failure that never settled. The summary is the first line of the underlying error message. Exits 1, the taxonomy's bug code, rather than the usual 2. Meta: none. ### CLI.INVALID_ARGUMENTS [#CLI.INVALID_ARGUMENTS] The invocation's arguments did not parse or contradict each other. Raise sites: stricli's argument-parse failure mapped at the adapter boundary (its usage text becomes summary and `why`), `--config=` given an empty value, a statement flag (such as `--delete`) given the wrong number of values for its declared arity or an empty value, `prisma init --skills` given `none` combined with agent names or an unknown agent name, and `prisma skills sync` given both `--disable` and `--enable`. Exits 2 as a usage error. Meta: none. ### CLI.MISSING_DEPENDENCY [#CLI.MISSING_DEPENDENCY] A command declared an optional peer dependency in `needs.dependencies` that does not resolve from the project; the engine probes with `require.resolve` and phrases the install command for the package manager it detected. Meta: `specifier`, `installCommand`. ### CLI.PACKAGE_MANAGER_FAILED [#CLI.PACKAGE_MANAGER_FAILED] A `ctx.packages` operation (an install, or running a package through the manager) exited non-zero — or nothing ran at all because the host wires no package-manager runner, recorded in `meta.reason`. The redacted command line is offered as a run-command next action so the user can run it themselves. Meta: `form`, `manager`, `command`, `exitCode`, `stderrTail`, `reason` (only when the runner is unavailable). ### CLI.PROMPT_CANCELLED [#CLI.PROMPT_CANCELLED] The user cancelled a prompt: EOF on stdin at a line-rendered prompt, a clack cancel (Ctrl-C at the prompt UI), a SIGINT or SIGTERM delivered while a prompt waits for input, an abort during a browserWait poll, Ctrl-C at the `prisma auth login` paste prompt, or — via the service commands' `userCancelledError` — consent declined interactively. Settles with exit 3, the cancellation code, instead of 2; a SIGTERM that cancels a waiting prompt settles 143 instead, because it ends the run as a delivered signal, while a SIGINT is the user declining, like Ctrl-C. After a signal has cancelled a prompt, every later prompt in the run is cancelled at once and a further signal force-exits. Meta: none. ### CLI.PROMPT_INVALID [#CLI.PROMPT_INVALID] An answer could not be interpreted: not a yes/no for a confirm, not one of a select's options with no default to fall back to, a consent token typed wrong where re-prompting is impossible (scripted answers or piped stdin — the interactive clack renderer re-prompts instead), or a statement answer the command rejected. A statement answer is rejected when a verb flag's value fails the command's check (re-prompting cannot correct a flag), or when a typed answer does not start with one of the verbs or fails the check where re-prompting is impossible; the summary carries the command's reason. Meta: `consentToken` (token-mismatch raise only). ### CLI.PROMPT_REQUIRED [#CLI.PROMPT_REQUIRED] A prompt with no declared default was reached where it cannot be shown — under `--yes` or in a non-interactive session — so the run halts; the fix is to run from an interactive terminal or pass a flag that answers the prompt. Meta: none. ### CLI.SPAWN_FAILED [#CLI.SPAWN_FAILED] `ctx.spawn` could not start the requested program, or the child's lifecycle promise rejected; the first line of the underlying failure is in `why`, and the guidance is to check the program is installed and on PATH. Meta: `command`. ### CLI.TELEMETRY_PREFERENCE_UNAVAILABLE [#CLI.TELEMETRY_PREFERENCE_UNAVAILABLE] The `prisma telemetry status|enable|disable` commands could not resolve the user-level config directory because none of XDG_CONFIG_HOME, HOME, APPDATA, or USERPROFILE is set; unreachable in production, where HOME or USERPROFILE is always set. Meta: none. ### CLI.UNKNOWN_COMMAND [#CLI.UNKNOWN_COMMAND] The typed path routed to no command (stricli's route failure, mapped at the adapter boundary) and no redirect claims it; next actions carry up to three "did you mean" suggestions ranked by edit distance over command paths and their prefixes, plus a pointer to `--help`. Exits 2. Meta: none. ## FEEDBACK [#feedback] ### FEEDBACK.EMAIL_INVALID [#FEEDBACK.EMAIL_INVALID] `prisma feedback --email` was given a value that fails the CLI's local check (a basic address pattern, at most 320 characters), which mirrors the feedback service's own limit so the refusal happens before any network round trip. The suggested actions are to pass a valid address or drop the flag to send anonymously. Meta: none. ### FEEDBACK.MESSAGE_REQUIRED [#FEEDBACK.MESSAGE_REQUIRED] `prisma feedback` was run with a message that is empty after trimming. The fix is to pass a non-empty message. Meta: none. ### FEEDBACK.MESSAGE_TOO_LONG [#FEEDBACK.MESSAGE_TOO_LONG] The `prisma feedback` message exceeds 4000 characters, the feedback service's limit, checked locally before the network round trip; the actual length and the limit are stated in `why`. The fix is to shorten the message. Meta: none. ### FEEDBACK.SEND_FAILED [#FEEDBACK.SEND_FAILED] `prisma feedback` could not deliver the submission: the feedback endpoint was unreachable (or timed out), the service answered a non-OK HTTP status (the status and any service-supplied error message go into `why`), or the response body could not be read; a body that arrived but was not JSON is treated as success, and a user cancellation is rethrown rather than wrapped. The suggested action is to check the network and rerun. Meta: none. ## GIT [#git] ### GIT.REPO_ALREADY_CONNECTED [#GIT.REPO_ALREADY_CONNECTED] `prisma git connect` found the resolved project already connected to a different GitHub repository than the one requested (reconnecting the same repository is idempotent and succeeds). The fix is to run `prisma git disconnect` first. Meta: `repository`. ### GIT.REPO_CONNECTION_FAILED [#GIT.REPO_CONNECTION_FAILED] A management API call in the `prisma git connect`/`disconnect` flow failed — creating the install intent, listing installations or repositories, reading or writing the source-repository connection, or a pagination cursor that stopped advancing; the API's message and hint, when present, become `why` and the suggested fix, and a 401/403 is routed to the auth error path instead. Meta: `status`, `apiCode` (when the API supplied one). ### GIT.REPO_INSTALLATION_REQUIRED [#GIT.REPO_INSTALLATION_REQUIRED] `prisma git connect` waited for a GitHub App installation but the wait ended (timeout or final poll) with the workspace still holding no inspectable installation that could link the repository. The fix is to finish installing the GitHub App at the install URL, then rerun `prisma git connect`; the URL is also offered as an open-url next action. Meta: `repository`, `installUrl`. ### GIT.REPO_NOT_ACCESSIBLE [#GIT.REPO_NOT_ACCESSIBLE] `prisma git connect` waited for repository access but the wait ended with the workspace's existing GitHub App installations still not exposing the requested repository — the same install wait as `GIT.REPO_INSTALLATION_REQUIRED`, distinguished by at least one inspectable installation existing. The fix is to grant the App access to this repository at the install URL, then rerun `prisma git connect`. Meta: `repository`, `installUrl`. ### GIT.REPO_NOT_CONNECTED [#GIT.REPO_NOT_CONNECTED] `prisma git disconnect` found no active GitHub repository connection on the resolved project, so there is nothing to disconnect. The fix is to run `prisma git connect` first. Meta: none. ### GIT.REPO_PROVIDER_UNSUPPORTED [#GIT.REPO_PROVIDER_UNSUPPORTED] The URL given to `prisma git connect` (or read from the local `origin` remote) did not parse as a GitHub repository URL; repository connection supports GitHub only. The fix is to pass a GitHub repository URL. Meta: none. ### GIT.USAGE_ERROR [#GIT.USAGE_ERROR] `prisma git connect` was run with no repository URL argument and the local repository has no `origin` remote to fall back on. The fix is to pass a GitHub repository URL or add a GitHub `origin` remote and rerun. Meta: none. ## INIT [#init] ### INIT.CONFIG_KEPT [#INIT.CONFIG_KEPT] A warn diagnostic from `prisma init`: a `prisma.config.ts` already exists that does not configure `skills.agents`, and init never edits an existing config, so it was left untouched. The nextAction carries the exact `skills: { agents: [...] }` snippet to add to the `definePrismaConfig` call; a config that already sets `skills.agents` produces no diagnostic at all. Meta: none. ### INIT.CONFIG_UNWRITABLE [#INIT.CONFIG_UNWRITABLE] A warn diagnostic from `prisma init`: writing the scaffolded `prisma.config.ts` failed, so the config step was skipped. The nextAction asks the user to create the file themselves with the intended `skills: { agents: [...] }` section. Meta: none. ### INIT.DEV_DEPENDENCIES_NOT_AN_OBJECT [#INIT.DEV_DEPENDENCIES_NOT_AN_OBJECT] A warn diagnostic from `prisma init`: the `devDependencies` field in package.json is not an object, so init did not add the `prisma` dev dependency (the postinstall-hook edit still proceeds when its own field is fine). The nextAction is to fix the field, add `"prisma": ""` by hand, and run the package manager's install. Meta: none. ### INIT.NO_PACKAGE_JSON [#INIT.NO_PACKAGE_JSON] A warn diagnostic from `prisma init`: there is no package.json in the current directory, so neither the skills-sync postinstall hook nor the `prisma` dev dependency was added. The nextActions are to rerun init from the directory that holds package.json, or to add the dependency by hand. Meta: none. ### INIT.PACKAGE_JSON_UNREADABLE [#INIT.PACKAGE_JSON_UNREADABLE] A warn diagnostic from `prisma init`: package.json exists but could not be parsed as JSON, so the postinstall hook and the `prisma` dev dependency were not added. The nextActions give the exact postinstall script and dependency entry to add by hand. Meta: none. ### INIT.PACKAGE_JSON_UNWRITABLE [#INIT.PACKAGE_JSON_UNWRITABLE] A warn diagnostic from `prisma init`: the manifest edit was prepared but writing package.json back failed, so the file was left unchanged. The nextActions cover only what the failed write would have added — the postinstall script, the dev dependency, or both. Meta: none. ### INIT.POSTINSTALL_KEPT [#INIT.POSTINSTALL_KEPT] A warn diagnostic from `prisma init`: package.json already has a postinstall script that is not the CLI's own, and init never overwrites a user's script, so it was left alone. The nextAction is to append `prisma skills sync || exit 0` to the existing script yourself so skills resync on every install. Meta: none. ### INIT.SCRIPTS_NOT_AN_OBJECT [#INIT.SCRIPTS_NOT_AN_OBJECT] A warn diagnostic from `prisma init`: the `scripts` field in package.json is not an object, so init left the manifest untouched instead of adding the postinstall hook (which also skips adding the dev dependency, since the manifest is not edited at all). The nextAction gives the postinstall script to add by hand. Meta: none. ### INIT.SKILLS_SYNC_FAILED [#INIT.SKILLS_SYNC_FAILED] A warn diagnostic from `prisma init`: the final step, syncing the agent skills from installed Prisma packages, threw; the cause's message is appended to the summary. The nextAction is to retry with `prisma skills sync` on its own. Meta: none. ## POSTGRES [#postgres] ### POSTGRES.AMBIGUOUS [#POSTGRES.AMBIGUOUS] A database name passed to a `prisma postgres` subcommand matched more than one database in the resolved project — raised by the shared database resolver (`controllers/database.ts`). The fix is to pass the database id, or `--branch ` to narrow the match; the candidates are carried in meta as `matches`, each with `id`, `name`, and `branchName`. Meta: `matches`. ### POSTGRES.API_ERROR [#POSTGRES.API_ERROR] A database REST API request failed without a more specific code — the generic fallback for every `prisma postgres` operation, raised by the provider (`lib/database/provider.ts`), and also when a database response omits its project id. The `why` carries the API's message or HTTP status, and the fix is the API's hint when it sent one. An HTTP 401 or 403 raises this code too, with a `why` saying the API rejected the request as unauthorized and a `prisma auth login` next action. Meta: `status`, `apiCode` (the API's own error code, when the response supplied one). ### POSTGRES.BACKUP_NOT_FOUND [#POSTGRES.BACKUP_NOT_FOUND] `prisma postgres backup restore` got a 404 from the restore endpoint; the source and target databases are resolved before the call, so the 404 identifies the backup id. The fix is to pass a backup id from `prisma postgres backup list `. Meta: none. ### POSTGRES.BACKUPS_UNSUPPORTED [#POSTGRES.BACKUPS_UNSUPPORTED] Listing backups returned a 422 because the platform does not manage backups for this database — for example a remote/BYO database (raised by `prisma postgres backup list`). The fix is to use your own backup tooling for externally managed databases. Meta: none. ### POSTGRES.CONNECTION_MISSING [#POSTGRES.CONNECTION_MISSING] `prisma postgres create` created the database, but the API response did not include the first one-time connection payload. The fix is to create a connection explicitly with `prisma postgres connection create `. Meta: none. ### POSTGRES.CONNECTION_STRING_MISSING [#POSTGRES.CONNECTION_STRING_MISSING] A connection create or rotate succeeded, but the API response did not include the one-time connection string the CLI would show exactly once (raised by `prisma postgres create`, `postgres connection create`, and `postgres connection rotate`). The fix is to rerun the operation, or create a replacement connection and store the returned URL immediately. Meta: none. ### POSTGRES.NOT_FOUND [#POSTGRES.NOT_FOUND] The database a `prisma postgres` subcommand targets could not be resolved: either no database matched the given id or name in the project (and optional `--branch`) scope, or a database that was just listed returned 404 on read because it was removed while the command ran (raised by the shared resolver in `controllers/database.ts`). The fix is to pass an id or name from `prisma postgres list`. Meta: none. ### POSTGRES.PLAN_LIMIT_REACHED [#POSTGRES.PLAN_LIMIT_REACHED] A database operation was blocked because the workspace has used up the operations included in its plan — the API's structured plan-limit discriminator, detected on any `prisma postgres` REST API call; this is a workspace plan restriction, not a Prisma outage. The one nextAction is to upgrade the workspace plan, with the upgrade URL and current plan name when the workspace subscription could be read. Meta: `workspaceId`, `blockedFeature`, `planName`, `usageBlocked`, `upgradeUrl` (each `null` when unavailable). ### POSTGRES.RESTORE_CONFLICT [#POSTGRES.RESTORE_CONFLICT] `prisma postgres backup restore` got a 409 because the target database is provisioning or already recovering. The fix is to wait for the database to become ready — check with `prisma postgres show ` — then retry. Meta: none. ### POSTGRES.USAGE_ERROR [#POSTGRES.USAGE_ERROR] A `prisma postgres` subcommand was called with missing or invalid arguments: `create` without a name, `connection delete`/`connection rotate` without a connection id, `backup restore` without `--backup`, or `usage` with `--from` later than `--to`. The nextActions show the corrected command form. Meta: none. ## PROJECT [#project] ### PROJECT.AMBIGUOUS [#PROJECT.AMBIGUOUS] Project resolution matched more than one project: an explicit project reference (matched by id first, then by name) hit several projects, or the implicit directory context did — raised by any command that resolves a project, including `branch list`. The fix is to pass `--project `, and the next actions include `prisma project link ` with the first match's id verbatim so the user can copy an exact disambiguating reference. Meta: `matches`. ### PROJECT.API_ERROR [#PROJECT.API_ERROR] A REST API project operation (list, rename, delete, or transfer) failed. Listing projects deliberately throws this rather than returning an empty list, so a rejected request is distinguishable from a workspace that genuinely has no projects. The `why` carries the API's message or HTTP status, and the API's own error code, when it sent one, is in `meta.apiCode`. Meta: `status`, `apiCode` (each present only when the response supplied it). ### PROJECT.CREATE_FAILED [#PROJECT.CREATE_FAILED] The platform rejected creating a project — raised by `prisma project create` and by the create-a-new-project path of `prisma project link`. An HTTP 401/403 gets a permissions-focused fix; any other failure surfaces the underlying error message as the `why`. Meta: none. ### PROJECT.DELETE_BLOCKED [#PROJECT.DELETE_BLOCKED] The REST API answered `prisma project delete` with HTTP 400, which typically means the project still has active deployments; the fix is to delete the project's services first (`prisma service delete --service `) and retry. The API's own message replaces the default `why` when present. Meta: none. ### PROJECT.ENV_API_ERROR [#PROJECT.ENV_API_ERROR] A REST API call made by the `prisma project env` commands (reading, writing, or deleting variables, or resolving and creating branches for a scope) failed. The summary names the call that failed, for example "Failed to add STRIPE_KEY". An HTTP 401 or 403 raises this code too, with a `why` saying the API rejected the request as unauthorized and a `prisma auth login` next action. Meta: `status`, `apiCode` (the API's own error code, when the response supplied one). ### PROJECT.ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH [#PROJECT.ENV_BRANCH_CREATE_REQUIRES_DEFAULT_BRANCH] `prisma project env add --branch ` will create a missing preview branch, but the project has no default branch yet, and creating the first branch would make it the default while branch env overrides are preview-only. The fix is to create or deploy the default branch first, for example via `prisma git connect`. Meta: none. ### PROJECT.ENV_BRANCH_NOT_FOUND [#PROJECT.ENV_BRANCH_NOT_FOUND] A `prisma project env` update, list, or delete named a branch scope (`--branch `) that does not exist — only `env add` creates missing branches. The fix is to create the branch by deploying it, or to use `project env add --branch` to create its first override. Meta: none. ### PROJECT.ENV_BRANCH_SCOPE_IS_PRODUCTION [#PROJECT.ENV_BRANCH_SCOPE_IS_PRODUCTION] A `prisma project env` command's `--branch` flag resolved to the project's production branch; production variables are project-level only, and branch overrides apply to preview branches. The fix is to use `--role production` instead. Meta: none. ### PROJECT.ENV_FILE_APPLY_FAILED [#PROJECT.ENV_FILE_APPLY_FAILED] `prisma project env add --file` or `update --file` failed while writing one of the file's keys, after zero or more earlier keys were already written. The `why` names the keys written before the failure and the underlying cause, and the next actions include a retry command scoped to a file of the remaining keys. Meta: `file`, `failedKey`, `writtenKeys`. ### PROJECT.ENV_PREVIEW_DEFAULT_MISSING [#PROJECT.ENV_PREVIEW_DEFAULT_MISSING] Not an error: a warn diagnostic emitted on a successful `prisma project env add` to a branch scope (single key or `--file`) for each key that has no preview-level default, meaning the variable will exist only on that branch. Meta: none. ### PROJECT.ENV_VARIABLE_ALREADY_EXISTS [#PROJECT.ENV_VARIABLE_ALREADY_EXISTS] `prisma project env add` targeted a key (or, in `--file` mode, one or more keys) that already exists in the targeted scope. The single-key fix is to use `prisma project env update`; the file-mode fix is to split the input file and update existing keys separately from adding new ones. Meta: `keys` (file mode only; the single-key form carries no meta). ### PROJECT.ENV_VARIABLE_NOT_FOUND [#PROJECT.ENV_VARIABLE_NOT_FOUND] `prisma project env update` or `env delete` targeted a key (or, in update's `--file` mode, one or more keys) that does not exist in the targeted scope. The fix for update is to create the variable with `env add` (or split a mixed file); for delete it is to list the scope's variables first. Meta: `keys` (file-mode update only; the single-key forms carry no meta). ### PROJECT.LOCAL_STATE_STALE [#PROJECT.LOCAL_STATE_STALE] The local project binding in `.prisma/local.json` is unusable: the pinned project is no longer in the selected workspace's project list, or the pin file is invalid JSON or has an invalid shape — raised by any command that resolves the project implicitly through the pin. The fix is to delete the pin file and choose a project explicitly. Meta: `pinPath`. ### PROJECT.LOCAL_STATE_WRITE_FAILED [#PROJECT.LOCAL_STATE_WRITE_FAILED] `prisma project link` or `project create` could not save the local binding: writing `.prisma/local.json` failed, or updating `.gitignore` to keep the binding out of git failed — the fix is to check directory permissions and retry. The same code is also emitted as a warn diagnostic (not an error) by `project delete` and `project transfer` when the operation itself succeeded but the now-stale local pin could not be removed or rewritten. Meta: `pinPath` or `gitignorePath`, plus `operation` (the error form; the diagnostic form carries none). ### PROJECT.LOCAL_WORKSPACE_MISMATCH [#PROJECT.LOCAL_WORKSPACE_MISMATCH] `.prisma/local.json` links the directory to a project in one workspace, but the CLI session's active workspace is a different one — raised by any command that resolves the project through the pin. The fix is to switch to the pinned workspace (`prisma auth workspace use `) or relink the directory to a project in the current workspace. Meta: `pinPath`, `pinnedWorkspaceId`, `pinnedProjectId`, `activeWorkspaceId`, `activeWorkspaceName`. ### PROJECT.NOT_FOUND [#PROJECT.NOT_FOUND] An explicit project reference matched no project in the active workspace, either because it does not exist or because the credential cannot see it — raised during project resolution for any command that accepts one, including `branch list` and the `project link`/`transfer`/`delete` target lookup. The fix is to pass an id or name from `prisma project list`. Service commands raise the same code one step later, when the services API answers "Resource Not Found" for a project that did resolve — the directory binding points at a project that no longer exists or is no longer accessible — and their next actions point at `project show` to inspect the binding and `project link` to fix it. Meta: none. ### PROJECT.RENAME_FAILED [#PROJECT.RENAME_FAILED] The REST API answered `prisma project rename` with HTTP 400 or 422, meaning the platform rejected the new name; the API's message and hint replace the default `why` and fix when present, and the fallback fix is to retry with a different name. Meta: none. ### PROJECT.SETUP_REQUIRED [#PROJECT.SETUP_REQUIRED] A command needed a project but the directory is not linked and no `--project` flag was given; the CLI deliberately refuses to pick a project from package or directory names, treating them as suggestions only. The meta carries the inferred name suggestion and any matching candidate projects, and the next actions walk through choosing between linking an existing project (`prisma project link`) and creating a new one. Meta: `suggestedProjectName`, `suggestedProjectNameSource`, `candidates`, `recoveryCommands`. ### PROJECT.TRANSFER_RECIPIENT_REQUIRED [#PROJECT.TRANSFER_RECIPIENT_REQUIRED] `prisma project transfer` was invoked without naming a receiving workspace: neither `--to-workspace ` (for a locally authenticated workspace) nor `--recipient-token ` (for a cross-account transfer) was passed. Meta: none. ### PROJECT.TRANSFER_RECIPIENT_UNAVAILABLE [#PROJECT.TRANSFER_RECIPIENT_UNAVAILABLE] `prisma project transfer --to-workspace` needs to resolve locally stored OAuth workspace sessions, but `PRISMA_SERVICE_TOKEN` is set and service-token mode does not read them. The fix is to pass `--recipient-token ` for the receiving workspace, or to unset the service token. Meta: none. ### PROJECT.TRANSFER_REJECTED [#PROJECT.TRANSFER_REJECTED] The REST API answered `prisma project transfer` with HTTP 400 — for example because the recipient token is invalid or expired; the API's message replaces the default `why` when present, and the fix is to check the recipient session or token and retry. Meta: none. ### PROJECT.USAGE_ERROR [#PROJECT.USAGE_ERROR] A `project` or `branch` group command was invoked with unusable arguments — for example a `project env` write without an explicit `--role` or `--branch` scope, `project transfer` with both recipient flags at once, `project create` with an empty name, or an interactive `project link` whose selection was cancelled. Usage errors exit 2. Meta: none. ## SERVICE [#service] ### SERVICE.BRANCH_INVALID [#SERVICE.BRANCH_INVALID] A `--branch` flag was passed with an empty or whitespace-only value to a service command; the check runs before any resolution because a blank value must never fall through to the default-branch behavior of omitting the flag. The fix is to pass a non-empty branch name, or omit `--branch` to target the default branch. Meta: none. ### SERVICE.BRANCH_NOT_DEPLOYABLE [#SERVICE.BRANCH_NOT_DEPLOYABLE] A `service domain` command was pointed at a non-production branch, which the domain-target resolver refuses because custom domains on preview branches are not supported in Public Beta. The fix is to use `--branch production`, or attach the domain after promoting to the production branch. Meta: none. ### SERVICE.DELETE_FAILED [#SERVICE.DELETE_FAILED] `service delete` called the platform's app-teardown API and it rejected; the underlying error's message becomes `why` and the original error is carried in `cause`. Next actions point at `service show` and `service version list` for the service. Meta: none. ### SERVICE.DEPLOY_FAILED [#SERVICE.DEPLOY_FAILED] The general "REST API call failed" wrapper for the `service` command family — despite the name there is no deploy command here: it wraps failures to create a service, list services or versions, show/promote/roll back/delete/start/stop a version, resolve a service URL, and unrecognized custom-domain API failures. The underlying error's message becomes `why` and the original error is carried in `cause`; each raise site attaches its own next actions. On the domain fallback path only, a `DomainApiError` adds debug meta. Meta: `status`, `apiCode`, `hint` (domain fallback path only; otherwise none). ### SERVICE.DOMAIN_ALREADY_REGISTERED [#SERVICE.DOMAIN_ALREADY_REGISTERED] `service domain add` got HTTP 409 from the domain API because the hostname is already registered, to this or another service. The fix is to delete the domain on the service that owns it, or contact Prisma support if that service is not accessible. Meta: `status`, `apiCode`, `hint`. ### SERVICE.DOMAIN_DNS_NOT_CONFIGURED [#SERVICE.DOMAIN_DNS_NOT_CONFIGURED] `service domain add` got HTTP 400 or 422 whose message the CLI recognizes as a DNS problem (no CNAME, DNS verification failed, and similar). When the API's text names a `*.prisma.build` target, the CLI composes the exact CNAME record to add and carries it in `meta.dnsRecord` and in the advice action; without a target it advises rerunning with `--log-level verbose` to see the API response. Meta: `status`, `apiCode`, `hint`, `dnsRecord` (when the DNS target could be extracted). ### SERVICE.DOMAIN_HOSTNAME_INVALID [#SERVICE.DOMAIN_HOSTNAME_INVALID] The hostname given to a `service domain` command is not a usable custom domain — raised either by local validation before any API call (protocol, path, port, wildcard, single label, or bad DNS labels) or when `service domain add` gets a plain HTTP 400 rejection from the domain API. The fix is to pass a bare hostname such as `shop.acme.com`. Meta: `status`, `apiCode`, `hint` (API-rejection path only; none for local validation). ### SERVICE.DOMAIN_NOT_FOUND [#SERVICE.DOMAIN_NOT_FOUND] A `service domain` command targeted a hostname that is not attached to the resolved service — raised when the service's domain listing has no matching hostname, or when a show/delete/retry/wait call gets HTTP 404. The fix is to check the hostname and service, or add the domain first. Meta: none. ### SERVICE.DOMAIN_QUOTA_EXCEEDED [#SERVICE.DOMAIN_QUOTA_EXCEEDED] `service domain add` was refused because the custom-domain quota is reached — HTTP 429, or a 409 whose text mentions a quota, maximum, or limit. The fix is to delete an existing custom domain before adding another. Meta: `status`, `apiCode`, `hint`. ### SERVICE.DOMAIN_RETRY_NOT_ELIGIBLE [#SERVICE.DOMAIN_RETRY_NOT_ELIGIBLE] `service domain retry` got HTTP 409: the domain is not in a state that can be retried, typically because a verification or TLS step is still in progress. The fix is to wait for the current step to finish and retry only if the domain then fails. Meta: `status`, `apiCode`, `hint`. ### SERVICE.DOMAIN_VERIFICATION_FAILED [#SERVICE.DOMAIN_VERIFICATION_FAILED] `service domain wait` observed the domain reach the terminal `failed` status; `why` carries the platform's failure category and reason when reported, and a failure-specific fix line is attached when the CLI can derive one from the domain record. Next actions point at `service domain show` and `service domain retry`. Meta: none. ### SERVICE.DOMAIN_VERIFICATION_TIMEOUT [#SERVICE.DOMAIN_VERIFICATION_TIMEOUT] `service domain wait` ran out of time (default 15m, or `--timeout 0` for a single check) before the domain became active; `why` reports the status the domain was last seen in. The fix is to inspect the domain with `service domain show` or rerun the wait with a longer `--timeout`. Meta: none. ### SERVICE.FEATURE_UNAVAILABLE [#SERVICE.FEATURE_UNAVAILABLE] `service open` found a live version but the provider does not expose a stable live service URL for this service yet, so there is nothing to open. The next action is to inspect the service state with `service show`. Meta: none. ### SERVICE.LIVE_VERSION_UNKNOWN [#SERVICE.LIVE_VERSION_UNKNOWN] `service version rollback` without `--to` needs to know which version is live, because the default rollback target is defined relative to it, and the service record names no live version — the CLI refuses rather than guess what production is serving. The fix is to pass `--to ` explicitly. Meta: none. ### SERVICE.LOGS_FAILED [#SERVICE.LOGS_FAILED] `service logs` could not read the log — either the logs endpoint answered with a non-OK HTTP status (other than 404, which becomes SERVICE.VERSION_NOT_FOUND), or a page's closing terminal record reported `kind: "error"`, meaning the platform itself says the log read failed. The meta differs by path: the HTTP path carries `status`; the terminal-record path carries the platform's error `code`, `retryable`, and the resume `cursor` when present (in `--follow` mode one retryable terminal error is retried once before this settles the run). Meta: `status` (HTTP path) or `code`, `retryable`, `cursor` (stream path). ### SERVICE.LOGS_INCOMPLETE [#SERVICE.LOGS_INCOMPLETE] A `service logs` response body ended without the terminal record that closes a page, so the read was truncated and the lines already printed may be only part of the page; the run must not settle as if it had read the whole page. Distinct from SERVICE.LOGS_NO_CURSOR, which is a properly closed page with nothing to resume from. The fix is to rerun the command. Meta: none. ### SERVICE.LOGS_NO_CURSOR [#SERVICE.LOGS_NO_CURSOR] `service logs --follow` needs a resume cursor from each page to fetch the next one, and the page ended without one — continuing would re-request the default tail and silently print the same lines every interval, so the run stops and says why. It settles as an error rather than a clean end because `--follow` has no successful ending. The fix is to rerun without `--follow`, or retry if the version is still starting. Meta: none. ### SERVICE.LOGS_RANGE_CONFLICT [#SERVICE.LOGS_RANGE_CONFLICT] `service logs` was invoked with both `--tail` and `--from-start`, which ask for opposite ends of the log; the run is refused before any work. The fix is to pass `--tail ` for the last n lines or `--from-start` for the whole log, not both. Meta: none. ### SERVICE.NAME_REQUIRED [#SERVICE.NAME_REQUIRED] `service create` received an empty or whitespace-only name positional; the check runs before any resolution. The fix is to pass a name, as in `service create my-service`. Meta: none. ### SERVICE.NO_PREVIOUS_VERSION [#SERVICE.NO_PREVIOUS_VERSION] `service version rollback` without `--to` found no earlier version to switch back to — the service has no versions at all, or every version is the live one. The fix is to deploy a second version first, or pass `--to ` for a specific version. Meta: none. ### SERVICE.NO_VERSIONS [#SERVICE.NO_VERSIONS] The resolved service has no usable version for the command — raised by `service open` when the service has no versions, by `service logs` when it has no live version, and by `service domain add` when the API answers 422 because the production service has no promoted version that can receive a custom domain. The fix on the domain path is to promote a version on the production branch first, then add the domain again. Meta: `status`, `apiCode`, `hint` (domain-add path only; otherwise none). ### SERVICE.SELECTION_INVALID [#SERVICE.SELECTION_INVALID] The named service could not be found among the resolved project branch's services — the match tries the stable platform id first, then the name. The fix is to pass the id or name of an existing service; the suggested command is `service list`, deliberately not `service version list`, which itself has to resolve a service and would fail the same way. Meta: none. ### SERVICE.TARGET_REQUIRED [#SERVICE.TARGET_REQUIRED] A service command that acts on an existing service was run without naming one; service commands act only on an explicitly named target — nothing is inferred, remembered, or prompted for. The fix is to pass the service id or name as the first argument, with `service list` to find one. Meta: none. ### SERVICE.TIMEOUT_INVALID [#SERVICE.TIMEOUT_INVALID] The `--timeout` value passed to `service domain wait` is not a duration the parser accepts (`0`, or an integer with `ms`/`s`/`m`/`h`, such as `30s` or `15m`). Meta: none. ### SERVICE.VERSION_ALREADY_LIVE [#SERVICE.VERSION_ALREADY_LIVE] Not an error: a warn-severity diagnostic attached by `service version promote` and `service version rollback` when the selected version is already live for the service — the command skips the promote call, still reports the result, and exits 0. Meta: none. ### SERVICE.VERSION_ALREADY_RUNNING [#SERVICE.VERSION_ALREADY_RUNNING] Not an error: a warn-severity diagnostic attached by `service version start` when the selected version already reports `running` status — the start call is skipped, the result carries `alreadyInState: true`, and the run exits 0. Meta: none. ### SERVICE.VERSION_ALREADY_STOPPED [#SERVICE.VERSION_ALREADY_STOPPED] Not an error: a warn-severity diagnostic attached by `service version stop` when the selected version already reports `stopped` status — the stop call is skipped, the result carries `alreadyInState: true`, and the run exits 0. Meta: none. ### SERVICE.VERSION_DETACHED [#SERVICE.VERSION_DETACHED] A version was resolved by its globally-unique id but the REST API returned it without an owning service, so there is nothing to report or act on it as. The next action shows the version with `service version show`. Meta: none. ### SERVICE.VERSION_NOT_FOUND [#SERVICE.VERSION_NOT_FOUND] The requested service version does not exist or is not available — raised when resolving a version by its globally-unique id finds nothing (including a 404 from the logs endpoint), and, in the "for service" variant used by rollback and logs `--version-id`, when the id exists but does not belong to the resolved service. The fix is to pick an id from `service version list`. Meta: none. ### SERVICE.WORKSPACE_REQUIRED [#SERVICE.WORKSPACE_REQUIRED] A service command ran without a credential that names a workspace — either no authenticated session, or an environment token whose claims carry no workspace, which cannot scope these commands and is treated the same as having no credential. The fix is `auth login`. Meta: none. ## SKILLS [#skills] ### SKILLS.CONFIG_INVALID [#SKILLS.CONFIG_INVALID] An error diagnostic from validating the `skills` section of `prisma.config.ts`, surfaced by the commands that consume it (`init`, `skills sync`, `skills list`): the section is not an object, `skills.check` is not a boolean, `skills.agents` is not an array of strings, or an agent name is one this CLI does not know. Each variant's nextAction says what to write instead; code that reads the config outside a command handler (the staleness check, the post-login tip) treats an invalid config as absent and falls back to the default agent set rather than silencing the check. Meta: none. ### SKILLS.UNMANAGED_DIRECTORY [#SKILLS.UNMANAGED_DIRECTORY] A warn diagnostic from the skills sync (`skills sync`, and the sync step of `init`): a target agent skill directory already holds a skill this CLI does not manage, so sync left it untouched instead of installing the packaged skill there. The nextAction is to move or remove the unmanaged directory and rerun `skills sync`. Meta: none. ### SKILLS.VERSION_CONFLICT [#SKILLS.VERSION_CONFLICT] A warn diagnostic from the skills sync (`skills sync`, and the sync step of `init`): workspace members install different versions of the same skill-bearing Prisma package, so the skills for the highest version were installed and the members pinning a lower version get a skill describing a version they did not install. The nextAction is to pin one version of the package across the workspace. Meta: none. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # feedback (/docs/cli/feedback) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Send feedback to the Prisma CLI team. Location: CLI > feedback `feedback` sends feedback to the Prisma CLI team, straight from the terminal. ## Usage [#usage] #### bun ```bash bunx prisma feedback "The service logs command saved my afternoon" ``` #### pnpm ```bash pnpm prisma feedback "The service logs command saved my afternoon" ``` #### yarn ```bash yarn prisma feedback "The service logs command saved my afternoon" ``` #### npm ```bash npx prisma feedback "The service logs command saved my afternoon" ``` The message can be up to 4000 characters. Feedback is anonymous unless you add `--email
` so the team can reply. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # git (/docs/cli/git) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Connect a GitHub repository for push-to-deploy. Location: CLI > git Use `git` commands to manage the GitHub repository connection. Connecting enables the OIDC credential exchange for GitHub Actions deploys and the branch lifecycle automation. See [GitHub integration](https://www.prisma.io/docs/compute/github) and [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push). ## Usage [#usage] #### bun ```bash bunx prisma git connect ``` #### pnpm ```bash pnpm prisma git connect ``` #### yarn ```bash yarn prisma git connect ``` #### npm ```bash npx prisma git connect ``` Run it from a [linked](https://www.prisma.io/docs/cli/project) project directory. The command needs an interactive terminal: it opens the browser to install the GitHub App when needed and waits for the install. With `--no-interactive` it fails with `CLI.INTERACTION_REQUIRED`. Connecting from the CLI does not add a deploy workflow to the repository. [Add it yourself](https://www.prisma.io/docs/compute/deploy-on-push#4-add-the-deploy-workflow), or connect through the [Console](https://pris.ly/pdp), which opens a pull request with the workflow. ## Commands [#commands] | Command | Description | | ----------------------- | ----------------------------------------------------------------------------------------------- | | `git connect [git-url]` | Connect the linked project to a GitHub repository. Starts the GitHub App install flow if needed | | `git disconnect` | Stop the credential exchange and branch automation. Keeps the project and existing branches | ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # Global flags (/docs/cli/global-flags) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Flags accepted by every command in the unified Prisma CLI. Location: CLI > Global flags All commands in the unified Prisma CLI accept these flags. | Flag | What it does | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- | | `--json` | Print machine-readable output (shorthand for `--format json`). Use this in CI and scripts. | | `--format ` | Output format: `human`, `json`, or `markdown`. | | `--log-level ` | Commentary verbosity: `error`, `warn`, `info`, or `verbose`. | | `-q`, `--quiet` | Suppress nonessential output (shorthand for `--log-level error`). | | `-v`, `--verbose` | Print more detail (shorthand for `--log-level verbose`). | | `--color` / `--no-color` | Force colored output on or off. | | `--interactive` / `--no-interactive` | Force prompts on or off. | | `-y`, `--yes` | Accept prompt defaults without asking. | | `--confirm ` | Grant a consent prompt in advance by giving its token, so the command does not ask (repeatable). Works in scripts and in an interactive terminal. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `-h`, `--help` | Print the manual for a command: what it does, its options, a workflow where one applies, and examples. | | `--version` | Print the CLI version and exit. | ## Output formats [#output-formats] Without `--format`, a terminal gets `human` output and a pipe gets `json`. `markdown` is never chosen for you; pass `--format markdown` to get it. `markdown` is for AI agents that read command output as text rather than parsing JSON. It prints the same content as `human`, as plain Markdown: a summary line, `label: value` lines, pipe tables, bullet lists, and fenced code, followed by a `### Next` section with the suggested next commands and a `### Diagnostics` section with any findings. There is no padding, alignment, or color, and no JSON envelope. Everything the command produces, including errors, help, `--version`, and progress, goes to stdout, so an agent reads one stream. Use `npx prisma --help` when you need the exact command help from the installed version. The output modes and the JSON envelope these flags select are documented on [Configuration](https://www.prisma.io/docs/cli/configuration#output-modes). ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # Overview (/docs/cli) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Prisma CLI reference Location: Overview Prisma ORM ships with a unified Prisma CLI. One binary contains the ORM commands and the platform commands for [Prisma Composer](https://www.prisma.io/docs/composer), [Prisma Compute](https://www.prisma.io/docs/compute), Prisma Postgres, and object-store buckets. > [!NOTE] > Prisma ORM 7 users > > Prisma ORM 8 is a release candidate. Prisma ORM 7 remains fully supported; its docs live at [/orm/v7](https://www.prisma.io/docs/orm/v7) and its setup paths at [/v7/getting-started](https://www.prisma.io/docs/v7/getting-started). > > For what release candidate means, when the final release is expected, and how to stay on version 7, see [Release status](https://www.prisma.io/docs/orm/release-status). For the Prisma ORM 8 name of every Prisma ORM 7 API, see [Coming from Prisma ORM 7](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7). The Prisma ORM CLI ships in the `prisma` package. Run it without installing: #### bun ```bash bunx prisma --help bunx prisma contract emit ``` #### pnpm ```bash pnpm prisma --help pnpm prisma contract emit ``` #### yarn ```bash yarn prisma --help yarn prisma contract emit ``` #### npm ```bash npx prisma --help npx prisma contract emit ``` With `prisma` installed in your project, these commands become plain `prisma contract emit` and so on. For a full app scaffold, follow [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql). That path creates the project files and package scripts for you. This CLI reference is for the lower-level commands those scripts call. ## Platform commands [#platform-commands] The platform commands manage the services, databases, and buckets your app runs on. With [`deploy`](https://www.prisma.io/docs/cli/deploy) and [`dev`](https://www.prisma.io/docs/cli/dev) you deploy a [Prisma Composer](https://www.prisma.io/docs/composer) app to [Prisma Compute](https://www.prisma.io/docs/compute) and run it locally; with [`service`](https://www.prisma.io/docs/cli/service) and [`git`](https://www.prisma.io/docs/cli/git) you manage services, their versions, logs, and domains, and the repository connection. With [`postgres`](https://www.prisma.io/docs/cli/postgres) you spin up and manage [Prisma Postgres](https://www.prisma.io/docs/postgres) databases. With [`bucket`](https://www.prisma.io/docs/cli/bucket) you create blob-storage buckets and their access keys. [`auth`](https://www.prisma.io/docs/cli/auth), [`project`](https://www.prisma.io/docs/cli/project), and [`branch`](https://www.prisma.io/docs/cli/branch) handle the account, project, and branch plumbing around them. Each command group has its own page in this section. Deployments are created by `deploy`, by a git push once you [set up deploy on push](https://www.prisma.io/docs/compute/deploy-on-push), or from the [Console](https://pris.ly/pdp), and each deploy produces a service **version**. ## Common workflows [#common-workflows] Platform users deploy apps, create databases, and provision buckets with the [platform commands](#platform-commands), while ORM users manage their schema, contracts, and migrations with the ORM and migration commands. ### Deploy an app [#deploy-an-app] Sign in, then build and deploy a [Prisma Composer](https://www.prisma.io/docs/composer) app. `deploy` creates the project on its first run, and a new project needs a region, set in the deploy config or as `PRISMA_REGION`; see [`deploy`](https://www.prisma.io/docs/cli/deploy). #### bun ```bash bunx prisma auth login bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm prisma auth login pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn prisma auth login yarn build yarn prisma deploy module.ts ``` #### npm ```bash npx prisma auth login npm run build npx prisma deploy module.ts ``` To deploy on every push, also connect the repository with [`git connect`](https://www.prisma.io/docs/cli/git) and add the deploy workflow; see [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push). Follow a deploy with [`service logs`](https://www.prisma.io/docs/cli/service) and manage the result with the [`service` commands](https://www.prisma.io/docs/cli/service). ### Create a database [#create-a-database] #### bun ```bash bunx prisma postgres create mydb bunx prisma postgres connection create mydb ``` #### pnpm ```bash pnpm prisma postgres create mydb pnpm prisma postgres connection create mydb ``` #### yarn ```bash yarn prisma postgres create mydb yarn prisma postgres connection create mydb ``` #### npm ```bash npx prisma postgres create mydb npx prisma postgres connection create mydb ``` `postgres create` prints a one-time connection URL; `postgres connection create` mints another when you need one. See [`postgres`](https://www.prisma.io/docs/cli/postgres). ### Create a bucket [#create-a-bucket] #### bun ```bash bunx prisma bucket create --name my-bucket bunx prisma bucket key create ``` #### pnpm ```bash pnpm prisma bucket create --name my-bucket pnpm prisma bucket key create ``` #### yarn ```bash yarn prisma bucket create --name my-bucket yarn prisma bucket key create ``` #### npm ```bash npx prisma bucket create --name my-bucket npx prisma bucket key create ``` `bucket key create` prints the key's one-time credentials. See [`bucket`](https://www.prisma.io/docs/cli/bucket). ### Start in an existing project [#start-in-an-existing-project] #### bun ```bash bunx prisma@latest orm init --target postgres --authoring psl bunx prisma contract emit bunx prisma db init --db "$DATABASE_URL" --advance-ref db ``` #### pnpm ```bash pnpm dlx prisma@latest orm init --target postgres --authoring psl pnpm prisma contract emit pnpm prisma db init --db "$DATABASE_URL" --advance-ref db ``` #### yarn ```bash yarn dlx prisma@latest orm init --target postgres --authoring psl yarn prisma contract emit yarn prisma db init --db "$DATABASE_URL" --advance-ref db ``` #### npm ```bash npx prisma@latest orm init --target postgres --authoring psl npx prisma contract emit npx prisma db init --db "$DATABASE_URL" --advance-ref db ``` `db init` and `db update` advance the [ref](https://www.prisma.io/docs/cli/migration-ref) named `db` on their own when you leave `--db` off and let the connection come from `prisma.config.ts`. Passing `--db` suppresses that, so add `--advance-ref db` to keep the ref current. Without it, `--db` leaves any existing `db` ref where it was, and a later [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) starts from that stale contract; if the ref was never created, `migration plan` refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` as soon as migrations exist on disk. ### Adopt an existing database [#adopt-an-existing-database] #### bun ```bash bunx prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma bunx prisma contract emit bunx prisma db sign --db "$DATABASE_URL" bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma pnpm prisma contract emit pnpm prisma db sign --db "$DATABASE_URL" pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma yarn prisma contract emit yarn prisma db sign --db "$DATABASE_URL" yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract infer --db "$DATABASE_URL" --output ./prisma/contract.prisma npx prisma contract emit npx prisma db sign --db "$DATABASE_URL" npx prisma db verify --db "$DATABASE_URL" ``` `db sign` also sets the `db` ref to the signed contract, so the next `migration plan` starts from the database you adopted. Pass `--no-advance-ref` to sign without moving a ref. ### Use checked-in migrations [#use-checked-in-migrations] #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_users bunx prisma migration status --db "$DATABASE_URL" bunx prisma db migrate --db "$DATABASE_URL" --advance-ref db bunx prisma db verify --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_users pnpm prisma migration status --db "$DATABASE_URL" pnpm prisma db migrate --db "$DATABASE_URL" --advance-ref db pnpm prisma db verify --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_users yarn prisma migration status --db "$DATABASE_URL" yarn prisma db migrate --db "$DATABASE_URL" --advance-ref db yarn prisma db verify --db "$DATABASE_URL" ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_users npx prisma migration status --db "$DATABASE_URL" npx prisma db migrate --db "$DATABASE_URL" --advance-ref db npx prisma db verify --db "$DATABASE_URL" ``` Plain `db migrate` never moves a ref; `--advance-ref db` is what keeps the next `migration plan` incremental. Leave it off in deploy and CI applies, where a repository ref should not move. ## Agent skills [#agent-skills] Prisma packages ship [agent skills](https://www.prisma.io/docs/ai/tools/skills): instructions that teach AI coding agents the installed version's commands and APIs. [`init`](https://www.prisma.io/docs/cli/init) prepares a repository once, and [`skills sync`](https://www.prisma.io/docs/cli/skills) keeps the installed copies matching your package versions. Install after `init`, so the `prisma` dev dependency it adds is in `node_modules`: #### bun ```bash bunx --bun prisma@latest init bun install bunx prisma skills sync ``` #### pnpm ```bash pnpm dlx prisma@latest init pnpm install pnpm prisma skills sync ``` #### yarn ```bash yarn dlx prisma@latest init yarn install yarn prisma skills sync ``` #### npm ```bash npx prisma@latest init npm install npx prisma skills sync ``` ## Other commands [#other-commands] A few commands ship without dedicated pages yet. `contract format` formats your PSL contract source in place, including a Prisma ORM 7 schema read through `prisma7Schema`, and `lsp` starts the Prisma ORM language server (spawned by editors, not run interactively; see [Editor support](https://www.prisma.io/docs/orm/contract-authoring/editor-support)). The `migration` group also has read-only inspection commands: `migration list` (on-disk migrations per [contract space](https://www.prisma.io/docs/orm/migrations/the-migration-graph#terms-used-on-this-page); `--space`, `--ascii`, `--legend`), `migration log` (executed history from the database ledger; `--db`, `--utc`, `--ascii`), `migration graph` (the chain of migrations; `--space`, `--dot` for Graphviz output, `--ascii`, `--legend`), and `migration check [target]` (artifact and graph integrity; `--space`). Run any of them with `--help` for the details. ## Global flags [#global-flags] Every command accepts the same set of output, prompt, and config flags. They are documented on [Global flags](https://www.prisma.io/docs/cli/global-flags). ## Related pages - [`Choose a Prisma ORM setup path`](https://www.prisma.io/docs/getting-started): Choose the fastest path to try Prisma ORM in a new or existing project. - [`Console`](https://www.prisma.io/docs/console): Learn how to use the Console to manage and integrate Prisma products into your application. - [`Introduction to Prisma ORM`](https://www.prisma.io/docs/prisma-orm): Prisma ORM 8 is a release candidate. - [`Local development`](https://www.prisma.io/docs/local-development): Run the whole Prisma stack on your machine, with your app on Bun, a local Prisma Postgres database, and local object storage. - [`Overview`](https://www.prisma.io/docs/guides): Practical, step-by-step guides for building with Prisma ORM and deploying to Prisma Compute, each one run end to end before it ships. # init (/docs/cli/init) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Prepare a repository for Prisma development. Location: CLI > init `init` prepares the current directory for Prisma development. It runs locally and calls no platform API. It does not scaffold the ORM: use [`orm init`](https://www.prisma.io/docs/cli/orm-init) to add the Prisma ORM config, contract, and runtime files to a project. `init` does four things: 1. Adds a `postinstall` script to `package.json` (`prisma skills sync || exit 0`), so the [Prisma agent skills](https://www.prisma.io/docs/cli/skills) resync on every install and upgrade. 2. Adds `prisma` to `devDependencies` in `package.json`, pinned to the CLI's version, unless it is already listed. It does not install it. 3. Scaffolds a `prisma.config.ts` recording which agents to install skills for, in the [`skills` config section](https://www.prisma.io/docs/cli/configuration#agent-skills). 4. Runs [`skills sync`](https://www.prisma.io/docs/cli/skills) once. It never prompts, always exits 0, and is safe to rerun: each step reports what is already done. A `prisma.config.ts`, `postinstall` script, or `prisma` entry that already exists is never edited. If `package.json` already has a different `postinstall` script, `init` reports it and tells you what to append instead of chaining or replacing it. ## Usage [#usage] #### bun ```bash bunx --bun prisma@latest init bun install ``` #### pnpm ```bash pnpm dlx prisma@latest init pnpm install ``` #### yarn ```bash yarn dlx prisma@latest init yarn install ``` #### npm ```bash npx prisma@latest init npm install ``` If `init` added `prisma`, run the install straight after, so the package is in `node_modules`. ## Options [#options] | Option | What it does | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--skills ` | Agents to install skills for, comma-separated: `claude`, `cursor`, `agents`, `devin`. Pass `none` to record that no agent skills are wanted. Default: all of them. | | `--postinstall`/`--no-postinstall` | Add the skills-sync `postinstall` hook. `--no-postinstall` skips that step. | ## Examples [#examples] #### bun ```bash bunx --bun prisma@latest init bunx --bun prisma@latest init --skills=claude,cursor bunx --bun prisma@latest init --skills=none bunx --bun prisma@latest init --no-postinstall ``` #### pnpm ```bash pnpm dlx prisma@latest init pnpm dlx prisma@latest init --skills=claude,cursor pnpm dlx prisma@latest init --skills=none pnpm dlx prisma@latest init --no-postinstall ``` #### yarn ```bash yarn dlx prisma@latest init yarn dlx prisma@latest init --skills=claude,cursor yarn dlx prisma@latest init --skills=none yarn dlx prisma@latest init --no-postinstall ``` #### npm ```bash npx prisma@latest init npx prisma@latest init --skills=claude,cursor npx prisma@latest init --skills=none npx prisma@latest init --no-postinstall ``` ## The scaffolded config [#the-scaffolded-config] `init` writes a minimal `prisma.config.ts` when none exists: ```typescript title="prisma.config.ts" import { definePrismaConfig } from "prisma/config"; export default definePrismaConfig({ skills: { agents: ["claude", "cursor", "agents", "devin"], }, }); ``` The `prisma/config` import resolves from your project's `node_modules`. Until `prisma` is installed, commands that read this file stop at `CLI.CONFIG_UNREADABLE`. See [Configuration](https://www.prisma.io/docs/cli/configuration). Only `init` writes the `postinstall` hook. [`orm init`](https://www.prisma.io/docs/cli/orm-init) also adds `prisma` to `devDependencies`. [`skills sync`](https://www.prisma.io/docs/cli/skills) never touches `package.json`. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # migration new (/docs/cli/migration-new) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Scaffold a Prisma ORM migration package for manual authoring. Location: CLI > migration new `migration new` creates a migration package with a `migration.ts` file for manual authoring. Use it when the generated plan is not enough and you want to write the migration operations yourself. The command is offline. It does not consult the database. ## Usage [#usage] #### bun ```bash bunx prisma migration new --name split-name ``` #### pnpm ```bash pnpm prisma migration new --name split-name ``` #### yarn ```bash yarn prisma migration new --name split-name ``` #### npm ```bash npx prisma migration new --name split-name ``` ## Options [#options] | Option | What it does | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--name ` | Sets the migration directory name suffix. | | `--from ` | Starts from the contract state an existing migration ends at: the `to` hash in that migration's `migration.json`, or a unique prefix of it. Without it, see [Where it starts](#where-it-starts). | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | `migration new --from` takes only a hash or a unique prefix of one. Unlike [`migration plan --from`](https://www.prisma.io/docs/cli/migration-plan#options), it does not accept a migration directory name or a ref name. It refuses `--from` when `migrations/app/` has no migrations, and refuses a prefix that matches more than one migration. Run `npx prisma migration list` to see the hashes of your migrations. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) compares it with the other commands. ## Where it starts [#where-it-starts] Without `--from`, `migration new` picks its starting point the way [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) does. The result depends on whether the [`db` ref](https://www.prisma.io/docs/cli/migration-ref#the-db-ref) exists and whether `migrations/app/` has migrations. The `db` ref is the file `migrations/app/refs/db.json`, which records the contract state you expect your local database to match. `migrations/app/` is the folder for your application's own migrations, and each extension package that ships migrations has its own folder beside it. * When the `db` ref exists and `migrations/app/` has migrations, it starts from the `db` ref. * When there are no migrations and no `db` ref, it starts from an empty database. * When migrations exist but there is no `db` ref, it refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN`. Pass `--from` with the `to` hash of the migration to build on, or point the `db` ref at the state your database is on. To find that state, run `npx prisma migration status --db "$DATABASE_URL" --json`: the `currentContract` of the `app` entry in `result.spaces` is the hash of the contract state your database matches. Then run `npx prisma migration ref set db `, where `` is that hash or the directory name of the migration that ends at it. * When the `db` ref exists but there are no migrations, it refuses with `MIGRATION.HASH_NOT_IN_GRAPH`, because there is no migration yet that ends at the state the `db` ref names. Run `migration plan` first. It writes a [baseline migration](https://www.prisma.io/docs/orm/migrations/the-migration-graph#baselines), a migration from an empty database to that state, and then `migration new` can start from it. `migration plan` reads that state from the copy saved under `migrations/snapshots/` by the command that set the `db` ref, and if the copy is missing, it fails and asks you to restore `migrations/snapshots/` from version control. Before `8.0.0-rc.12`, `migration new` started from the newest migration. Scripts that relied on that should pass `--from` with the newest migration's `to` hash. ## Examples [#examples] #### bun ```bash bunx prisma migration new --name split-name bunx prisma migration new --name custom-fk --from abc123 ``` #### pnpm ```bash pnpm prisma migration new --name split-name pnpm prisma migration new --name custom-fk --from abc123 ``` #### yarn ```bash yarn prisma migration new --name split-name yarn prisma migration new --name custom-fk --from abc123 ``` #### npm ```bash npx prisma migration new --name split-name npx prisma migration new --name custom-fk --from abc123 ``` ## After scaffolding [#after-scaffolding] Write the migration body in `migration.ts`, then run the file with Node so it emits `ops.json` and attests the package: ```bash node migration.ts ``` Inspect the result: #### bun ```bash bunx prisma migration show ``` #### pnpm ```bash pnpm prisma migration show ``` #### yarn ```bash yarn prisma migration show ``` #### npm ```bash npx prisma migration show ``` Apply the migration only after review: #### bun ```bash bunx prisma db migrate --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db migrate --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma db migrate --db "$DATABASE_URL" ``` #### npm ```bash npx prisma db migrate --db "$DATABASE_URL" ``` Manual migrations should still end at a contract state that matches the emitted contract. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # migration plan (/docs/cli/migration-plan) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Plan an on-disk migration from Prisma ORM contract changes. Location: CLI > migration plan `migration plan` compares the emitted contract against a starting contract and produces a new migration package with the required operations. The starting contract is whatever `--from` names, or the [ref](https://www.prisma.io/docs/cli/migration-ref) named `db` when `--from` is absent. With neither, what happens depends on the migrations already on disk: * **`migrations/app/` is empty.** The plan starts from an empty database and contains the full `CREATE` operations for the whole contract. This is the first migration in a new project. The command says so under its summary (`No db ref set — planning from an empty database`), and `--json` output carries `fromDefaulted: true`, so a plan that recreates a database you already have is easy to spot. If a database already exists, run `db init`, `db update`, or `db sign` first. * **Migrations already exist.** The command refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` rather than write a full-create package that no real database could apply. The error names the three exits: `migration ref set db ` to point the ref at the state your database is on, `--from ` to name the origin for this one plan, or `--from @empty` to plan from an empty database deliberately. Keep the `db` ref current and you never see the refusal. [`db init`](https://www.prisma.io/docs/cli/db-init) and [`db update`](https://www.prisma.io/docs/cli/db-update) advance it for you when you run them without `--db`; with `--db` you have to add `--advance-ref db`. [`db sign`](https://www.prisma.io/docs/cli/db-sign) advances it whenever it signs, `--db` or not. [`db migrate`](https://www.prisma.io/docs/cli/db-migrate) advances nothing unless you pass `--advance-ref db`. ### The automatic baseline [#the-automatic-baseline] Say `migrations/app/` has no migrations yet, the `db` ref is set, and `migrations/snapshots/` holds a copy of the contract the ref points at. Then `migration plan` writes a baseline: a migration from an empty database to the ref's contract. If your emitted contract has changed since then, the same run also writes a second migration with those changes. Expect one or two new directories in `git status`. `db migrate` never applies the baseline to a database that already records which contract it matches, such as one you ran `db sign` on. It applies only the migrations after the contract that database records. The baseline only creates things, because it is planned from an empty database, so it never removes data. The second migration can remove data. `migration plan` writes it without asking, and the command's output marks each destructive operation in it and warns that it may cause data loss. Review that migration before you apply it. If a migration already starts from the contract the `db` ref points at, `migration plan` writes a second migration that starts from the same contract, so the [migration history](https://www.prisma.io/docs/orm/migrations/the-migration-graph) splits into two branches. This happens, for example, after `db migrate` without `--advance-ref db`, which applies migrations but leaves the `db` ref where it was. `migration plan` still writes the migration and prints a warning. A database that has already run the existing migration then has no migration leading to your new contract, so `db migrate` on that database fails with `MIGRATION.PATH_UNREACHABLE`. To build on the newest migration instead, delete the directory this run wrote and plan again with `--from` set to the newest migration's directory name. The command is offline, so it does not need a database connection. ## Usage [#usage] #### bun ```bash bunx prisma migration plan --name add_users_table ``` #### pnpm ```bash pnpm prisma migration plan --name add_users_table ``` #### yarn ```bash yarn prisma migration plan --name add_users_table ``` #### npm ```bash npx prisma migration plan --name add_users_table ``` ## Options [#options] | Option | What it does | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--name ` | Sets the migration directory name suffix. | | `--from ` | Uses a specific starting contract state instead of the `db` ref. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, [`^`](#contract-references), or [`@empty`](#contract-references). `@db` is not accepted here, because `migration plan` is offline and cannot read a database. `@contract` is not accepted either. | | `--to ` | Sets the destination contract state. Defaults to the emitted contract. Accepts the same forms as `--from`, except `@empty`, which is refused as a destination. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ### Contract references [#contract-references] A ref name is a name you gave a contract state with [`migration ref set`](https://www.prisma.io/docs/cli/migration-ref), such as `prod`. `^` is a migration directory name followed by `^`, and it names the contract state before that migration, while the directory name alone names the state after it. `@empty` names the empty database, before any migration. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms for every command. ## Recommended flow [#recommended-flow] #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_users_table bunx prisma migration show ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_users_table pnpm prisma migration show ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_users_table yarn prisma migration show ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_users_table npx prisma migration show ``` Review the generated migration package before applying it with [`db migrate`](https://www.prisma.io/docs/cli/db-migrate). ## Planning a rollback [#planning-a-rollback] Because `--to` accepts `^` (the source contract of a migration), you can plan a migration that walks back a change: #### bun ```bash bunx prisma migration plan --to ^ --name rollback ``` #### pnpm ```bash pnpm prisma migration plan --to ^ --name rollback ``` #### yarn ```bash yarn prisma migration plan --to ^ --name rollback ``` #### npm ```bash npx prisma migration plan --to ^ --name rollback ``` ## When to use migration plan [#when-to-use-migration-plan] Use `migration plan` when your team wants database changes reviewed in version control. For local prototypes where review is not needed, [`db update`](https://www.prisma.io/docs/cli/db-update) is usually faster. If the generated migration is not the migration you want, use [`migration new`](https://www.prisma.io/docs/cli/migration-new) and author the migration manually. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # migration ref (/docs/cli/migration-ref) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Manage named Prisma ORM refs that point at contracts. Location: CLI > migration ref Use `migration ref` commands to manage named refs stored with your migration history. A ref maps a logical environment name, such as `staging` or `production`, to a contract hash. Other commands can then target that environment by name: [`db migrate --to production`](https://www.prisma.io/docs/cli/db-migrate), [`db update --to production`](https://www.prisma.io/docs/cli/db-update), or [`db sign production`](https://www.prisma.io/docs/cli/db-sign). Refs live on disk as `migrations/app/refs/.json`, so they are versioned with your migrations. The commands are offline. The contract a ref points at must already be part of the on-disk migration graph, which is why `migration ref set` does not accept `@db`: that token stands for the live database's marker, and the offline commands never read one. ## The db ref [#the-db-ref] The `db` ref has a special meaning: it records the contract state you expect your local database to match. When you run [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) without `--from`, Prisma ORM assumes you mean from the `db` ref, so as long as the ref is kept up to date, each plan contains only your latest change. The `db` ref is updated automatically in the following situations: * [`db init`](https://www.prisma.io/docs/cli/db-init) and [`db update`](https://www.prisma.io/docs/cli/db-update) update it when you run them without `--db`, so that the connection comes from `prisma.config.ts`. With `--db`, they leave it alone unless you also pass `--advance-ref db`. * [`db sign`](https://www.prisma.io/docs/cli/db-sign) updates it after a successful signature, with or without `--db`. `--no-advance-ref` turns that off. * [`db migrate --advance-ref db`](https://www.prisma.io/docs/cli/db-migrate) updates it after an apply. Plain `db migrate` never touches it, on purpose: a deploy or CI run should not change a file in your repository. You can also set it by hand with `migration ref set db `. ## Usage [#usage] #### bun ```bash bunx prisma migration ref set production 4cb4256 bunx prisma migration ref list bunx prisma migration ref delete production ``` #### pnpm ```bash pnpm prisma migration ref set production 4cb4256 pnpm prisma migration ref list pnpm prisma migration ref delete production ``` #### yarn ```bash yarn prisma migration ref set production 4cb4256 yarn prisma migration ref list yarn prisma migration ref delete production ``` #### npm ```bash npx prisma migration ref set production 4cb4256 npx prisma migration ref list npx prisma migration ref delete production ``` ## Subcommands [#subcommands] | Subcommand | What it does | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `set ` | Points a ref at a contract. The contract is a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash or prefix, another ref name, a migration directory name, or `^` for that migration's source contract. | | `list` | Lists every ref with the contract hash it points at and the invariants recorded against it. | | `delete ` | Deletes a ref. The contract it pointed at is untouched. | ## Example workflow [#example-workflow] #### bun ```bash bunx prisma migration ref set production 20260101T1000_add_user bunx prisma migration status --db "$DATABASE_URL" --to production bunx prisma db migrate --db "$DATABASE_URL" --to production ``` #### pnpm ```bash pnpm prisma migration ref set production 20260101T1000_add_user pnpm prisma migration status --db "$DATABASE_URL" --to production pnpm prisma db migrate --db "$DATABASE_URL" --to production ``` #### yarn ```bash yarn prisma migration ref set production 20260101T1000_add_user yarn prisma migration status --db "$DATABASE_URL" --to production yarn prisma db migrate --db "$DATABASE_URL" --to production ``` #### npm ```bash npx prisma migration ref set production 20260101T1000_add_user npx prisma migration status --db "$DATABASE_URL" --to production npx prisma db migrate --db "$DATABASE_URL" --to production ``` Use refs when you want to name the contract state an environment should match, instead of always applying up to the latest migration on disk. If you pass `--advance-ref ` to a command that changes the database, that command also points the ref at the state it applied, once it succeeds. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # migration show (/docs/cli/migration-show) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Inspect a Prisma ORM migration package. Location: CLI > migration show `migration show` displays the operations, statement preview, and metadata for one migration package. Use this command during review before applying a migration package. The command is offline. It does not consult the database. ## Usage [#usage] #### bun ```bash bunx prisma migration show ``` #### pnpm ```bash pnpm prisma migration show ``` #### yarn ```bash yarn prisma migration show ``` #### npm ```bash npx prisma migration show ``` ## Options [#options] | Argument or option | What it does | | ------------------ | ------------------------------------------------------------------------------------ | | `` | The migration to inspect: a directory name, a hash or hash prefix, a ref, or a path. | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ## Examples [#examples] #### bun ```bash bunx prisma migration show 20260101_100000_add_user bunx prisma migration show a1b2c3 bunx prisma migration show migrations/app/20260101_100000_add_user bunx prisma migration show 20260101_100000_add_user --json ``` #### pnpm ```bash pnpm prisma migration show 20260101_100000_add_user pnpm prisma migration show a1b2c3 pnpm prisma migration show migrations/app/20260101_100000_add_user pnpm prisma migration show 20260101_100000_add_user --json ``` #### yarn ```bash yarn prisma migration show 20260101_100000_add_user yarn prisma migration show a1b2c3 yarn prisma migration show migrations/app/20260101_100000_add_user yarn prisma migration show 20260101_100000_add_user --json ``` #### npm ```bash npx prisma migration show 20260101_100000_add_user npx prisma migration show a1b2c3 npx prisma migration show migrations/app/20260101_100000_add_user npx prisma migration show 20260101_100000_add_user --json ``` ## What to check [#what-to-check] Before applying, review: * the source and destination contract hashes * the planned operations * generated SQL statements or operation payloads * the migration's metadata ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # migration status (/docs/cli/migration-status) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Show the Prisma ORM migration path and pending status. Location: CLI > migration status The database stores a marker, a row that says which version of your contract it currently matches. `migration status` compares that marker with a target version and lists the migrations in between. The target is the contract you last ran `contract emit` on, unless `--to` names another. Use it before and after [`db migrate`](https://www.prisma.io/docs/cli/db-migrate), and when debugging why an environment is not at the expected contract state. ## Usage [#usage] #### bun ```bash bunx prisma migration status --db "$DATABASE_URL" ``` #### pnpm ```bash pnpm prisma migration status --db "$DATABASE_URL" ``` #### yarn ```bash yarn prisma migration status --db "$DATABASE_URL" ``` #### npm ```bash npx prisma migration status --db "$DATABASE_URL" ``` ## Options [#options] | Option | What it does | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--db ` | Connects to the database. | | `--space ` | Narrows output to a single [contract space](#contract-spaces), named by its directory under `migrations/`. | | `--to ` | Sets the target contract state. Accepts a [contract reference](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms): a hash, a hash prefix, a [ref name](#contract-references), a migration directory name, [`^`](#contract-references), [`@contract`](#contract-references), [`@db`](#contract-references), or [`@empty`](#contract-references). | | `--from ` | Sets the origin contract state, and accepts the same forms as `--to`. With `--from`, the command computes the path offline and does not need a database. It needs one when `--from` or `--to` is `@db`, because it then reads the marker from the database. | | `--legend` | Prints a key to the symbols in the rows (`○`, the arrows, `✓`, `⧗`, `∅`) and to the labels. | | `--ascii` | Uses ASCII glyphs (pipe-friendly). | | `--config ` | Read this config file instead of `./prisma.config.ts`. | | `--json` | Prints a machine-readable result. | ### Contract spaces [#contract-spaces] Prisma ORM keeps one migration history for your own models, called the `app` space, in `migrations/app/`, and one for each [extension package](https://www.prisma.io/docs/orm/extensions/using-extensions) that ships its own migrations, such as pgvector. Each of these histories is a [contract space](https://www.prisma.io/docs/orm/migrations/the-migration-graph#terms-used-on-this-page). Pass `--space app` to see only your own migrations. ### Contract references [#contract-references] A ref name is a name you gave a contract state with [`migration ref set`](https://www.prisma.io/docs/cli/migration-ref), such as `prod`. `^` is a migration directory name followed by `^`, and it names the contract state before that migration, while the directory name alone names the state after it. `@contract` names the contract in `contract.json`, which `contract emit` writes. `@db` names the contract state that the database's marker records. `@empty` names the empty database, before any migration. [Contract references each command accepts](https://www.prisma.io/docs/orm/migrations/the-migration-graph#contract-reference-forms) lists the forms for every command. ## Examples [#examples] #### bun ```bash bunx prisma migration status --db "$DATABASE_URL" bunx prisma migration status --to production bunx prisma migration status --from abc123 --to production bunx prisma migration status --ascii ``` #### pnpm ```bash pnpm prisma migration status --db "$DATABASE_URL" pnpm prisma migration status --to production pnpm prisma migration status --from abc123 --to production pnpm prisma migration status --ascii ``` #### yarn ```bash yarn prisma migration status --db "$DATABASE_URL" yarn prisma migration status --to production yarn prisma migration status --from abc123 --to production yarn prisma migration status --ascii ``` #### npm ```bash npx prisma migration status --db "$DATABASE_URL" npx prisma migration status --to production npx prisma migration status --from abc123 --to production npx prisma migration status --ascii ``` ## Reading the result [#reading-the-result] ### Sample output [#sample-output] When you do not pass `--db`, the command connects to `db.connection` in `prisma.config.ts`. With one migration applied and one pending, `npx prisma migration status` prints: ```text │ migrations: migrations │ database: postgresql://****@localhost:54329/gaps ○ a4c3fa7 @contract │↑ 20260929T1558_add_user_phone 91e7f9f → a4c3fa7 1 ops ⧗ pending ○ 91e7f9f @db (db) │↑ 20260929T1558_baseline ∅ → 91e7f9f 6 ops ✓ applied ○ ∅ ⚠ 1 pending — run `prisma db migrate --to a4c3fa7fc3b2` ``` Read the drawing from the bottom up, because the earliest state is the bottom row. `∅` is the empty database, before any migration. `@contract` marks the contract in `contract.json`. `@db` marks the contract state that the database's marker records. `(db)` is the `db` [ref](https://www.prisma.io/docs/cli/migration-ref), the file `migrations/app/refs/db.json`, which records the contract state your development database is at. [`db init`](https://www.prisma.io/docs/cli/db-init) writes that file, and so does `db migrate --advance-ref db`. In this output `@db` and `(db)` are on the same row, because the marker and the `db` ref name the same state. [Check before, preview, then apply](https://www.prisma.io/docs/orm/migrations/applying-a-migration#check-before-preview-then-apply) explains each row and each label in more detail. The last line suggests the command to run next. It names the pending state by the first 12 characters of its hash, `a4c3fa7fc3b2`, which is the same hash the drawing shortens to 7 characters, `a4c3fa7`. With `--db`, status reads the marker from the database and compares it with the target. With `--from`, it starts from the state you name instead. It then needs no database, unless `--from` or `--to` is `@db`. ### Warnings [#warnings] When the command warns with `MIGRATION.MARKER_NOT_IN_HISTORY`, the marker in the database records a contract state that is not in your migration history. This is one kind of drift, and the next `db migrate` on that database fails with `MIGRATION.MARKER_MISMATCH`. To fix it, see [Drift](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery#drift-when-the-database-isnt-where-migrations-left-it). ### JSON output [#json-output] With `--json`, the command prints one `"kind": "result"` line. Inside its `envelope`: * `result.summary` is the sentence the human output ends with, such as `Up to date` or a count of pending migrations. * `result.spaces[].migrations[]` lists each migration on disk with a `status`: `pending` (on the way to the target, not yet applied), `applied` (on the way to the target, already applied), or `null` (not on the way to the target). * `diagnostics[]`, beside `result` rather than inside it, lists each problem found. Each entry has a `code`, for example `MIGRATION.MARKER_NOT_IN_HISTORY`, a `summary`, and a `severity`, which this command always sets to `warn`. The exit code is 0 even when `diagnostics` is not empty. A non-zero exit code means the command itself failed, for example with `MIGRATION.REF_NOT_FOUND` when `--to` names a ref that does not exist. ## Related commands [#related-commands] The `migration` group has three more read-only views: `migration graph` draws the chain of migrations, `migration log` lists what has run, and `migration list` lists the migrations on disk. Run each with `--help` for details. Use [`db verify`](https://www.prisma.io/docs/cli/db-verify) after applying migrations to check the final database shape against the emitted contract. ## Related pages - [`auth`](https://www.prisma.io/docs/cli/auth): Sign in to your Prisma account from the CLI, sign out, and manage workspace sessions. - [`branch`](https://www.prisma.io/docs/cli/branch): List platform branches for a project. - [`bucket`](https://www.prisma.io/docs/cli/bucket): Create and manage object-store buckets. - [`Configuration`](https://www.prisma.io/docs/cli/configuration): Configure Prisma ORM CLI commands with prisma.config.ts and global flags. - [`contract emit`](https://www.prisma.io/docs/cli/contract-emit): Emit Prisma ORM contract artifacts. # orm init (/docs/cli/orm-init) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Initialize Prisma ORM files in a project. Location: CLI > orm init `orm init` scaffolds the Prisma ORM config, contract source, and runtime files inside an existing project, installs dependencies, and emits the contract. It gets you from zero to typed queries in one step. Follow [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql) when you want a complete new application template. Use `orm init` when you already have a project and want to add the lower-level Prisma ORM files. ## Usage [#usage] Run it interactively for a guided setup: #### bun ```bash bunx prisma@latest orm init ``` #### pnpm ```bash pnpm dlx prisma@latest orm init ``` #### yarn ```bash yarn dlx prisma@latest orm init ``` #### npm ```bash npx prisma@latest orm init ``` Or supply `--target` and `--authoring` for a fully scriptable run (CI, AI coding agents, automation): #### bun ```bash bunx prisma@latest orm init --yes --target postgres --authoring psl ``` #### pnpm ```bash pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### yarn ```bash yarn dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### npm ```bash npx prisma@latest orm init --yes --target postgres --authoring psl ``` ## Options [#options] | Option | What it does | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `--target ` | Sets the database target. Use `postgres` or `mongodb`. | | `--authoring ``` Replace the home page and add the other two pages: ```astro title="src/pages/index.astro" --- import Base from "../layouts/Base.astro"; ---

Ship faster with Acme

Everything your team needs to go from idea to production.

See pricing ``` ```astro title="src/pages/pricing.astro" --- import Base from "../layouts/Base.astro"; const plans = [ { name: "Starter", price: "$0", blurb: "For side projects." }, { name: "Team", price: "$29", blurb: "For growing teams." }, ]; ---

Pricing

    {plans.map((plan) => (
  • {plan.name} {plan.price}/month: {plan.blurb}
  • ))}
``` ```astro title="src/pages/about.astro" --- import Base from "../layouts/Base.astro"; ---

About Acme

We build tools for teams that ship.

``` Run `npm run dev` and open [http://localhost:4321](http://localhost:4321) to check the pages, then stop the dev server. ## 3. Add the Node adapter [#3-add-the-node-adapter] Compute needs a server entrypoint to run. A plain Astro build only produces HTML files, so add the Node adapter in standalone mode: #### bun ```bash bunx astro add node --yes ``` #### pnpm ```bash pnpm dlx astro add node --yes ``` #### yarn ```bash yarn dlx astro add node --yes ``` #### npm ```bash npx astro add node --yes ``` ```js title="astro.config.mjs" // @ts-check import { defineConfig } from 'astro/config'; import node from '@astrojs/node'; // https://astro.build/config export default defineConfig({ adapter: node({ mode: 'standalone' }) }); ``` Leave `output` unset. It defaults to `"static"`, so every page is still rendered at build time. The adapter adds a server that serves those files. Build to confirm: #### bun ```bash bun run build ``` #### pnpm ```bash pnpm run build ``` #### yarn ```bash yarn build ``` #### npm ```bash npm run build ``` ```text no-copy [build] output: "static" [build] mode: "server" [build] adapter: @astrojs/node [build] Building server entrypoints... prerendering static routes ├─ /about/index.html (+7ms) ├─ /pricing/index.html (+3ms) ├─ /index.html (+4ms) ✓ Completed in 50ms. [build] Server built in 815ms [build] Complete! ``` The pages land in `dist/client/` and the server lands in `dist/server/entry.mjs`. Compute runs that server. ## 4. Declare the app with Prisma Composer [#4-declare-the-app-with-prisma-composer] [Prisma Composer](https://www.prisma.io/docs/composer) describes what to deploy. Install it, plus the Prisma CLI as a dev dependency: #### bun ```bash bun add @prisma/composer @prisma/composer-prisma-cloud bun add --dev prisma ``` #### pnpm ```bash pnpm add @prisma/composer @prisma/composer-prisma-cloud pnpm add -D prisma ``` #### yarn ```bash yarn add @prisma/composer @prisma/composer-prisma-cloud yarn add --dev prisma ``` #### npm ```bash npm install @prisma/composer @prisma/composer-prisma-cloud npm install -D prisma ``` Declare the service and point it at the Astro server entry. `dir` is the build output directory, and `entry` is the server file inside it: ```ts title="service.ts" import node from "@prisma/composer/node"; import { compute } from "@prisma/composer-prisma-cloud"; export default compute({ name: "web", deps: {}, build: node({ module: import.meta.url, dir: "./dist", entry: "server/entry.mjs" }), }); ``` The root module is the app. Its name becomes the project name on Compute: ```ts title="module.ts" import { module } from "@prisma/composer"; import web from "./service.ts"; export default module("marketing-site", ({ provision }) => { provision(web); }); ``` The deploy config tells the CLI where to deploy. The `region` applies when the first deploy creates the project: ```ts title="prisma.config.ts" import { defineConfig as composer } from "@prisma/composer/config"; import { nodeBuild } from "@prisma/composer/node/control"; import { prismaCloud, prismaState } from "@prisma/composer-prisma-cloud/control"; import { definePrismaConfig } from "prisma/config"; export default definePrismaConfig({ composer: composer({ extensions: [prismaCloud({ region: "us-east-1" }), nodeBuild()], state: prismaState(), }), }); ``` Composer writes local state to `.alchemy/` and `.prisma-composer/`. Add both to `.gitignore`: ```bash title=".gitignore" /.alchemy /.prisma-composer ``` ## 5. Run it locally [#5-run-it-locally] Run the built site the way Compute will. No account is needed: #### bun ```bash bun run build bunx prisma dev module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma dev module.ts ``` #### yarn ```bash yarn build yarn prisma dev module.ts ``` #### npm ```bash npm run build npx prisma dev module.ts ``` ```text no-copy [dev] compute emulator ready at http://127.0.0.1:4300 ... {"kind":"endpoint","name":"web","url":"http://localhost:3032"} ``` Check each route against the URL `dev` printed (your port may differ): ```bash curl -s -o /dev/null -w "%{http_code}\n" http://localhost:3032/pricing ``` ```text no-copy 200 ``` Composer hands the server its port, so you do not set `PORT` or `HOST` yourself. Stop `dev` with `Ctrl+C`. ## 6. Deploy to Prisma Compute [#6-deploy-to-prisma-compute] Sign in once. It opens your browser: #### bun ```bash bunx prisma auth login ``` #### pnpm ```bash pnpm prisma auth login ``` #### yarn ```bash yarn prisma auth login ``` #### npm ```bash npx prisma auth login ``` Then build and deploy: #### bun ```bash bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn build yarn prisma deploy module.ts ``` #### npm ```bash npm run build npx prisma deploy module.ts ``` ```text no-copy Plan: 4 to create, 1 tasks ... marketing-site └─ web compute-service cps_abc123 https://abc123.ewr.prisma.build ... Done: 5 succeeded (16.8s) ``` The deploy creates a project named `marketing-site` in your workspace and prints the live URL. If a project with that name already exists and its deploy state cannot be verified, the deploy stops with `HostedStateBootstrapError`; rename the module in `module.ts` or pass `--name `. Verify the live site: ```bash curl -s https://abc123.ewr.prisma.build/ | grep -o '

.*

' ``` ```text no-copy

Ship faster with Acme

``` ## 7. Ship a change [#7-ship-a-change] Edit a page, then build and deploy again: ```astro title="src/pages/index.astro"

Ship faster with Acme

// [!code --]

Ship faster with Acme, now on Prisma Compute

// [!code ++] ``` #### bun ```bash bun run build bunx prisma deploy module.ts ``` #### pnpm ```bash pnpm run build pnpm prisma deploy module.ts ``` #### yarn ```bash yarn build yarn prisma deploy module.ts ``` #### npm ```bash npm run build npx prisma deploy module.ts ``` ```text no-copy Plan: 2 to update, 1 to replace, 1 tasks ... marketing-site └─ web compute-service cps_abc123 https://abc123.ewr.prisma.build ``` The deploy reuses the project and keeps the URL. Fetch it again and you get the new heading. To deploy on every push instead of from your terminal, see [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push). ## Common gotchas [#common-gotchas] **`ASSEMBLE.BUILD_FAILED`: no built entry at `dist/server/entry.mjs`.** Either the Node adapter is missing, so the build only wrote HTML files, or you ran `dev` or `deploy` before `npm run build`. Add the adapter (step 3), build, and run the command again. Composer never runs your build for you, so always build before `deploy`; otherwise it ships whatever is already in `dist/`. **Setting `output: "server"`.** You don't need it for a marketing site. It renders every page per request instead of serving prebuilt HTML. Leave `output` at its static default, and opt a single page into request-time rendering with `export const prerender = false` when you need one. ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Build a static Astro marketing site and deploy it to Prisma Compute. Follow https://www.prisma.io/docs/guides/compute/astro-marketing-site.md. 1. Scaffold: `npm create astro@latest -- marketing-site --template minimal --install --no-git --yes`, then work in `marketing-site`. 2. Build the pages I describe (default: a shared layout in `src/layouts/Base.astro` plus home, pricing, and about pages). Keep every page static; do not set `output: "server"`. 3. Run `npx astro add node --yes` so `astro.config.mjs` uses `node({ mode: "standalone" })`. Run `npm run build` and confirm `dist/server/entry.mjs` exists. 4. Install `@prisma/composer` and `@prisma/composer-prisma-cloud`, and `prisma` as a dev dependency. Add `service.ts`, `module.ts`, and `prisma.config.ts` exactly as the guide shows, and add `/.alchemy` and `/.prisma-composer` to `.gitignore`. 5. Run `npx prisma dev module.ts` in the background, curl each page at the printed URL and confirm a 200, then stop it. 6. Deploy: check `npx prisma auth whoami`; if I am not signed in, stop and ask me to run `npx prisma auth login`. Then run `npm run build` followed by `npx prisma deploy module.ts`, and curl the printed URL to confirm the home page renders. ``` Install the [Prisma Compute agent skill](https://www.prisma.io/docs/ai/tools/skills) so your agent knows the deploy workflow: #### bun ```bash bunx skills add prisma/skills --skill prisma-compute ``` #### pnpm ```bash pnpm dlx skills add prisma/skills --skill prisma-compute ``` #### yarn ```bash yarn dlx skills add prisma/skills --skill prisma-compute ``` #### npm ```bash npx skills add prisma/skills --skill prisma-compute ``` Prompts that map to this guide: * "Add a blog to my Astro marketing site and redeploy it to Prisma Compute." * "Put my Prisma Compute site on a custom domain." * "Set up deploy on push for this site." ## Next steps [#next-steps] * [Add a custom domain](https://www.prisma.io/docs/compute/domains) to the deployed site. * [Deploy on push](https://www.prisma.io/docs/compute/deploy-on-push) so every Git push deploys, with a preview for every branch. * [Use Prisma ORM with Astro](https://www.prisma.io/docs/guides/frameworks/astro) when the site needs data, like a waitlist form backed by Prisma Postgres. # Expand-and-contract migrations (/docs/guides/database/data-migration) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Replace a column without downtime using the expand and contract pattern, with the data backfill inside a Prisma ORM migration. Location: Guides > Database > Expand-and-contract migrations ## Introduction [#introduction] When you change a production schema, you want the data to stay consistent and the app to keep running. The expand and contract pattern gets you there in two reviewable steps: first add the new column and copy the data across (expand), then remove the old column once nothing reads it (contract). In this guide you replace a `published` boolean on a `Post` model with a `status` enum. In Prisma ORM the data move is not a separate script. A migration is a TypeScript file, and the backfill is one more operation in it, a `dataTransform` that runs in the same transaction as the schema change. Every command and output below was run against a local PostgreSQL database. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/database/data-migration](https://www.prisma.io/docs/guides/v7/database/data-migration). ## Prerequisites [#prerequisites] * [Node.js](https://nodejs.org) 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (24 recommended) * A PostgreSQL connection string for a development database, and one for the database you deploy to * Git, for the branch-per-migration workflow ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Perform an expand-and-contract migration with Prisma ORM, following https://www.prisma.io/docs/guides/database/data-migration.md. 1. In a project with a `Post` model that has a `published Boolean @default(false)` field, make sure Prisma ORM is set up (`npx prisma@latest orm init --yes --target postgres --authoring psl` if it is not), `DATABASE_URL` is in `.env`, and the current contract is applied as a migration (`npx prisma contract emit`, `npx prisma migration plan --name init`, `npx prisma db migrate --advance-ref db`). Run `npx prisma@latest init` so the Prisma agent skills are installed, and use them. 2. Expand: add `enum Status { @@type("pg/text@1") Draft = "Draft" InProgress = "InProgress" InReview = "InReview" Published = "Published" }` and `status Status @default(Draft)` to `Post`. Keep `published`. Run `contract emit` and `migration plan --name add_status`. 3. Open the planned `migration.ts` and append a `this.dataTransform(endContract, 'backfill-post-status', { check, run })` operation built with the typed SQL builder against the migration's own end contract: `check` selects `id` from `Post` where `published = true AND status != 'Published'` with `limit(1)`, `run` updates those rows to `status = 'Published'`. Recompile with `node /migration.ts`, confirm `ops.json` contains a `data` operation, then apply with `npx prisma db migrate --advance-ref db`. Verify with a query that every row with `published = true` now has `status = 'Published'`. 4. Update application code to read and write `status` instead of `published`. 5. Contract: remove `published` from the contract, run `contract emit`, `migration plan --name drop_published`, review the destructive warning, and apply with `npx prisma db migrate --advance-ref db`. 6. For the production database, run `npx prisma db migrate --db "$PRODUCTION_DATABASE_URL"`, adding `--show` first if you want to preview the route. Do not use `db update` against production. ``` ## 1. Set up the project [#1-set-up-the-project] If you already have a Prisma ORM project whose contract is applied through migrations, skip to [step 2](#2-expand-the-contract). This step builds the starting point: a `Post` model with a `published` boolean, an `init` migration, and three rows of data. Create a project and add Prisma ORM to it: ```bash mkdir expand-contract && cd expand-contract ``` #### bun ```bash bun init bunx prisma@latest orm init --yes --target postgres --authoring psl ``` #### pnpm ```bash pnpm init pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### yarn ```bash yarn init yarn dlx prisma@latest orm init --yes --target postgres --authoring psl ``` #### npm ```bash npm init npx prisma@latest orm init --yes --target postgres --authoring psl ``` `orm init` writes `prisma.config.ts`, `src/prisma/contract.prisma`, `src/prisma/db.ts`, and `.env.example`, installs `@prisma/orm-postgres` and the `prisma` CLI, and emits the contract once. There is nothing to generate afterwards: the runtime reads the emitted `contract.json` directly, so there is no `prisma generate` step and no client package to install. `npm init -y` sets `"type": "commonjs"`, and `orm init` warns that the generated `db.ts` needs ES modules. Change it to `"type": "module"` in `package.json`. Put your development connection string in `.env`. Both `prisma.config.ts` and `db.ts` load it: ```bash title=".env" DATABASE_URL="postgres://user:password@localhost:5432/expand_contract" ``` Replace the generated contract with the starting model: ```prisma title="src/prisma/contract.prisma" // use prisma-8 model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) } ``` Emit the contract, plan the first migration, and apply it. `--advance-ref db` records where your development database is, so the next `migration plan` knows what to diff against: #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name init bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name init pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name init yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name init npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (2 operation(s)) across 1 contract space(s) App space ├─ Create schema "public" ├─ Create table "Post" └─ marker e8804d8e27d8737475c7be0c538afd9665907df8b82c605bd5cd50926dd73fde ✔ Advanced ref "db" → e8804d8e27d8737475c7be0c538afd9665907df8b82c605bd5cd50926dd73fde ``` Seed a few posts so the migration has data to move. Node.js runs TypeScript directly: ```typescript title="src/seed.ts" import { db } from "./prisma/db.ts"; await db.orm.public.Post.create({ title: "Hello World", content: "First post", published: true }); await db.orm.public.Post.create({ title: "Draft ideas", content: null, published: false }); await db.orm.public.Post.create({ title: "Release notes", content: "v1.0", published: true }); const posts = await db.orm.public.Post.select("id", "title", "published").all(); console.log(posts); await db.close(); ``` ```bash node src/seed.ts ``` ```text no-copy [ { id: 1, title: 'Hello World', published: true }, { id: 2, title: 'Draft ideas', published: false }, { id: 3, title: 'Release notes', published: true } ] ``` ## 2. Expand the contract [#2-expand-the-contract] Create a branch for the expand step: ```bash git checkout -b expand-post-status ``` Add the `Status` enum and a `status` field. Leave `published` in place; the app keeps working against it until the contract step. Prisma ORM stores an enum as `text` with a `CHECK` constraint, and the `@@type` line says so: ```prisma title="src/prisma/contract.prisma" // use prisma-8 model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) status Status @default(Draft) // [!code ++] } enum Status { // [!code ++] @@type("pg/text@1") // [!code ++] Draft = "Draft" // [!code ++] InProgress = "InProgress" // [!code ++] InReview = "InReview" // [!code ++] Published = "Published" // [!code ++] } // [!code ++] ``` Emit the contract and plan the migration. Planning is offline; it diffs the emitted contract against the `db` ref and writes a migration directory: #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_status ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_status ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_status ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_status ``` ```text no-copy ✔ Planned 2 operation(s) migrations/app/20260910T1552_add_status ├─ Add column "status" to "Post" └─ Add check constraint "Post_status_check_eaa35e2a" on "Post" from: e8804d8e27d8737475c7be0c538afd9665907df8b82c605bd5cd50926dd73fde to: 0e9486dcd7f4216d673cbe0ec503edb638520dec9b0a8a22f48bb88edd1442c4 app space: migrations/app/20260910T1552_add_status ℹ DDL preview ALTER TABLE "public"."Post" ADD COLUMN "status" text DEFAULT 'Draft' NOT NULL; ALTER TABLE "public"."Post" ADD CONSTRAINT "Post_status_check_eaa35e2a" CHECK ("status" IN ('Draft', 'InProgress', 'InReview', 'Published')); ``` Your directory name carries your own timestamp. The planner did not ask for a backfill because the column has a default, so every existing row would become `Draft`. That is wrong for posts that are already published. The next step fixes it. ## 3. Add the data migration [#3-add-the-data-migration] Open `migrations/app/_add_status/migration.ts`. It is the file the planner wrote, and you edit it like any other TypeScript. Add the imports that wire up a typed SQL builder against this migration's own end contract, then append a `dataTransform` operation after the two schema operations: ```typescript title="migrations/app/20260910T1552_add_status/migration.ts" #!/usr/bin/env -S node import type { Contract as End } from '../../snapshots/0e9486dcd7f4216d673cbe0ec503edb638520dec9b0a8a22f48bb88edd1442c4/contract'; import endContractJson from '../../snapshots/0e9486dcd7f4216d673cbe0ec503edb638520dec9b0a8a22f48bb88edd1442c4/contract.json' with { type: 'json' }; import type { Contract as Start } from '../../snapshots/e8804d8e27d8737475c7be0c538afd9665907df8b82c605bd5cd50926dd73fde/contract'; import startContract from '../../snapshots/e8804d8e27d8737475c7be0c538afd9665907df8b82c605bd5cd50926dd73fde/contract.json' with { type: 'json' }; import { Migration, MigrationCLI, col, lit } from '@prisma/orm-postgres/migration'; import postgresAdapter from '@prisma/orm-postgres/adapter/runtime'; // [!code ++] import { sql } from '@prisma/orm-postgres/builder/runtime'; // [!code ++] import { createExecutionContext, createSqlExecutionStack } from '@prisma/orm-postgres/family-runtime'; // [!code ++] import postgresTarget, { PostgresContractSerializer } from '@prisma/orm-postgres/target/runtime'; // [!code ++] const endContract = new PostgresContractSerializer().deserializeContract(endContractJson) as End; // [!code ++] const db = sql({ // [!code ++] context: createExecutionContext({ // [!code ++] contract: endContract, // [!code ++] stack: createSqlExecutionStack({ target: postgresTarget, adapter: postgresAdapter }), // [!code ++] }), // [!code ++] rawCodecInferer: postgresAdapter.rawCodecInferer, // [!code ++] }); // [!code ++] export default class M extends Migration { override readonly startContractJson = startContract; override readonly endContractJson = endContractJson; override get operations() { return [ this.addColumn({ schema: 'public', table: 'Post', column: col('status', 'text', { notNull: true, default: lit('Draft'), codecRef: { codecId: 'pg/text@1' }, }), }), this.addCheckConstraint({ schema: 'public', table: 'Post', constraint: 'Post_status_check_eaa35e2a', expression: "\"status\" IN ('Draft', 'InProgress', 'InReview', 'Published')", }), this.dataTransform(endContract, 'backfill-post-status', { // [!code ++] check: () => // [!code ++] db.public.Post // [!code ++] .select('id') // [!code ++] .where((f, fns) => fns.and(fns.eq(f.published, true), fns.ne(f.status, 'Published'))) // [!code ++] .limit(1), // [!code ++] run: () => // [!code ++] db.public.Post // [!code ++] .update({ status: 'Published' }) // [!code ++] .where((f, fns) => fns.and(fns.eq(f.published, true), fns.ne(f.status, 'Published'))), // [!code ++] }), // [!code ++] ]; } } MigrationCLI.run(import.meta.url, M); ``` The snapshot paths in your file carry your own contract hashes; keep the ones the planner wrote. The planner writes the JSON import as `endContract`. Rename that import to `endContractJson`, as the version above does, and bind `endContract` to the deserialized contract, because the builder and `dataTransform` both need the deserialized form. Three things to notice: * `check` is a query that returns a row while work remains. Prisma compiles it twice: as an `EXISTS` precheck that decides whether the step runs, and as a `NOT EXISTS` postcheck that proves the step finished. * `run` is the update. Both closures are typed against the migration's own contract snapshot, which is why `f.status` type-checks even though the column does not exist in the database yet. * There is no `published: false` branch. Those rows take the column default, `Draft`, when `ADD COLUMN` runs. Recompile the migration. Running the file regenerates `ops.json`, the file `db migrate` actually executes, and re-signs `migration.json`: ```bash node migrations/app/20260910T1552_add_status/migration.ts ``` ```text no-copy Wrote ops.json + migration.json to /Users/you/expand-contract/migrations/app/20260910T1552_add_status ``` Review what the backfill compiled to. `migration show` lists the operation, and `ops.json` holds the exact SQL: #### bun ```bash bunx prisma migration show 20260910T1552_add_status ``` #### pnpm ```bash pnpm prisma migration show 20260910T1552_add_status ``` #### yarn ```bash yarn prisma migration show 20260910T1552_add_status ``` #### npm ```bash npx prisma migration show 20260910T1552_add_status ``` ```text no-copy ✔ 20260910T1552_add_status 3 operation(s) ├─ Add column "status" to "Post" ├─ Add check constraint "Post_status_check_eaa35e2a" on "Post" └─ Data transform: backfill-post-status ``` ```json title="migrations/app/20260910T1552_add_status/ops.json (the data operation)" no-copy { "id": "data_migration.backfill-post-status", "operationClass": "data", "precheck": [{ "sql": "SELECT EXISTS (SELECT \"id\" AS \"id\" FROM \"public\".\"Post\" WHERE (\"published\" = $1 AND \"status\" != $2) LIMIT 1) AS ok", "params": [true, "Published"] }], "execute": [{ "sql": "UPDATE \"public\".\"Post\" SET \"status\" = $1 WHERE (\"published\" = $2 AND \"status\" != $3)", "params": ["Published", true, "Published"] }], "postcheck": [{ "sql": "SELECT NOT EXISTS (SELECT \"id\" AS \"id\" FROM \"public\".\"Post\" WHERE (\"published\" = $1 AND \"status\" != $2) LIMIT 1) AS ok", "params": [true, "Published"] }] } ``` Commit `migration.ts`, `ops.json`, and `migration.json` together. Recompiling stores a hash of `ops.json` and `migration.json` inside `migration.json`. If either file changes after that, `db migrate` refuses to run the migration, and `npx prisma migration check` reports the same problem before you commit. ## 4. Apply the expand migration [#4-apply-the-expand-migration] Apply it to your development database. On PostgreSQL the whole run is one transaction, so the column, the constraint, and the backfill land together or not at all: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (3 operation(s)) across 1 contract space(s) App space ├─ Add column "status" to "Post" ├─ Add check constraint "Post_status_check_eaa35e2a" on "Post" ├─ Data transform: backfill-post-status └─ marker 0e9486dcd7f4216d673cbe0ec503edb638520dec9b0a8a22f48bb88edd1442c4 ✔ Advanced ref "db" → 0e9486dcd7f4216d673cbe0ec503edb638520dec9b0a8a22f48bb88edd1442c4 ``` Check the rows. The published posts moved to `Published`, and the draft kept the default: ```typescript title="src/posts.ts" import { db } from "./prisma/db.ts"; const posts = await db.orm.public.Post .select("id", "title", "published", "status") .orderBy((p) => p.id.asc()) .all(); console.log(posts); await db.close(); ``` ```bash node src/posts.ts ``` ```text no-copy [ { id: 1, title: 'Hello World', published: true, status: 'Published' }, { id: 2, title: 'Draft ideas', published: false, status: 'Draft' }, { id: 3, title: 'Release notes', published: true, status: 'Published' } ] ``` Both columns exist now, so this is the moment to move application code from `published` to `status`. The new field is typed as `'Draft' | 'InProgress' | 'InReview' | 'Published'` in the emitted contract: ```typescript const published = await db.orm.public.Post .select("id", "title", "status") .where((p) => p.status.eq("Published")) .orderBy((p) => p.id.asc()) .all(); ``` ```text no-copy [ { id: 1, title: 'Hello World', status: 'Published' }, { id: 3, title: 'Release notes', status: 'Published' } ] ``` Merge the branch and deploy it. Wait until nothing reads or writes `published` before you continue. ## 5. Contract the schema [#5-contract-the-schema] Create a branch for the cleanup: ```bash git checkout -b drop-published-column ``` Remove the old field: ```prisma title="src/prisma/contract.prisma" model Post { id Int @id @default(autoincrement()) title String content String? published Boolean @default(false) // [!code --] status Status @default(Draft) } ``` Emit and plan. The planner flags the drop as destructive, which is the review signal for this step: #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name drop_published ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name drop_published ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name drop_published ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name drop_published ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1554_drop_published └─ ⚠ Drop column "published" from "Post" ⚠ This migration contains destructive operations that may cause data loss. ℹ DDL preview ALTER TABLE "public"."Post" DROP COLUMN "published"; ``` Nothing to edit this time. Apply it: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s) App space ├─ ⚠ Drop column "published" from "Post" └─ marker 79567c619bcb9790bf476efc358730a10317c94fc352e908e19424fc6441350a ``` The data survived the round trip. Drop `published` from the select in `src/posts.ts` and run it again: ```text no-copy [ { id: 1, title: 'Hello World', status: 'Published' }, { id: 2, title: 'Draft ideas', status: 'Draft' }, { id: 3, title: 'Release notes', status: 'Published' } ] ``` `migration log` reads the database's own ledger of what ran: #### bun ```bash bunx prisma migration log ``` #### pnpm ```bash pnpm prisma migration log ``` #### yarn ```bash yarn prisma migration log ``` #### npm ```bash npx prisma migration log ``` ```text no-copy Applied at Migration Change Ops 2026-09-10 17:51:19 +02:00 20260910T1551_init ∅ → e8804d8 2 ops 2026-09-10 17:53:46 +02:00 20260910T1552_add_status e8804d8 → 0e9486d 3 ops 2026-09-10 17:55:39 +02:00 20260910T1554_drop_published 0e9486d → 79567c6 1 ops ``` ## 6. Deploy to production [#6-deploy-to-production] Production runs the same migrations from the same files; only the connection string changes. `db migrate` reads the database's marker, finds the path through the [migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph), and applies whatever is pending, backfill included. Your deploy pipeline needs one command: #### bun ```bash bunx prisma db migrate --db "$PRODUCTION_DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db migrate --db "$PRODUCTION_DATABASE_URL" ``` #### yarn ```bash yarn prisma db migrate --db "$PRODUCTION_DATABASE_URL" ``` #### npm ```bash npx prisma db migrate --db "$PRODUCTION_DATABASE_URL" ``` It refuses to run a migration whose `ops.json` or `migration.json` changed after it was compiled. If your pipeline deploys each environment to a ref, as [Applying a migration](https://www.prisma.io/docs/orm/migrations/applying-a-migration) shows, add `--to ` to that command. To see the route before it runs, add `--show`, which previews it without changing anything. Here is that preview against a production database that shipped the `init` migration and holds live rows, followed by the apply: ```text no-copy ℹ The following 2 migrations will run: 20260910T1552_add_status e8804d8 → 0e9486d 20260910T1554_drop_published 0e9486d → 79567c6 ``` ```text no-copy ✔ Applied 2 migration(s) (4 operation(s)) across 1 contract space(s) App space ├─ Add column "status" to "Post" ├─ Add check constraint "Post_status_check_eaa35e2a" on "Post" ├─ Data transform: backfill-post-status ├─ ⚠ Drop column "published" from "Post" └─ marker 79567c619bcb9790bf476efc358730a10317c94fc352e908e19424fc6441350a ``` In practice the two migrations reach production in separate deploys, one per branch, and the app moves from `published` to `status` between them. The commands are the same either way. Production never runs your TypeScript: the runner executes `ops.json`, which is plain data, so no application code runs with production credentials. ## Common gotchas [#common-gotchas] > [!WARNING] > Do not use `db update` for this change. It reconciles the database with the contract directly and does not carry data operations, so the backfill would not run. Use `migration plan` and `db migrate`, which is also what production needs: a reviewed, checked-in migration. > [!NOTE] > If `migration plan` stops with `MIGRATION.PLAN_ORIGIN_UNKNOWN`, the `db` ref is missing. That happens when you applied a migration with plain `db migrate`. Either re-run `npx prisma db migrate --advance-ref db`, or pass `--from ` to the plan. > [!NOTE] > If `db migrate` reports `MIGRATION.HASH_MISMATCH`, the migration's `ops.json` or `migration.json` changed after it was compiled, for example by a hand edit. Restore both files from Git, or move the edit into `migration.ts` and recompile with `node /migration.ts`, then apply again. Never edit `ops.json` by hand. > [!NOTE] > The `check` query must return rows, not a count. Prisma wraps it in `EXISTS` and `NOT EXISTS`; an aggregate always returns one row, so the step would never be skipped and the postcheck would always fail. ## Prompt your coding agent [#prompt-your-coding-agent] Run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once to install the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent and keep them matching your installed packages. Prompts that map to this guide: * "Using the prisma-8 skill, add a `dataTransform` to the latest migration that backfills `status` from `published`, recompile it, and show me the compiled SQL in ops.json." * "Plan a migration that drops the `published` column and explain the destructive warning." * "Preview what `db migrate` would run against staging and summarize the operations." ## Next steps [#next-steps] * [Editing a migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration): placeholders, `rawSql`, and the recompile loop in depth. * [Applying a migration](https://www.prisma.io/docs/orm/migrations/applying-a-migration): targets, refs, and what happens when a run fails. * [Rollbacks and recovery](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery): a rollback is one more planned migration, and a failed run is safe to retry. * [The migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph): why two branches with migrations merge cleanly. ## Related pages - [`Multiple databases`](https://www.prisma.io/docs/guides/database/multiple-databases): Connect one Next.js app to two PostgreSQL databases with Prisma ORM: one contract, config, and client per database, selected with the --config flag. - [`Schema management in teams`](https://www.prisma.io/docs/guides/database/schema-changes): Plan, merge, and apply Prisma ORM migrations when several developers change the schema at the same time. # Multiple databases (/docs/guides/database/multiple-databases) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Connect one Next.js app to two PostgreSQL databases with Prisma ORM: one contract, config, and client per database, selected with the --config flag. Location: Guides > Database > Multiple databases ## Introduction [#introduction] This guide shows you how to use two databases from one [Next.js](https://nextjs.org/) app with Prisma ORM. You give each database its own data contract, its own `prisma.config.ts`, and its own Prisma ORM client, then render data from both on one page. The same layout works for any number of databases and is a good fit for multi-tenant apps or for keeping unrelated data in separate databases. Every command and code block below was run against two local PostgreSQL databases, `users` and `posts`. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/database/multiple-databases](https://www.prisma.io/docs/guides/v7/database/multiple-databases). ## Prerequisites [#prerequisites] * [Node.js](https://nodejs.org) 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (24 recommended) * Two PostgreSQL connection strings, or nothing at all: `npx create-db@latest` can create a [Prisma Postgres](https://www.prisma.io/docs/postgres) database for you, run it twice for two * A [Vercel](https://vercel.com/) account if you want to deploy at the end ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Create a Next.js app that reads from two PostgreSQL databases with Prisma ORM, following https://www.prisma.io/docs/guides/database/multiple-databases.md. 1. Scaffold with `npx create-next-app@latest my-multi-db-app --yes`, then in the project run `npx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma`. Run `npx prisma@latest init` so the Prisma agent skills are installed, and use them. 2. Rename `prisma.config.ts` to `prisma.users.config.ts`, point it at `USERS_DATABASE_URL` and `./prisma/users/migrations`, and reduce `prisma/users/contract.prisma` to a `User` model. Create the same three files for the posts database: `prisma.posts.config.ts` reading `POSTS_DATABASE_URL`, `prisma/posts/contract.prisma` with a `Post` model that stores `authorId Int` (no relation across databases), and `prisma/posts/db.ts`. Export `usersDb` and `postsDb` from the two `db.ts` files. 3. Put both connection strings in `.env` (use the ones I give you, or create two Prisma Postgres databases with `npx create-db@latest` and show me the claim URLs). Add package scripts that run `prisma contract emit`, `prisma db init`, and `prisma db verify` once per config with `--config`, then run them. 4. Write a seed script that inserts two users and two posts, run it with `node prisma/seed.ts`, and rewrite `app/page.tsx` as a server component that lists users from `usersDb` and posts from `postsDb`, joining authors in application code. 5. Start `npm run dev` in the background, verify http://localhost:3000 renders both lists, stop the server, and confirm `npm run build` passes. ``` ## 1. Set up a Next.js project [#1-set-up-a-nextjs-project] Create a new Next.js app and accept the defaults (TypeScript, Tailwind CSS, App Router, no `src` directory): #### bun ```bash bunx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### pnpm ```bash pnpm dlx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### yarn ```bash yarn dlx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` #### npm ```bash npx create-next-app@latest my-multi-db-app cd my-multi-db-app ``` This guide starts from `create-next-app` and adds Prisma ORM with `orm init` instead of using the `next` template of `create-prisma`. That template wires a single database and declares it for Prisma Compute; here you want exactly the Prisma files, once per database, and nothing else. ## 2. Add Prisma ORM for the users database [#2-add-prisma-orm-for-the-users-database] Run `orm init` from the project root. The `--schema-path` flag puts the contract in a folder named after the database, and `orm init` places the client file next to it: #### bun ```bash bunx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### pnpm ```bash pnpm dlx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### yarn ```bash yarn dlx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` #### npm ```bash npx prisma@latest orm init --yes --target postgres --authoring psl --schema-path ./prisma/users/contract.prisma ``` The command installs `@prisma/orm-postgres` and `dotenv`, adds `prisma` as a dev dependency, sets `"type": "module"` in `package.json`, updates `tsconfig.json`, and writes these files: ```text no-copy prisma/users/contract.prisma prisma/users/db.ts prisma.config.ts prisma-8.md .env.example ``` There is no `prisma generate` and no generated client package. The emitted `contract.json` and `contract.d.ts` next to the contract are what the runtime and the type checker read. ### 2.1. Give the config a database-specific name [#21-give-the-config-a-database-specific-name] Prisma ORM commands read `./prisma.config.ts` by default and take a different file with the global [`--config`](https://www.prisma.io/docs/cli/global-flags) flag. Rename the generated config so each database has one: ```bash mv prisma.config.ts prisma.users.config.ts ``` Point it at the users contract, a users-only migrations folder, and a `USERS_DATABASE_URL` variable: ```typescript title="prisma.users.config.ts" import 'dotenv/config'; import { definePrismaConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/users/contract.prisma", migrations: { // [!code ++] dir: "./prisma/users/migrations", // [!code ++] }, // [!code ++] db: { connection: process.env.USERS_DATABASE_URL!, // [!code ++] }, }), }); ``` `migrations.dir` matters once you use checked-in migrations: without it both configs would write to the same `./migrations` folder. ### 2.2. Define the users contract [#22-define-the-users-contract] Replace the starter contract with a single `User` model: ```prisma title="prisma/users/contract.prisma" // use prisma-8 model User { id Int @id @default(autoincrement()) email String @unique name String? createdAt TimestamptzString @default(now()) } ``` ### 2.3. Name the client after the database [#23-name-the-client-after-the-database] Change `prisma/users/db.ts` to read `USERS_DATABASE_URL` and export a name you can tell apart from the second client: ```typescript title="prisma/users/db.ts" import 'dotenv/config'; import postgres from '@prisma/orm-postgres/runtime'; import type { Contract } from './contract.d'; import contractJson from './contract.json' with { type: 'json' }; export const usersDb = postgres({ // [!code ++] contractJson, url: process.env.USERS_DATABASE_URL!, // [!code ++] }); ``` `contract.d.ts` and `contract.json` do not exist yet; you emit them in step 4. ## 3. Add the posts database [#3-add-the-posts-database] The second database needs the same three files. Create them by hand; running `orm init` again would replace the files from step 2. ```bash mkdir -p prisma/posts ``` ```typescript title="prisma.posts.config.ts" import 'dotenv/config'; import { definePrismaConfig } from '@prisma/cli-engine'; import { defineConfig as ormConfig } from '@prisma/orm-postgres/config'; export default definePrismaConfig({ orm: ormConfig({ contract: "./prisma/posts/contract.prisma", migrations: { dir: "./prisma/posts/migrations", }, db: { connection: process.env.POSTS_DATABASE_URL!, }, }), }); ``` ```prisma title="prisma/posts/contract.prisma" // use prisma-8 model Post { id Int @id @default(autoincrement()) title String content String? authorId Int createdAt TimestamptzString @default(now()) } ``` `authorId` is a plain integer, not a relation. A contract describes one database, and PostgreSQL cannot enforce a foreign key into another database, so the link between a post and its author is resolved in application code in step 5. ```typescript title="prisma/posts/db.ts" import 'dotenv/config'; import postgres from '@prisma/orm-postgres/runtime'; import type { Contract } from './contract.d'; import contractJson from './contract.json' with { type: 'json' }; export const postsDb = postgres({ contractJson, url: process.env.POSTS_DATABASE_URL!, }); ``` Now set both connection strings. Both config files and both clients load `.env` through `dotenv/config`, and Next.js loads it as well: ```bash title=".env" USERS_DATABASE_URL="postgres://user:password@localhost:5432/users" POSTS_DATABASE_URL="postgres://user:password@localhost:5432/posts" ``` `orm init` already added `.env` to `.gitignore`. Update `.env.example` with the two variable names so collaborators know what to set. ## 4. Emit the contracts and initialize both databases [#4-emit-the-contracts-and-initialize-both-databases] Each Prisma ORM command works on one config. Add scripts that run every command once per database so you do not have to type `--config` twice: ```json title="package.json" "scripts": { "dev": "next dev", "build": "next build", "start": "next start", "contract:emit": "prisma contract emit --config ./prisma.users.config.ts && prisma contract emit --config ./prisma.posts.config.ts", // [!code ++] "db:init": "prisma db init --config ./prisma.users.config.ts && prisma db init --config ./prisma.posts.config.ts", // [!code ++] "db:verify": "prisma db verify --config ./prisma.users.config.ts && prisma db verify --config ./prisma.posts.config.ts", // [!code ++] "db:update": "prisma db update --config ./prisma.users.config.ts && prisma db update --config ./prisma.posts.config.ts", // [!code ++] "db:seed": "node prisma/seed.ts", // [!code ++] "prebuild": "npm run contract:emit" // [!code ++] } ``` `prebuild` re-emits both contracts before every `next build`, locally and on Vercel, so the build never runs against stale artifacts. Emit the contract artifacts for both databases. This step is offline: #### bun ```bash bun run contract:emit ``` #### pnpm ```bash pnpm run contract:emit ``` #### yarn ```bash yarn contract:emit ``` #### npm ```bash npm run contract:emit ``` ```text no-copy ✔ Emitted contract.json and contract.d.ts │ contract: prisma/users/contract.json │ types: prisma/users/contract.d.ts ✔ Emitted contract.json and contract.d.ts │ contract: prisma/posts/contract.json │ types: prisma/posts/contract.d.ts ``` Create the tables in both databases and sign each one with its contract: #### bun ```bash bun run db:init ``` #### pnpm ```bash pnpm run db:init ``` #### yarn ```bash yarn db:init ``` #### npm ```bash npm run db:init ``` ```text no-copy │ contract: prisma/users/contract.json │ database: postgres://****@localhost:5432/users ✔ Applied 2 operation(s) across 1 contract space App space ├─ Create table "User" ├─ Add unique constraint on "User" (email) └─ marker f340195186d0acf8e9914097e58af0375650e7251358382aa9ad3ed5b724daf7 │ contract: prisma/posts/contract.json │ database: postgres://****@localhost:5432/posts ✔ Applied 1 operation(s) across 1 contract space App space ├─ Create table "Post" └─ marker a0c012793cc392569087d82d7ea41686c8e60586b63c7a131391b99717157804 ``` Each database now carries a marker with the hash of the contract it was initialized from. Confirm both match: #### bun ```bash bun run db:verify ``` #### pnpm ```bash pnpm run db:verify ``` #### yarn ```bash yarn db:verify ``` #### npm ```bash npm run db:verify ``` ```text no-copy │ contract: prisma/users/contract.json │ database: postgres://****@localhost:5432/users ✔ Database marker and schema match contract │ contract: prisma/posts/contract.json │ database: postgres://****@localhost:5432/posts ✔ Database marker and schema match contract ``` The marker is also what protects you from crossing the wires. Verifying the users contract against the posts database stops immediately: #### bun ```bash bunx prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### pnpm ```bash pnpm prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### yarn ```bash yarn prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` #### npm ```bash npx prisma db verify --config ./prisma.users.config.ts --db "$POSTS_DATABASE_URL" ``` ```text no-copy ✘ [CONTRACT.MARKER_MISMATCH] Hash mismatch why: Contract storageHash does not match database marker ``` ## 5. Query both databases from the app [#5-query-both-databases-from-the-app] ### 5.1. Seed both databases [#51-seed-both-databases] Create a script that writes through both clients. Node.js runs TypeScript directly, so no extra tooling is needed: ```typescript title="prisma/seed.ts" import { usersDb } from "./users/db.ts"; import { postsDb } from "./posts/db.ts"; async function main() { const alice = await usersDb.orm.public.User.create({ email: "alice@prisma.io", name: "Alice", }); const bob = await usersDb.orm.public.User.create({ email: "bob@prisma.io", name: "Bob", }); await postsDb.orm.public.Post.create({ title: "Hello from the posts database", content: "This row lives in the posts database.", authorId: alice.id, }); await postsDb.orm.public.Post.create({ title: "Two databases, one app", content: null, authorId: bob.id, }); console.log("Seeded", alice.email, "and", bob.email, "with one post each."); await usersDb.close(); await postsDb.close(); } main().catch((error) => { console.error(error); process.exit(1); }); ``` The `.ts` extensions in the imports are what Node.js needs to resolve the files. Allow them in `tsconfig.json`, otherwise `next build` fails its type check with `TS5097`: ```json title="tsconfig.json" { "compilerOptions": { "allowImportingTsExtensions": true // [!code ++] } } ``` Run the seed: #### bun ```bash bun run db:seed ``` #### pnpm ```bash pnpm run db:seed ``` #### yarn ```bash yarn db:seed ``` #### npm ```bash npm run db:seed ``` ```text no-copy Seeded alice@prisma.io and bob@prisma.io with one post each. ``` Both clients open their connection pool on the first query. The script closes them at the end so the process can exit. ### 5.2. Render data from both databases [#52-render-data-from-both-databases] Replace `app/page.tsx` with a server component that queries each client and joins posts to their authors in memory: ```tsx title="app/page.tsx" import { usersDb } from "@/prisma/users/db"; import { postsDb } from "@/prisma/posts/db"; export const dynamic = "force-dynamic"; export default async function Home() { const users = await usersDb.orm.public.User.select("id", "email", "name").all(); const posts = await postsDb.orm.public.Post .select("id", "title", "authorId") .orderBy((p) => p.id.asc()) .all(); const authorById = new Map(users.map((u) => [u.id, u])); return (

Multi-database showcase

Users come from one PostgreSQL database, posts from another.

Users

    {users.map((user) => (
  • {user.name} ({user.email})
  • ))}

Posts

    {posts.map((post) => (
  • {post.title}, by {authorById.get(post.authorId)?.name ?? "unknown"}
  • ))}
); } ``` Each client is typed by its own `contract.d.ts`: `usersDb.orm.public` only knows `User`, `postsDb.orm.public` only knows `Post`, and mixing them up is a type error. `force-dynamic` keeps Next.js from trying to render the page at build time, when no database is reachable. ### 5.3. Run the development server [#53-run-the-development-server] #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` ```text no-copy ▲ Next.js 16.3.4 (Turbopack) - Local: http://localhost:3000 - Environments: .env ✓ Ready in 894ms ``` Open [http://localhost:3000](http://localhost:3000). The page lists Alice and Bob under **Users** and the two posts under **Posts**, each attributed to its author. One request, two databases, no adapter or extra client package in between. Finally, confirm the production build passes. `prebuild` emits both contracts first: #### bun ```bash bun run build ``` #### pnpm ```bash pnpm run build ``` #### yarn ```bash yarn build ``` #### npm ```bash npm run build ``` ```text no-copy > prisma contract emit --config ./prisma.users.config.ts && prisma contract emit --config ./prisma.posts.config.ts ... ✓ Compiled successfully ``` ## 6. Deploy to Vercel [#6-deploy-to-vercel] The app needs two environment variables in production and nothing else that is Prisma-specific: `prebuild` runs on Vercel, and there is no client to generate and no engine binary to ship. 1. Push the project to a GitHub repository. If you do not have one yet, [create one on GitHub](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository), then run: ```bash git add . git commit -m "Next.js app with two Prisma 8 databases" git branch -M main git remote add origin https://github.com//.git git push -u origin main ``` 2. In the [Vercel dashboard](https://vercel.com/), follow [Import an existing project](https://vercel.com/docs/getting-started-with-vercel/import) and stop at the step where you configure the project, before clicking **Deploy**. 3. Expand **Environment variables** and add both connection strings: * **Key**: `USERS_DATABASE_URL`, **Value**: the users database connection string from your `.env` * **Key**: `POSTS_DATABASE_URL`, **Value**: the posts database connection string from your `.env` 4. Click **Deploy**. Vercel runs `npm run build`, which emits both contracts and builds the app. > [!WARNING] > Set both variables before the first deploy. Each config file reads its variable with a non-null assertion, and a missing one fails the first request to the page. Open the live URL. The page renders the same two lists from the same two databases. The Vercel deploy was not run while validating this guide; the `npm run build` it executes is the one from step 5. ## Common gotchas [#common-gotchas] > [!WARNING] > Commands without `--config` fail once the default config is gone. Running `npx prisma contract emit` with no `./prisma.config.ts` (or with one that has no `orm` section) stops with `[CONFIG.FILE_NOT_FOUND]` and a hint that the `orm` config section is absent. Use the package scripts from step 4, or pass `--config` explicitly. * **No relations across databases.** A contract covers one database. Keep a plain id column such as `authorId` on the side that references the other database, and join in application code. * **`npx prisma@latest init` writes a third config.** It creates a `prisma.config.ts` that holds only the `skills` section for agent skills. That is expected; the database commands keep using `--config` with the two database configs. * **Do not close a client per request.** In `page.tsx` and route handlers, never call `usersDb.close()`. The pools are shared across requests and close when the process exits. Only scripts such as `prisma/seed.ts` close them. * **`orm init` picks the package manager from your lockfile.** Run it after `create-next-app` has written `package-lock.json`, `pnpm-lock.yaml`, or `bun.lock`, or it may install with a different package manager than the one you use. ## Prompt your coding agent [#prompt-your-coding-agent] Run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once to install the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent and keep them matching your installed packages. Prompts that map to this guide: * "Using the prisma-8 skill, add a `Comment` model to `prisma/posts/contract.prisma`, emit with `--config ./prisma.posts.config.ts`, and update the posts database." * "Add a third database for billing following the same layout: `prisma.billing.config.ts`, `prisma/billing/contract.prisma`, `prisma/billing/db.ts`, and extend the package scripts." * "Write a route handler that creates a post for the signed-in user, validating that the `authorId` exists in the users database first." ## Next steps [#next-steps] * Change a contract, then run `npm run contract:emit` and `npm run db:update` to apply the change to both databases in development. For checked-in migrations, run [`migration plan`](https://www.prisma.io/docs/cli/migration-plan) and [`db migrate`](https://www.prisma.io/docs/cli/db-migrate) with the `--config` of the database you changed; each writes to its own `migrations.dir`. * [Learn the fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data): filtering, sorting, pagination, and writes. * [CLI configuration](https://www.prisma.io/docs/cli/configuration) covers everything `prisma.config.ts` can hold. * Splitting the app itself into packages? See [pnpm workspaces](https://www.prisma.io/docs/guides/deployment/pnpm-workspaces) and [Turborepo](https://www.prisma.io/docs/guides/deployment/turborepo). ## Related pages - [`Expand-and-contract migrations`](https://www.prisma.io/docs/guides/database/data-migration): Replace a column without downtime using the expand and contract pattern, with the data backfill inside a Prisma ORM migration. - [`Schema management in teams`](https://www.prisma.io/docs/guides/database/schema-changes): Plan, merge, and apply Prisma ORM migrations when several developers change the schema at the same time. # Schema management in teams (/docs/guides/database/schema-changes) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Plan, merge, and apply Prisma ORM migrations when several developers change the schema at the same time. Location: Guides > Database > Schema management in teams ## Introduction [#introduction] When a team works on one schema, two people change it at the same time. In Prisma ORM, migrations form a graph rather than a numbered list, so parallel changes do not force anyone to rename files or rebuild history. This guide shows how that works day to day: what to commit, how to incorporate a teammate's migration, and how to resolve the case where two branches planned a migration from the same starting point and both merged. Every command and every output below was run against a Git repository with three checkouts (two teammates and you), each with its own local PostgreSQL database. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/database/schema-changes](https://www.prisma.io/docs/guides/v7/database/schema-changes). ## Prerequisites [#prerequisites] * [Node.js](https://nodejs.org) 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (24 recommended) * A Prisma ORM project with a contract and at least one migration ([Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql) gives you one) * A PostgreSQL database per developer, reachable as `DATABASE_URL` * Basic familiarity with Git branches and merges * The [migration loop](https://www.prisma.io/docs/orm/migrations/how-migrations-work): `contract emit`, `migration plan`, `db migrate` ## Use with your agent [#use-with-your-agent] To delegate the pull-and-apply part of this guide to your coding agent, copy the prompt below and hand it over: ```text Bring my local database up to date with the migrations on this branch using Prisma ORM, following https://www.prisma.io/docs/guides/database/schema-changes.md. 1. Run `npx prisma migration status` and tell me what is pending. If it reports "Up to date", stop. 2. If it warns that there is no migration path from the database state to the contract, the graph has two branch tips. Run `npx prisma migration graph`, then plan one merge migration from each tip with `npx prisma migration plan --from --name merge_`, and show me both DDL previews before continuing. 3. Apply with `npx prisma db migrate --advance-ref db`, then run `npx prisma migration check` and `npx prisma db verify`. 4. If `migration plan` fails with MIGRATION.PLAN_ORIGIN_UNKNOWN, do not delete any migration directory. Find the contract state the database is on in the `npx prisma migration status` output, pass that hash with `--from`, and show me the error text. If you cannot tell which state the database is on, stop and ask me. ``` ## 1. Understand migration basics [#1-understand-migration-basics] ### 1.1. Migrations form a graph [#11-migrations-form-a-graph] Each migration directory records the contract state it starts `from` and the state it ends at, `to`, as hashes in its `migration.json`. Those links are the migration history. The timestamp in the directory name is for humans; nothing depends on it. That is why two developers can plan migrations from the same state on separate branches and merge without renaming anything. [The migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph) explains the model in full. Three commands answer the questions a team asks most: | Question | Command | Needs a database? | | ---------------------------------------------------- | ----------------------------- | ----------------- | | What does the history look like? | `npx prisma migration graph` | No | | Where is my database, and what is pending? | `npx prisma migration status` | Yes | | Do the migration files on this branch hold together? | `npx prisma migration check` | No | ### 1.2. Commit these files [#12-commit-these-files] Commit everything Prisma ORM needs to rebuild a database from scratch: * `src/prisma/contract.prisma`, the schema you author * `src/prisma/contract.json` and `src/prisma/contract.d.ts`, the emitted contract (generated, but committed; `orm init` marks them `linguist-generated` in `.gitattributes`) * `migrations/app/`, every migration directory: `migration.ts`, `ops.json`, and `migration.json` together * `migrations/snapshots/`, the contract snapshots migrations are typed against * `migrations/app/refs/`, named refs, including the `db` ref described next * `prisma.config.ts` Do not commit `.env`. `orm init` already lists it in `.gitignore`. ### 1.3. Where a plan starts [#13-where-a-plan-starts] `migration plan` is offline. It never asks the database which contract state it matches, so you have to tell it which state to plan from. It resolves the origin in this order: an explicit `--from`, otherwise the ref named `db` in `migrations/app/refs/db.json`, otherwise the empty database. Keep the `db` ref pointing at the state your local database matches by applying with `--advance-ref db`: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` Do that every time you apply, and every plan chains from the right place without flags. Section 3 shows what happens when it cannot. ### 1.4. The project this guide starts from [#14-the-project-this-guide-starts-from] The project was created with `npx prisma@latest orm init --yes --target postgres --authoring psl` and has this contract: ```prisma title="src/prisma/contract.prisma" // use prisma-8 model User { id Int @id @default(autoincrement()) email String @unique username String? name String? posts Post[] createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } model Post { id Int @id @default(autoincrement()) title String content String? author User @relation(fields: [authorId], references: [id]) authorId Int createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } ``` Its first migration was planned and applied on `main` with: #### bun ```bash bunx prisma migration plan --name init bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma migration plan --name init pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma migration plan --name init yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma migration plan --name init npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (6 operation(s)) across 1 contract space(s) App space ├─ Create schema "public" ├─ Create table "Post" ├─ Create table "User" ├─ Add unique constraint on "User" (email) ├─ Create index "Post_authorId_idx_e47547ed" on "Post" ├─ Add foreign key "Post_authorId_fkey" on "Post" └─ marker 91e7f9f035806fa2789a4d726ef7724cad434fd6b00014d47ebf12d6e6bb784e ✔ Advanced ref "db" → 91e7f9f035806fa2789a4d726ef7724cad434fd6b00014d47ebf12d6e6bb784e → Check every space against the database: prisma migration status ``` Every developer clones this repository, points `DATABASE_URL` in their `.env` at their own database, and runs `npx prisma db migrate` once to bring it to the same state. ## 2. Incorporate team changes [#2-incorporate-team-changes] ### 2.1. Pull, check, apply [#21-pull-check-apply] When a teammate merges a schema change, it arrives as three things: an edited `contract.prisma`, the re-emitted `contract.json` and `contract.d.ts`, and a new migration directory. Pull them, then ask Prisma which contract state your database matches: ```bash git pull ``` #### bun ```bash bunx prisma migration status ``` #### pnpm ```bash pnpm prisma migration status ``` #### yarn ```bash yarn prisma migration status ``` #### npm ```bash npx prisma migration status ``` If nothing changed, you see the whole history marked applied: ```text no-copy ○ 91e7f9f @contract @db (db) │↑ 20260910T1543_init ∅ → 91e7f9f 6 ops ✓ applied ○ ∅ ✔ Up to date ``` `@db` marks the contract state your database matches, `@contract` the emitted contract, and `(db)` the state the `db` ref names. When migrations are pending, the tree flags each one with `⧗ pending` and a summary line tells you how many. Apply them and update the `db` ref in one step: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` There is no generate step after applying: your typed client reads `contract.json`, which you just pulled. ### 2.2. Example scenario [#22-example-scenario] The rest of this guide follows three developers changing the same contract. Ania adds a `favoriteColor` field, Javier adds a `Tag` model, and you add a `bestPacmanScore` field: #### Before ```prisma title="src/prisma/contract.prisma" model User { id Int @id @default(autoincrement()) email String @unique username String? name String? posts Post[] createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } ``` #### After ```prisma title="src/prisma/contract.prisma" model User { id Int @id @default(autoincrement()) email String @unique username String? name String? favoriteColor String? // Added by Ania // [!code ++] bestPacmanScore Int? // Added by you // [!code ++] posts Post[] createdAt TimestamptzString @default(now()) updatedAt temporal.updatedAtString() } // Added by Javier // [!code ++] model Tag { // [!code ++] tagName String @id // [!code ++] tagCategory String // [!code ++] } // [!code ++] ``` ## 3. Handle concurrent changes [#3-handle-concurrent-changes] Ania and Javier both branch from the same commit on `main`, where the `db` ref points at the `init` state. ### 3.1. Ania adds a field [#31-ania-adds-a-field] On her branch, Ania adds the field: ```prisma title="src/prisma/contract.prisma" model User { /* ... */ favoriteColor String? } ``` Then she emits, plans, and applies to her own database: #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_favorite_color ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_favorite_color ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_favorite_color ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_favorite_color ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1546_add_favorite_color └─ Add column "favoriteColor" to "User" from: 91e7f9f035806fa2789a4d726ef7724cad434fd6b00014d47ebf12d6e6bb784e to: b29af3990b528c7a67809a2ac609305c59aa9c0af1ed0e4591cc0a727b38b1d1 app space: migrations/app/20260910T1546_add_favorite_color ℹ DDL preview ALTER TABLE "public"."User" ADD COLUMN "favoriteColor" text; ``` The `from:` line is the `init` state, taken from the `db` ref. She applies and commits: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s) App space ├─ Add column "favoriteColor" to "User" └─ marker b29af3990b528c7a67809a2ac609305c59aa9c0af1ed0e4591cc0a727b38b1d1 ✔ Advanced ref "db" → b29af3990b528c7a67809a2ac609305c59aa9c0af1ed0e4591cc0a727b38b1d1 ``` ```bash git add -A git commit -m "Add User.favoriteColor" ``` ### 3.2. Javier adds a model [#32-javier-adds-a-model] On his branch, from the same starting commit, Javier adds a model: ```prisma title="src/prisma/contract.prisma" model Tag { tagName String @id tagCategory String } ``` #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_tag_model ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_tag_model ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_tag_model ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_tag_model ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1547_add_tag_model └─ Create table "Tag" from: 91e7f9f035806fa2789a4d726ef7724cad434fd6b00014d47ebf12d6e6bb784e to: 18b24e59a8b291a37b50e0d82c7afb58b314a2b64920d530627c73bd6e2d9768 app space: migrations/app/20260910T1547_add_tag_model ℹ DDL preview CREATE TABLE "public"."Tag" ( "tagCategory" text NOT NULL, "tagName" text NOT NULL, PRIMARY KEY ("tagName") ); ``` His plan also starts `from:` the `init` state. He applies with `db migrate --advance-ref db` and commits. Both migrations now leave the same node and arrive at different ones. Neither developer knows about the other yet, and neither needs to. ### 3.3. Merge both branches [#33-merge-both-branches] Ania's branch merges into `main` first, as a fast-forward. Javier's merge conflicts: ```bash git merge javier/tag-model ``` ```text no-copy Auto-merging migrations/app/refs/db.json CONFLICT (content): Merge conflict in migrations/app/refs/db.json Auto-merging src/prisma/contract.d.ts CONFLICT (content): Merge conflict in src/prisma/contract.d.ts Auto-merging src/prisma/contract.json CONFLICT (content): Merge conflict in src/prisma/contract.json Auto-merging src/prisma/contract.prisma Automatic merge failed; fix conflicts and then commit the result. ``` Read the list carefully. `contract.prisma`, the file you author, merged cleanly: the two edits touch different parts of the schema. The three conflicts are all in files Prisma writes for you, and none of them needs hand editing: * `contract.json` and `contract.d.ts` are emitted from `contract.prisma`. Re-emit and they are regenerated from the merged source, conflict markers and all: #### bun ```bash bunx prisma contract emit ``` #### pnpm ```bash pnpm prisma contract emit ``` #### yarn ```bash yarn prisma contract emit ``` #### npm ```bash npx prisma contract emit ``` ```text no-copy ✔ Emitted contract.json and contract.d.ts storageHash: 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ``` * `migrations/app/refs/db.json` records the contract state a local database last matched. Each branch pointed it at its own state, and neither is the state your database matches now. Take either side; you will set it correctly when you apply in section 3.5. ```bash git checkout --ours migrations/app/refs/db.json ``` Do not commit yet. The graph is not finished. ### 3.4. What Prisma reports after the merge [#34-what-prisma-reports-after-the-merge] Draw the graph: #### bun ```bash bunx prisma migration graph ``` #### pnpm ```bash pnpm prisma migration graph ``` #### yarn ```bash yarn prisma migration graph ``` #### npm ```bash npx prisma migration graph ``` ```text no-copy ○ 1e5059c @contract ○ b29af39 (db) │↑ 20260910T1546_add_favorite_color 91e7f9f → b29af39 1 ops │ ○ 18b24e5 │ │↑ 20260910T1547_add_tag_model 91e7f9f → 18b24e5 1 ops │─╯ ○ 91e7f9f │↑ 20260910T1543_init ∅ → 91e7f9f 6 ops ○ ∅ 1 space(s), 4 contract(s), 3 migration(s) ``` Read it bottom-up. From `init` (`91e7f9f`) two branches leave: Ania's ends at `b29af39`, Javier's at `18b24e5`. The merged contract you just emitted, `1e5059c`, sits at the top with no edge arriving at it. No database can reach it yet. `migration status` draws the same graph and marks where your database is, which is still on `init` (`@db`): #### bun ```bash bunx prisma migration status ``` #### pnpm ```bash pnpm prisma migration status ``` #### yarn ```bash yarn prisma migration status ``` #### npm ```bash npx prisma migration status ``` ```text no-copy ○ 1e5059c @contract ○ b29af39 (db) │↑ 20260910T1546_add_favorite_color 91e7f9f → b29af39 1 ops │ ○ 18b24e5 │ │↑ 20260910T1547_add_tag_model 91e7f9f → 18b24e5 1 ops │─╯ ○ 91e7f9f @db │↑ 20260910T1543_init ∅ → 91e7f9f 6 ops ✓ applied ○ ∅ ⚠ No migration path from the database state (91e7f9f03580) to the application's contract (1e5059c1976c). Run `prisma migration plan --name ` to author one. ``` Do not follow that hint literally. Without `--from`, the planner takes the `db` ref as its origin, and after `git checkout --ours` that ref is Ania's tip, so `migration plan --name merge_schema` writes only the half of the merge her branch is missing. Javier's database would still have no path to the top. Had the ref still pointed at `init`, the planner would have started a third branch from the divergence point instead, with a warning that it forks the graph. Neither is the merge. Do not delete either branch's migration directory to get out of this: Ania's and Javier's databases have already applied those migrations. Name the origin yourself, once per tip. ### 3.5. Close the diamond [#35-close-the-diamond] Plan one merge migration from each tip. Each one carries the change its branch is missing, and both arrive at the merged contract: #### bun ```bash bunx prisma migration plan --from 20260910T1546_add_favorite_color --name merge_add_tag_model ``` #### pnpm ```bash pnpm prisma migration plan --from 20260910T1546_add_favorite_color --name merge_add_tag_model ``` #### yarn ```bash yarn prisma migration plan --from 20260910T1546_add_favorite_color --name merge_add_tag_model ``` #### npm ```bash npx prisma migration plan --from 20260910T1546_add_favorite_color --name merge_add_tag_model ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1550_merge_add_tag_model └─ Create table "Tag" from: b29af3990b528c7a67809a2ac609305c59aa9c0af1ed0e4591cc0a727b38b1d1 to: 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ``` #### bun ```bash bunx prisma migration plan --from 20260910T1547_add_tag_model --name merge_add_favorite_color ``` #### pnpm ```bash pnpm prisma migration plan --from 20260910T1547_add_tag_model --name merge_add_favorite_color ``` #### yarn ```bash yarn prisma migration plan --from 20260910T1547_add_tag_model --name merge_add_favorite_color ``` #### npm ```bash npx prisma migration plan --from 20260910T1547_add_tag_model --name merge_add_favorite_color ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1550_merge_add_favorite_color └─ Add column "favoriteColor" to "User" from: 18b24e59a8b291a37b50e0d82c7afb58b314a2b64920d530627c73bd6e2d9768 to: 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ``` `--from` accepts a migration directory name, as here, or a contract hash from the graph. The planner diffs the two contract snapshots and writes exactly the operations that are missing on that side. Review both DDL previews the way you would review any migration. The graph is now a diamond, and every database on either branch has a path to the top: #### bun ```bash bunx prisma migration graph ``` #### pnpm ```bash pnpm prisma migration graph ``` #### yarn ```bash yarn prisma migration graph ``` #### npm ```bash npx prisma migration graph ``` ```text no-copy ○ 1e5059c @contract │─╮ │↑│ 20260910T1550_merge_add_tag_model b29af39 → 1e5059c 1 ops │ │↑ 20260910T1550_merge_add_favorite_color 18b24e5 → 1e5059c 1 ops ○ │ b29af39 (db) │↑│ 20260910T1546_add_favorite_color 91e7f9f → b29af39 1 ops │ ○ 18b24e5 │ │↑ 20260910T1547_add_tag_model 91e7f9f → 18b24e5 1 ops │─╯ ○ 91e7f9f │↑ 20260910T1543_init ∅ → 91e7f9f 6 ops ○ ∅ 1 space(s), 5 contract(s), 5 migration(s) ``` Your own database is still on `init`, so two migrations are pending for it. Apply them and let `--advance-ref db` set the `db` ref to where the database actually ends up, which also settles the `db.json` conflict from section 3.3: #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 2 migration(s) (2 operation(s)) across 1 contract space(s) App space ├─ Add column "favoriteColor" to "User" ├─ Create table "Tag" └─ marker 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ✔ Advanced ref "db" → 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ``` The runner walked `init → add_favorite_color → merge_add_tag_model`. It could equally have walked the other side; both paths end at the same contract. Now commit the merge, the re-emitted contract, both merge migrations, and the `db` ref together: ```bash git add -A git commit -m "Merge javier/tag-model and plan merge migrations" ``` ### 3.6. Every database runs only the migrations it needs [#36-every-database-catches-up-on-its-own-path] Ania pulls `main`. Her database matches `b29af39`, so only the migration that adds Javier's table is pending for her: #### bun ```bash bunx prisma migration status ``` #### pnpm ```bash pnpm prisma migration status ``` #### yarn ```bash yarn prisma migration status ``` #### npm ```bash npx prisma migration status ``` ```text no-copy ○ 1e5059c @contract (db) │─╮ │↑│ 20260910T1550_merge_add_tag_model b29af39 → 1e5059c 1 ops ⧗ pending │ │↑ 20260910T1550_merge_add_favorite_color 18b24e5 → 1e5059c 1 ops ○ │ b29af39 @db │↑│ 20260910T1546_add_favorite_color 91e7f9f → b29af39 1 ops ✓ applied │ ○ 18b24e5 │ │↑ 20260910T1547_add_tag_model 91e7f9f → 18b24e5 1 ops │─╯ ○ 91e7f9f │↑ 20260910T1543_init ∅ → 91e7f9f 6 ops ✓ applied ○ ∅ ⚠ 1 pending: run `prisma db migrate --to 1e5059c1976c` ``` #### bun ```bash bunx prisma db migrate --advance-ref db ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db ``` #### yarn ```bash yarn prisma db migrate --advance-ref db ``` #### npm ```bash npx prisma db migrate --advance-ref db ``` ```text no-copy ✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s) App space ├─ Create table "Tag" └─ marker 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b ``` Javier does the same, and for him the pending migration is `merge_add_favorite_color`, which adds Ania's column. Nobody re-created anything, and the database's own ledger shows the route each one took: #### bun ```bash bunx prisma migration log ``` #### pnpm ```bash pnpm prisma migration log ``` #### yarn ```bash yarn prisma migration log ``` #### npm ```bash npx prisma migration log ``` ```text no-copy Applied at Migration Change Ops 2026-09-10 17:45:41 +02:00 20260910T1543_init ∅ → 91e7f9f 6 ops 2026-09-10 17:46:17 +02:00 20260910T1546_add_favorite_color 91e7f9f → b29af39 1 ops 2026-09-10 17:52:47 +02:00 20260910T1550_merge_add_tag_model b29af39 → 1e5059c 1 ops ``` ## 4. Integrate your changes [#4-integrate-your-changes] ### 4.1. Pull first [#41-pull-first] With `main` merged and your database on the merged state, add your own field. Always pull and apply before you plan, so your plan chains from the state everyone shares: ```bash git pull ``` #### bun ```bash bunx prisma migration status ``` #### pnpm ```bash pnpm prisma migration status ``` #### yarn ```bash yarn prisma migration status ``` #### npm ```bash npx prisma migration status ``` ### 4.2. Edit, emit, plan [#42-edit-emit-plan] ```prisma title="src/prisma/contract.prisma" model User { /* ... */ favoriteColor String? bestPacmanScore Int? } ``` #### bun ```bash bunx prisma contract emit bunx prisma migration plan --name add_best_pacman_score ``` #### pnpm ```bash pnpm prisma contract emit pnpm prisma migration plan --name add_best_pacman_score ``` #### yarn ```bash yarn prisma contract emit yarn prisma migration plan --name add_best_pacman_score ``` #### npm ```bash npx prisma contract emit npx prisma migration plan --name add_best_pacman_score ``` ```text no-copy ✔ Planned 1 operation(s) migrations/app/20260910T1554_add_best_pacman_score └─ Add column "bestPacmanScore" to "User" from: 1e5059c1976c2439863eae8ac13211916ca411614c917394c413346f0d00f60b to: 4961e5b3ceb00cb24a60245343f288095c5a00fc2344d02db1b68509a8b91440 app space: migrations/app/20260910T1554_add_best_pacman_score ℹ DDL preview ALTER TABLE "public"."User" ADD COLUMN "bestPacmanScore" int4; ``` No `--from` was needed: the `db` ref points at the merged state because the last apply advanced it. Check the `from:` line anyway. If it does not name the state you expect, stop and read [Common gotchas](#common-gotchas). ### 4.3. Apply and check [#43-apply-and-check] #### bun ```bash bunx prisma db migrate --advance-ref db bunx prisma migration check ``` #### pnpm ```bash pnpm prisma db migrate --advance-ref db pnpm prisma migration check ``` #### yarn ```bash yarn prisma db migrate --advance-ref db yarn prisma migration check ``` #### npm ```bash npx prisma db migrate --advance-ref db npx prisma migration check ``` ```text no-copy ✔ Applied 1 migration(s) (1 operation(s)) across 1 contract space(s) App space ├─ Add column "bestPacmanScore" to "User" └─ marker 4961e5b3ceb00cb24a60245343f288095c5a00fc2344d02db1b68509a8b91440 ✔ Advanced ref "db" → 4961e5b3ceb00cb24a60245343f288095c5a00fc2344d02db1b68509a8b91440 ``` ```text no-copy ✔ All checks passed ``` `migration check` is offline and verifies every migration's hash and the graph's integrity. Run it after you edit a migration by hand, so an `ops.json` or `migration.json` that no longer matches its hash shows up before you commit. A pipeline does not need it, because `db migrate` refuses such a migration too. `migration check` reads only the files, so it cannot tell you whether a database matches them. `db verify` does. Run `npx prisma db verify --db "$DATABASE_URL"` after `db migrate`, and it fails if the database's marker or its tables differ from the contract you emitted. A pipeline that migrates a shared database runs `db migrate` alone; it refuses a database whose marker is outside your history with `MIGRATION.MARKER_MISMATCH` before it runs anything. [Migrate a shared database from a workflow](https://www.prisma.io/docs/guides/integrations/github-actions#migrate-a-shared-database) shows the job. ### 4.4. Commit [#44-commit] Commit the same set of files your teammates did: ```bash git add src/prisma/contract.prisma src/prisma/contract.json src/prisma/contract.d.ts migrations git commit -m "Add User.bestPacmanScore" ``` The final graph on `main` is a diamond with one more step on top, and your application code can use all three changes right away: ```ts title="script.ts" const user = await db.orm.public.User.create({ email: "pacman@prisma.io", favoriteColor: "yellow", bestPacmanScore: 3333360, }); const tag = await db.orm.public.Tag.create({ tagName: "arcade", tagCategory: "games" }); ``` ## Common gotchas [#common-gotchas] > [!WARNING] > `migration plan` fails with `MIGRATION.PLAN_ORIGIN_UNKNOWN` when the project has migrations but no `db` ref and you passed no `--from`. This happens in a fresh clone that has never run `db migrate --advance-ref db`, or after a merge dropped `refs/db.json`. The error lists the three exits: > > ```text no-copy > ✘ [MIGRATION.PLAN_ORIGIN_UNKNOWN] Cannot determine the plan origin: migrations exist but no origin is named > why: Migrations exist on disk, but there is no `db` ref and no --from was given, so the plan origin would silently fall back to an empty database and the resulting migration would recreate everything the existing migrations already create. > → Point the db ref at the origin contract: prisma migration ref set db > → Plan from an explicit origin: prisma migration plan --from > → Plan from an empty database deliberately: prisma migration plan --from @empty > ``` > > Pick the first or second. `--from @empty` over an existing history is only right for a deliberate rebuild. > [!WARNING] > Never run `db update` against a database another developer, CI, or production shares. It reconciles the database to your contract directly and leaves no migration behind, so the next `db migrate` from a teammate finds a marker the graph does not know. Use `db update` only on your own throwaway database, and switch to `migration plan` before you open a pull request. > [!NOTE] > The merge conflict in `migrations/app/refs/db.json` is expected whenever two branches both applied with `--advance-ref db`. The file records where one developer's database was, so no merged value is "correct" for everyone. Resolve it with either side and let your next `db migrate --advance-ref db` write the truth. > [!NOTE] > Renaming a field plans as a destructive drop column plus add column, because the planner has no rename hint yet. Before a rename reaches a shared database, [edit the migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration) to replace the pair with a `rawSql` `ALTER TABLE ... RENAME COLUMN`, then recompile it with `node migration.ts`. ## Prompt your coding agent [#prompt-your-coding-agent] Run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once to install the [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent and keep them matching your installed packages. Prompts that map to this guide: * "Using the prisma-8 skill, check whether my database is behind the migrations on this branch and apply what is pending." * "Two branches both added migrations from the same state. Draw the migration graph and plan the merge migrations from each tip." * "Run `migration check` and explain any integrity failure before I open this pull request." ## Next steps [#next-steps] * [The migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph): refs, markers, and how `db migrate` finds a path * [Applying a migration](https://www.prisma.io/docs/orm/migrations/applying-a-migration): the status, preview, apply rhythm for staging and production * [Rollbacks and recovery](https://www.prisma.io/docs/orm/migrations/rollbacks-and-recovery): planning a backwards edge when a merged change has to come out * [Editing a migration](https://www.prisma.io/docs/orm/migrations/editing-a-migration): backfills and raw SQL when a schema change needs a data step ## Related pages - [`Expand-and-contract migrations`](https://www.prisma.io/docs/guides/database/data-migration): Replace a column without downtime using the expand and contract pattern, with the data backfill inside a Prisma ORM migration. - [`Multiple databases`](https://www.prisma.io/docs/guides/database/multiple-databases): Connect one Next.js app to two PostgreSQL databases with Prisma ORM: one contract, config, and client per database, selected with the --config flag. # Bun workspaces (/docs/guides/deployment/bun-workspaces) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Set up Prisma ORM in a Bun workspaces monorepo through a shared database package, seed it with Bun, and query it from a Next.js app in the same workspace. Location: Guides > Deployment > Bun workspaces ## Introduction [#introduction] This guide shows you how to use Prisma ORM in a [Bun workspaces](https://bun.sh/docs/install/workspaces) monorepo. You create a shared `database` package that owns the schema, the database client, and a seed script, then import that client from a Next.js app in the same workspace. Every workspace member shares one lockfile and one `bun install`, so the database package is wired into the app like any other dependency. Every command, file, and output below was run end to end against a live PostgreSQL database. > [!NOTE] > Using Prisma ORM 7? > > Prisma ORM 8 is a [release candidate](https://www.prisma.io/docs/orm/release-status). Prisma ORM 7 remains fully supported; the Prisma ORM 7 version of this guide is at [/guides/v7/deployment/bun-workspaces](https://www.prisma.io/docs/guides/v7/deployment/bun-workspaces). ## Prerequisites [#prerequisites] * [Bun](https://bun.sh/docs/installation) 1.1 or later (`bun --version`) * [Node.js](https://nodejs.org) 22.18 or newer on the 22 line, 24.11 or newer on the 24 line, or 26 or newer (24 recommended): Bun runs the scripts, and Next.js runs on Node.js * A PostgreSQL connection string, or nothing at all: `bunx create-db@latest` can create a [Prisma Postgres](https://www.prisma.io/docs/postgres) database for you ## Use with your agent [#use-with-your-agent] To delegate this guide to your coding agent, copy the prompt below and hand it over: ```text Create a Bun workspaces monorepo with a shared Prisma ORM database package and a Next.js app that queries it. 1. Create `my-monorepo` with a root `package.json` that sets `"workspaces": ["apps/*", "packages/*"]`, then create `apps/` and `packages/database/`. Give `packages/database` a `package.json` with `"name": "database"`, `"private": true`, `"type": "module"` and `"main": "index.ts"`. 2. In `packages/database`, run `bunx prisma@latest orm init --yes --target postgres --authoring psl`. Then run `bunx prisma@latest init` there so the Prisma agent skills are installed and stay current, and use them. Get a database connection string: use the one I give you, or create a Prisma Postgres database with `bunx create-db@latest` and show me the claim URL it prints. Write it as `DATABASE_URL` into `packages/database/.env`. 3. Add package scripts to `packages/database`: `db:init` (`prisma db init`), `db:update` (`prisma db update`), `db:seed` (`bun src/prisma/seed.ts`). Run `bun run db:init` there. 4. Create `packages/database/index.ts` that re-exports `db` from `./src/prisma/db`, and `packages/database/src/prisma/seed.ts` that upserts three users with `db.orm.public.User.upsert({ create, update: {}, conflictOn: { email } })` and ends with `await db.close()`, following https://www.prisma.io/docs/guides/deployment/bun-workspaces.md. Run `bun run db:seed`. 5. Add root scripts `dev`, `build`, `start`, `db:init`, `db:update` and `seed` that use `bun run --filter // [!code ++] ``` Finally, add a server-side check to redirect authenticated users away from this page. If a user is already signed in, they should be redirected to the dashboard instead. ```html title="src/pages/sign-up/index.astro" --- export const prerender = false; if (Astro.locals.user?.id) return Astro.redirect("/dashboard"); // [!code ++] --- Sign Up

Sign Up

Already have an account? Sign in here.

``` ### 7.2. Sign in page [#72-sign-in-page] This page allows existing users to authenticate. Start with the basic HTML structure in `src/pages/sign-in/index.astro`. ```html title="src/pages/sign-in/index.astro" --- export const prerender = false; --- Sign In

Sign In

``` Add a form with input fields for email and password. This form will collect the user's credentials. ```html title="src/pages/sign-in/index.astro" --- export const prerender = false; --- Sign In

Sign In

// [!code ++] // [!code ++] // [!code ++] // [!code ++]
// [!code ++]

Don't have an account? Sign up here.

// [!code ++]
``` Now add a script to handle form submission. Import the `authClient` and add an event listener that prevents default submission, extracts the form data, and calls the Better Auth sign-in method. ```html title="src/pages/sign-in/index.astro" --- export const prerender = false; --- Sign In

Sign In

Don't have an account? Sign up here.

// [!code ++] ``` Finally, add a server-side check to redirect authenticated users away from this page. If a user is already signed in, they should be redirected to the dashboard instead. ```html title="src/pages/sign-in/index.astro" --- export const prerender = false; if (Astro.locals.user?.id) return Astro.redirect("/dashboard"); // [!code ++] --- Sign In

Sign In

Don't have an account? Sign up here.

``` ### 7.3. Dashboard page [#73-dashboard-page] This is the protected page for authenticated users. Start with the basic HTML structure in `src/pages/dashboard/index.astro`. ```html title="src/pages/dashboard/index.astro" --- export const prerender = false; --- Dashboard

Dashboard

``` Add a server-side check to protect this route. If the user is not authenticated, redirect them to the sign-in page. ```html title="src/pages/dashboard/index.astro" --- export const prerender = false; if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); // [!code ++] --- Dashboard

Dashboard

``` Now display the authenticated user's information. The `Astro.locals.user` object contains the user data that was set by the middleware. ```html title="src/pages/dashboard/index.astro" --- export const prerender = false; if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); --- Dashboard

Dashboard

{JSON.stringify(Astro.locals.user, null, 2)}
// [!code ++]
``` Finally, add a sign-out button. Import the `authClient` and add a button that calls the sign-out method, allowing the user to log out and be redirected to the sign-in page. ```html title="src/pages/dashboard/index.astro" --- export const prerender = false; if (!Astro.locals.user?.id) return Astro.redirect("/sign-in"); --- Dashboard

Dashboard

{JSON.stringify(Astro.locals.user, null, 2)}
// [!code ++]
// [!code ++] ``` ### 7.4. Home page [#74-home-page] Finally, update the home page to link to the sign-up, sign-in, and dashboard pages. Replace the contents of `src/pages/index.astro` with the following: ```html title="src/pages/index.astro" --- export const prerender = false; --- Better Auth + Astro + Prisma

Better Auth + Astro + Prisma

{ Astro.locals.user ? (

Welcome back, {Astro.locals.user.name}!

Go to Dashboard
) : ( ) }
``` ## 8. Test it out [#8-test-it-out] Your application is now fully configured. 1. Start the development server to test it: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` 2. Navigate to `http://localhost:4321` in your browser. You should see the home page with "Sign Up" and "Sign In" links. 3. Click on **Sign Up**, create a new account, and you should be redirected to the dashboard. You can then sign out and sign back in. 4. To view the user data directly in your database, you can use Prisma Studio. #### bun ```bash bunx prisma studio ``` #### pnpm ```bash pnpm prisma studio ``` #### yarn ```bash yarn prisma studio ``` #### npm ```bash npx prisma studio ``` 5. This will open a new tab in your browser where you can see the `User`, `Session`, and `Account` tables and their contents. > [!TIP] > You now have a working authentication system built with Better Auth, Prisma, and Astro. ## Next steps [#next-steps] * Add support for social login or magic links * Implement password reset and email verification * Add user profile and account management pages * Deploy to Vercel or Netlify and secure your environment variables * Extend your Prisma schema with custom application models ## Further reading [#further-reading] * [Better Auth documentation](https://www.better-auth.com/docs) * [Prisma documentation](https://www.prisma.io/docs/orm/v7) * [Astro documentation](https://astro.build/docs) ## Related pages - [`Better Auth (with Next.js)`](https://www.prisma.io/docs/guides/authentication/better-auth/nextjs): Learn how to use Prisma ORM in a Next.js app with Better Auth # Better Auth (with Next.js) (/docs/guides/authentication/better-auth/nextjs) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Learn how to use Prisma ORM in a Next.js app with Better Auth Location: Guides > Authentication > Better Auth > Better Auth (with Next.js) > [!NOTE] > This guide uses Prisma ORM 7 > > The commands and code on this page target Prisma ORM 7, which remains fully supported. To start a new Prisma ORM 8 project, see [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql); to add Prisma ORM 8 to an existing app, see [Add Prisma ORM to an existing PostgreSQL database](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql). A Prisma ORM 8 version of this guide is not available yet: Better Auth's `prismaAdapter` requires Prisma Client, which Prisma ORM 8 replaces with the `@prisma/orm-postgres` runtime. ## Introduction [#introduction] [Better Auth](https://better-auth.com/) is an open-source authentication library for web applications. It's written in TypeScript, can be extended with plugins, and supports multiple database adapters, including Prisma. In this guide, you'll wire Better Auth into a brand-new [Next.js](https://nextjs.org/) app and persist users in a [Prisma Postgres](https://prisma.io/postgres) database. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/betterauth-nextjs). ## Prerequisites [#prerequisites] * [Node.js 22.12+ on 22.x or 24+](https://nodejs.org) * Basic familiarity with Next.js App Router and Prisma ## 1. Set up your project [#1-set-up-your-project] Create a new Next.js application: #### bun ```bash bunx create-next-app@latest betterauth-nextjs-prisma ``` #### pnpm ```bash pnpm dlx create-next-app@latest betterauth-nextjs-prisma ``` #### yarn ```bash yarn dlx create-next-app@latest betterauth-nextjs-prisma ``` #### npm ```bash npx create-next-app@latest betterauth-nextjs-prisma ``` It will prompt you to customize your setup. Choose the defaults: > [!NOTE] > * *Would you like to use TypeScript?* `Yes` > * *Would you like to use ESLint?* `Yes` > * *Would you like to use Tailwind CSS?* `Yes` > * *Would you like your code inside a `src/` directory?* `Yes` > * *Would you like to use App Router?* `Yes` > * *Would you like to use Turbopack?* `Yes` > * _Would you like to customize the import alias (`@/_`by default)?\*`No` Navigate to the project directory: ```bash cd betterauth-nextjs-prisma ``` These selections create a Next.js project with TypeScript for type safety, ESLint for code quality, and Tailwind CSS for styling. Using the `src/` directory and the App Router are common conventions for new Next.js applications. ## 2. Set up Prisma [#2-set-up-prisma] Next, you'll add Prisma to your project to manage your database. ### 2.1. Install Prisma and dependencies [#21-install-prisma-and-dependencies] Install the necessary Prisma packages. The dependencies differ slightly depending on whether you use Prisma Postgres with Accelerate or another database. #### bun ```bash bun add prisma@prev tsx @types/pg --dev ``` #### pnpm ```bash pnpm add prisma@prev tsx @types/pg --save-dev ``` #### yarn ```bash yarn add prisma@prev tsx @types/pg --dev ``` #### npm ```bash npm install prisma@prev tsx @types/pg --save-dev ``` #### bun ```bash bun add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### pnpm ```bash pnpm add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### yarn ```bash yarn add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### npm ```bash npm install @prisma/client@7 @prisma/adapter-pg dotenv pg ``` > [!NOTE] > If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/database-drivers). Once installed, initialize Prisma in your project: #### bun ```bash bunx --bun prisma init --output ../src/generated/prisma ``` #### pnpm ```bash pnpm prisma init --output ../src/generated/prisma ``` #### yarn ```bash yarn prisma init --output ../src/generated/prisma ``` #### npm ```bash npx prisma init --output ../src/generated/prisma ``` > [!NOTE] > `prisma init` creates the Prisma scaffolding and a local `DATABASE_URL`. In the next step, you will create a Prisma Postgres database and replace that value with a direct `postgres://...` connection string. This will create: * A `prisma` directory with a `schema.prisma` file * A `.env` file containing a local `DATABASE_URL` at the project root * An `output` directory for the generated Prisma Client as `better-auth/generated/prisma` Create a Prisma Postgres database and replace the generated `DATABASE_URL` in your `.env` file with the `postgres://...` connection string from the CLI output: #### bun ```bash bunx create-db ``` #### pnpm ```bash pnpm dlx create-db ``` #### yarn ```bash yarn dlx create-db ``` #### npm ```bash npx create-db ``` ### 2.2. Configure Prisma [#22-configure-prisma] Create a `prisma.config.ts` file in the root of your project with the following content: ```typescript title="prisma.config.ts" import "dotenv/config"; import { defineConfig, env } from "prisma/config"; export default defineConfig({ schema: "prisma/schema.prisma", migrations: { path: "prisma/migrations", }, datasource: { url: env("DATABASE_URL"), }, }); ``` > [!NOTE] > The `dotenv` package should already be installed as it's a Next.js dependency. If not, install it using: > > > > > #### bun > ```bash > bun add dotenv > ``` > > > #### pnpm > ```bash > pnpm add dotenv > ``` > > > #### yarn > ```bash > yarn add dotenv > ``` > > > #### npm > ```bash > npm install dotenv > ``` > > ### 2.3. Generate the Prisma client [#23-generate-the-prisma-client] Run the following command to create the database tables and generate the Prisma Client: #### bun ```bash bunx prisma generate ``` #### pnpm ```bash pnpm prisma generate ``` #### yarn ```bash yarn prisma generate ``` #### npm ```bash npx prisma generate ``` ### 2.4. Set up a global Prisma client [#24-set-up-a-global-prisma-client] In the `src` directory, create a `lib` folder and a `prisma.ts` file inside it. This file will be used to create and export your Prisma Client instance. ```bash mkdir -p src/lib touch src/lib/prisma.ts ``` Set up the Prisma client like this: ```tsx title="src/lib/prisma.ts" import { PrismaClient } from "@/generated/prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL!, }); const globalForPrisma = global as unknown as { prisma: PrismaClient; }; const prisma = globalForPrisma.prisma || new PrismaClient({ adapter, }); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; export default prisma; ``` > [!WARNING] > We recommend using a connection pooler (like [Prisma Accelerate](https://www.prisma.io/accelerate)) to manage database connections efficiently. > > If you choose not to use one, **avoid** instantiating `PrismaClient` globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections. ## 3. Set up Better Auth [#3-set-up-better-auth] Next, add Better Auth to handle sign-up, sign-in, and sessions. ### 3.1. Install and configure Better Auth [#31-install-and-configure-better-auth] First, install the Better Auth core package: #### bun ```bash bun add better-auth ``` #### pnpm ```bash pnpm add better-auth ``` #### yarn ```bash yarn add better-auth ``` #### npm ```bash npm install better-auth ``` Next, generate a secure secret that Better Auth will use to sign authentication tokens. This ensures your tokens cannot be messed with. #### bun ```bash bunx auth@latest secret ``` #### pnpm ```bash pnpm dlx auth@latest secret ``` #### yarn ```bash yarn dlx auth@latest secret ``` #### npm ```bash npx auth@latest secret ``` Copy the generated secret and add it, along with your application's URL, to your `.env` file: ```bash title=".env" # Better Auth BETTER_AUTH_SECRET=your-generated-secret # [!code ++] BETTER_AUTH_URL=http://localhost:3000 # [!code ++] # Prisma DATABASE_URL="your-database-url" ``` Now, create a configuration file for Better Auth. In the `src/lib` directory, create an `auth.ts` file: ```bash touch src/lib/auth.ts ``` In this file, you'll configure Better Auth to use the Prisma adapter, which allows it to persist user and session data in your database. You will also enable email and password authentication. ```ts title="src/lib/auth.ts" import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import prisma from "@/lib/prisma"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), }); ``` Better Auth also supports other sign-in methods like social logins (Google, GitHub, etc.), which you can explore in their [email and password documentation](https://www.better-auth.com/docs/authentication/email-password). ```ts title="src/lib/auth.ts" import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import prisma from "@/lib/prisma"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { // [!code ++] enabled: true, // [!code ++] }, // [!code ++] }); ``` > [!NOTE] > If your application runs on a port other than `3000`, you must add it to the `trustedOrigins` in your `auth.ts` configuration to avoid CORS errors during authentication requests. > > ```ts title="src/lib/auth.ts" > import { betterAuth } from "better-auth"; > import { prismaAdapter } from "better-auth/adapters/prisma"; > import prisma from "@/lib/prisma"; > > export const auth = betterAuth({ > database: prismaAdapter(prisma, { > provider: "postgresql", > }), > emailAndPassword: { > enabled: true, > }, > trustedOrigins: ["http://localhost:3001"], // [!code ++] > }); > ``` ### 3.2. Add Better Auth models to your schema [#32-add-better-auth-models-to-your-schema] Better Auth provides a CLI command to automatically add the necessary authentication models (`User`, `Session`, `Account`, and `Verification`) to your `schema.prisma` file. Run the following command: #### bun ```bash bunx auth generate ``` #### pnpm ```bash pnpm dlx auth generate ``` #### yarn ```bash yarn dlx auth generate ``` #### npm ```bash npx auth generate ``` > [!NOTE] > It will ask for confirmation to overwrite your existing Prisma schema. Select `y`. This will add the following models: ```prisma model User { id String @id name String email String emailVerified Boolean image String? createdAt DateTime updatedAt DateTime sessions Session[] accounts Account[] @@unique([email]) @@map("user") } model Session { id String @id expiresAt DateTime token String createdAt DateTime updatedAt DateTime ipAddress String? userAgent String? userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@unique([token]) @@map("session") } model Account { id String @id accountId String providerId String userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) accessToken String? refreshToken String? idToken String? accessTokenExpiresAt DateTime? refreshTokenExpiresAt DateTime? scope String? password String? createdAt DateTime updatedAt DateTime @@map("account") } model Verification { id String @id identifier String value String expiresAt DateTime createdAt DateTime? updatedAt DateTime? @@map("verification") } ``` ### 3.3. Migrate the database [#33-migrate-the-database] With the new models in your schema, you need to update your database. Run a migration to create the corresponding tables: #### bun ```bash bunx prisma migrate dev --name add-auth-models ``` #### pnpm ```bash pnpm prisma migrate dev --name add-auth-models ``` #### yarn ```bash yarn prisma migrate dev --name add-auth-models ``` #### npm ```bash npx prisma migrate dev --name add-auth-models ``` #### bun ```bash bunx prisma generate ``` #### pnpm ```bash pnpm prisma generate ``` #### yarn ```bash yarn prisma generate ``` #### npm ```bash npx prisma generate ``` ## 4. Set up the API routes [#4-set-up-the-api-routes] Better Auth needs an API endpoint to handle authentication requests like sign-in, sign-up, and sign-out. You'll create a catch-all API route in Next.js to handle all requests sent to `/api/auth/[...all]`. In the `src/app/api` directory, create an `auth/[...all]` folder structure and a `route.ts` file inside it: ```bash mkdir -p "src/app/api/auth/[...all]" touch "src/app/api/auth/[...all]/route.ts" ``` Add the following code to the newly created `route.ts` file. This code uses a helper from Better Auth to create Next.js-compatible `GET` and `POST` request handlers. ```ts import { auth } from "@/lib/auth"; import { toNextJsHandler } from "better-auth/next-js"; export const { POST, GET } = toNextJsHandler(auth); ``` Next, you'll need a client-side utility to interact with these endpoints from your React components. In the `src/lib` directory, create an `auth-client.ts` file: ```bash touch src/lib/auth-client.ts ``` Add the following code, which creates the React hooks and functions you'll use in your UI: ```ts import { createAuthClient } from "better-auth/react"; export const { signIn, signUp, signOut, useSession } = createAuthClient(); ``` ## 5. Set up your pages [#5-set-up-your-pages] Next, build the user interface for authentication. In the `src/app` directory, create the following folder structure: * `sign-up/page.tsx` * `sign-in/page.tsx` * `dashboard/page.tsx` ```bash mkdir -p src/app/{sign-up,sign-in,dashboard} touch src/app/{sign-up,sign-in,dashboard}/page.tsx ``` ### 5.1. Sign up page [#51-sign-up-page] First, create the basic `SignUpPage` component in `src/app/sign-up/page.tsx`. This sets up the main container and a title for your page. ```tsx title="src/app/sign-up/page.tsx" "use client"; export default function SignUpPage() { return (

Sign Up

); } ``` Next, import the necessary hooks from React and Next.js to manage state and navigation. Initialize the router and a state variable to hold any potential error messages. ```tsx title="src/app/sign-up/page.tsx" "use client"; import { useState } from "react"; // [!code ++] import { useRouter } from "next/navigation"; // [!code ++] export default function SignUpPage() { const router = useRouter(); // [!code ++] const [error, setError] = useState(null); // [!code ++] return (

Sign Up

); } ``` Now, import the `signUp` function from your Better Auth client and add the `handleSubmit` function. This function is triggered on form submission and calls the `signUp.email` method provided by Better Auth, passing the user's name, email, and password. ```tsx title="src/app/sign-up/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; //add-next-lin import { signUp } from "@/lib/auth-client"; export default function SignUpPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { // [!code ++] e.preventDefault(); // [!code ++] setError(null); // [!code ++] // [!code ++] const formData = new FormData(e.currentTarget); // [!code ++] // [!code ++] const res = await signUp.email({ // [!code ++] name: formData.get("name") as string, // [!code ++] email: formData.get("email") as string, // [!code ++] password: formData.get("password") as string, // [!code ++] }); // [!code ++] // [!code ++] if (res.error) { // [!code ++] setError(res.error.message || "Something went wrong."); // [!code ++] } else { // [!code ++] router.push("/dashboard"); // [!code ++] } // [!code ++] } // [!code ++] return (

Sign Up

); } ``` To inform the user of any issues, add an element that conditionally renders when the `error` state is not null. ```tsx title="src/app/sign-up/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { signUp } from "@/lib/auth-client"; export default function SignUpPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); const formData = new FormData(e.currentTarget); const res = await signUp.email({ name: formData.get("name") as string, email: formData.get("email") as string, password: formData.get("password") as string, }); if (res.error) { setError(res.error.message || "Something went wrong."); } else { router.push("/dashboard"); } } return (

Sign Up

{error &&

{error}

} // [!code ++]
); } ``` Finally, add the HTML form with input fields for the user's name, email, and password, and a submit button. ```tsx title="src/app/sign-up/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { signUp } from "@/lib/auth-client"; export default function SignUpPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); const formData = new FormData(e.currentTarget); const res = await signUp.email({ name: formData.get("name") as string, email: formData.get("email") as string, password: formData.get("password") as string, }); if (res.error) { setError(res.error.message || "Something went wrong."); } else { router.push("/dashboard"); } } return (

Sign Up

{error &&

{error}

}
{" "} // [!code ++] {" "} // [!code ++] {" "} // [!code ++] {" "} // [!code ++] {" "} // [!code ++]
{" "} // [!code ++]
); } ``` ### 5.2. Sign in page [#52-sign-in-page] For the sign-in page, start with the basic structure in `src/app/sign-in/page.tsx`. ```tsx title="src/app/sign-in/page.tsx" "use client"; export default function SignInPage() { return (

Sign In

); } ``` Now, add the state and router hooks, similar to the sign-up page. ```tsx title="src/app/sign-in/page.tsx" "use client"; import { useState } from "react"; // [!code ++] import { useRouter } from "next/navigation"; // [!code ++] export default function SignInPage() { const router = useRouter(); // [!code ++] const [error, setError] = useState(null); // [!code ++] return (

Sign In

); } ``` Add the `handleSubmit` function, this time importing and using the `signIn.email` method from Better Auth. ```tsx title="src/app/sign-in/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { signIn } from "@/lib/auth-client"; // [!code ++] export default function SignInPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { // [!code ++] e.preventDefault(); // [!code ++] setError(null); // [!code ++] // [!code ++] const formData = new FormData(e.currentTarget); // [!code ++] // [!code ++] const res = await signIn.email({ // [!code ++] email: formData.get("email") as string, // [!code ++] password: formData.get("password") as string, // [!code ++] }); // [!code ++] // [!code ++] if (res.error) { // [!code ++] setError(res.error.message || "Something went wrong."); // [!code ++] } else { // [!code ++] router.push("/dashboard"); // [!code ++] } // [!code ++] } // [!code ++] return (

Sign In

); } ``` Add the conditional error message display. ```tsx title="src/app/sign-in/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { signIn } from "@/lib/auth-client"; export default function SignInPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); const formData = new FormData(e.currentTarget); const res = await signIn.email({ email: formData.get("email") as string, password: formData.get("password") as string, }); if (res.error) { setError(res.error.message || "Something went wrong."); } else { router.push("/dashboard"); } } return (

Sign In

{error &&

{error}

} // [!code ++]
); } ``` Finally, add the form fields for email and password and a sign-in button. ```tsx title="src/app/sign-in/page.tsx" "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { signIn } from "@/lib/auth-client"; export default function SignInPage() { const router = useRouter(); const [error, setError] = useState(null); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); const formData = new FormData(e.currentTarget); const res = await signIn.email({ email: formData.get("email") as string, password: formData.get("password") as string, }); if (res.error) { setError(res.error.message || "Something went wrong."); } else { router.push("/dashboard"); } } return (

Sign In

{error &&

{error}

}
{" "} // [!code ++] {" "} // [!code ++] {" "} // [!code ++] {" "} // [!code ++]
{" "} // [!code ++]
); } ``` ### 5.3. Dashboard page [#53-dashboard-page] This is the protected page for authenticated users. Start with the basic component in `src/app/dashboard/page.tsx`. ```tsx title="src/app/dashboard/page.tsx" "use client"; export default function DashboardPage() { return (

Dashboard

); } ``` Import the `useSession` hook from your Better Auth client. This hook is the key to managing authentication state on the client side. It provides the session data and a pending status. ```tsx title="src/app/dashboard/page.tsx" "use client"; import { useRouter } from "next/navigation"; // [!code ++] import { useSession } from "@/lib/auth-client"; // [!code ++] export default function DashboardPage() { const router = useRouter(); // [!code ++] const { data: session, isPending } = useSession(); // [!code ++] return (

Dashboard

); } ``` To protect this route, use a `useEffect` hook. This effect checks if the session has loaded (`!isPending`) and if there is no authenticated user (`!session?.user`). If both are true, it redirects the user to the sign-in page. ```tsx title="src/app/dashboard/page.tsx" "use client"; import { useRouter } from "next/navigation"; import { useSession } from "@/lib/auth-client"; import { useEffect } from "react"; // [!code ++] export default function DashboardPage() { const router = useRouter(); const { data: session, isPending } = useSession(); useEffect(() => { // [!code ++] if (!isPending && !session?.user) { // [!code ++] router.push("/sign-in"); // [!code ++] } // [!code ++] }, [isPending, session, router]); // [!code ++] return (

Dashboard

); } ``` Add loading and redirecting states so the page does not flash an empty dashboard while the session is being verified. ```tsx title="src/app/dashboard/page.tsx" "use client"; import { useRouter } from "next/navigation"; import { useSession } from "@/lib/auth-client"; import { useEffect } from "react"; export default function DashboardPage() { const router = useRouter(); const { data: session, isPending } = useSession(); useEffect(() => { if (!isPending && !session?.user) { router.push("/sign-in"); } }, [isPending, session, router]); if (isPending) // [!code ++] return

Loading...

; // [!code ++] if (!session?.user) // [!code ++] return

Redirecting...

; // [!code ++] return (

Dashboard

); } ``` Finally, if the user is authenticated, display their name and email from the `session` object. Also, import the `signOut` function and add a button that calls it, allowing the user to log out. ```tsx title="src/app/dashboard/page.tsx" "use client"; import { useRouter } from "next/navigation"; import { useSession, signOut } from "@/lib/auth-client"; import { useEffect } from "react"; export default function DashboardPage() { const router = useRouter(); const { data: session, isPending } = useSession(); useEffect(() => { if (!isPending && !session?.user) { router.push("/sign-in"); } }, [isPending, session, router]); if (isPending) return

Loading...

; if (!session?.user) return

Redirecting...

; const { user } = session; // [!code ++] return (

Dashboard

Welcome, {user.name || "User"}!

Email: {user.email}

{/* [!code ++] */}
); } ``` ### 5.4. Home page [#54-home-page] Finally, update the home page to link to the sign-in and sign-up pages. Replace the contents of `src/app/page.tsx` with the following: ```tsx title="src/app/page.tsx" "use client"; import { useRouter } from "next/navigation"; export default function Home() { const router = useRouter(); return (
); } ``` ## 6. Test it out [#6-test-it-out] Your application is now fully configured. 1. Start the development server to test it: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` 2. Navigate to `http://localhost:3000` in your browser. You should see the home page with "Sign Up" and "Sign In" buttons. 3. Click on **Sign Up**, create a new account, and you should be redirected to the dashboard. You can then sign out and sign back in. 4. To view the user data directly in your database, you can use Prisma Studio. #### bun ```bash bunx prisma studio ``` #### pnpm ```bash pnpm prisma studio ``` #### yarn ```bash yarn prisma studio ``` #### npm ```bash npx prisma studio ``` 5. This will open a new tab in your browser where you can see the `User`, `Session`, and `Account` tables and their contents. > [!TIP] > You now have a working authentication system built with Better Auth, Prisma, and Next.js. ## Next steps [#next-steps] * Add support for social login or magic links * Implement password reset and email verification * Add user profile and account management pages * Deploy to Vercel and secure your environment variables * Extend your Prisma schema with custom application models ## Further reading [#further-reading] * [Better Auth documentation](https://www.better-auth.com/docs) * [Prisma documentation](https://www.prisma.io/docs/orm/v7) * [Next.js App Router](https://nextjs.org/docs/app) ## Related pages - [`Better Auth (with Astro)`](https://www.prisma.io/docs/guides/authentication/better-auth/astro): Learn how to use Prisma ORM in an Astro app with Better Auth # Clerk (with Astro) (/docs/guides/authentication/clerk/astro) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Learn how to use Prisma ORM in an Astro app with Clerk Auth Location: Guides > Authentication > Clerk > Clerk (with Astro) > [!NOTE] > This guide uses Prisma ORM 7 > > The commands and code on this page target Prisma ORM 7, which remains fully supported. To start a new Prisma ORM 8 project, see [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql); to add Prisma ORM 8 to an existing app, see [Add Prisma ORM to an existing PostgreSQL database](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql). A Prisma ORM 8 version of this guide is planned. ## Introduction [#introduction] [Clerk](https://clerk.com/) is a drop-in auth provider that handles sign-up, sign-in, user management, and webhooks so you don't have to. In this guide you'll wire Clerk into a brand-new [Astro](https://astro.build/) app and persist users in a [Prisma Postgres](https://prisma.io/postgres) database. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/clerk-astro). ## Prerequisites [#prerequisites] * [Node.js 22.12+ on 22.x or 24+](https://nodejs.org) * [Clerk account](https://clerk.com) * [ngrok account](https://ngrok.com) ## 1. Set up your project [#1-set-up-your-project] Create a new Astro project: #### bun ```bash bun create astro@latest ``` #### pnpm ```bash pnpm create astro@latest ``` #### yarn ```bash yarn create astro@latest ``` #### npm ```bash npm create astro@latest ``` It will prompt you to customize your setup. Choose the defaults: > [!NOTE] > * *How would you like to start your new project?* `Empty` > * *Install dependencies?* `Yes` > * *Initialize a new git repository?* `Yes` Navigate into the newly created project directory: ```bash cd ``` ## 2. Set up Clerk [#2-set-up-clerk] ### 2.1. Create a new Clerk application [#21-create-a-new-clerk-application] [Sign in](https://dashboard.clerk.com/sign-in) to Clerk and navigate to the home page. From there, press the `Create Application` button to create a new application. Enter a title, select your sign-in options, and click `Create Application`. > [!NOTE] > For this guide, the Google, Github, and Email sign in options will be used. Install the Clerk Astro SDK and Node adapter: #### bun ```bash bun add @clerk/astro @astrojs/node ``` #### pnpm ```bash pnpm add @clerk/astro @astrojs/node ``` #### yarn ```bash yarn add @clerk/astro @astrojs/node ``` #### npm ```bash npm install @clerk/astro @astrojs/node ``` In the Clerk Dashboard, navigate to the **API keys** page. In the **Quick Copy** section, copy your Clerk Publishable and Secret Keys. Paste your keys into `.env` in the root of your project: ```bash title=".env" PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= ``` ### 2.2. Configure Astro with Clerk [#22-configure-astro-with-clerk] Astro needs to be configured for server-side rendering (SSR) with the Node adapter to work with Clerk. Update your `astro.config.mjs` file to include the Clerk integration and enable SSR: ```javascript title="astro.config.mjs" import { defineConfig } from "astro/config"; import node from "@astrojs/node"; // [!code ++] import clerk from "@clerk/astro"; // [!code ++] export default defineConfig({ integrations: [clerk()], // [!code ++] adapter: node({ mode: "standalone" }), // [!code ++] output: "server", // [!code ++] }); ``` ### 2.3. Set up Clerk middleware [#23-set-up-clerk-middleware] The `clerkMiddleware` helper enables authentication across your entire application. Create a `middleware.ts` file in the `src` directory: ```typescript title="src/middleware.ts" import { clerkMiddleware } from "@clerk/astro/server"; export const onRequest = clerkMiddleware(); ``` ### 2.4. Add Clerk UI to your page [#24-add-clerk-ui-to-your-page] Update your `src/pages/index.astro` file to import the Clerk authentication components: ```html title="src/pages/index.astro" --- import { // [!code ++] SignedIn, // [!code ++] SignedOut, // [!code ++] UserButton, // [!code ++] SignInButton, // [!code ++] } from "@clerk/astro/components"; // [!code ++] --- Astro ``` Now add a header with conditional rendering to show sign-in buttons for unauthenticated users and a user button for authenticated users: ```html title="src/pages/index.astro" --- import { SignedIn, SignedOut, UserButton, SignInButton, } from "@clerk/astro/components"; --- Astro
// [!code ++] // [!code ++] // [!code ++] // [!code ++] // [!code ++] // [!code ++] // [!code ++]
// [!code ++] ``` ## 3. Install and configure Prisma [#3-install-and-configure-prisma] ### 3.1. Install dependencies [#31-install-dependencies] To get started with Prisma, you'll need to install a few dependencies: #### bun ```bash bun add prisma@prev tsx @types/pg --dev ``` #### pnpm ```bash pnpm add prisma@prev tsx @types/pg --save-dev ``` #### yarn ```bash yarn add prisma@prev tsx @types/pg --dev ``` #### npm ```bash npm install prisma@prev tsx @types/pg --save-dev ``` #### bun ```bash bun add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### pnpm ```bash pnpm add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### yarn ```bash yarn add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### npm ```bash npm install @prisma/client@7 @prisma/adapter-pg dotenv pg ``` > [!NOTE] > If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/database-drivers). Once installed, initialize Prisma in your project: #### bun ```bash bunx --bun prisma init ``` #### pnpm ```bash pnpm prisma init ``` #### yarn ```bash yarn prisma init ``` #### npm ```bash npx prisma init ``` > [!NOTE] > `prisma init` creates the Prisma scaffolding and a local `DATABASE_URL`. In the next step, you will create a Prisma Postgres database and replace that value with a direct `postgres://...` connection string. This will create: * A `prisma/` directory with a `schema.prisma` file * A `prisma.config.ts` file with your Prisma configuration * A `.env` file with a local `DATABASE_URL` already set Create a Prisma Postgres database and replace the generated `DATABASE_URL` in your `.env` file with the `postgres://...` connection string from the CLI output: #### bun ```bash bunx create-db ``` #### pnpm ```bash pnpm dlx create-db ``` #### yarn ```bash yarn dlx create-db ``` #### npm ```bash npx create-db ``` ### 3.2. Define your Prisma Schema [#32-define-your-prisma-schema] Add a `User` model that will store authenticated user information from Clerk. The `clerkId` field uniquely links each database user to their Clerk account: ```prisma title="prisma/schema.prisma" generator client { provider = "prisma-client" output = "../src/generated/prisma" } datasource db { provider = "postgresql" } model User { // [!code ++] id Int @id @default(autoincrement()) // [!code ++] clerkId String @unique // [!code ++] email String @unique // [!code ++] name String? // [!code ++] } // [!code ++] ``` Run the following command to create the database tables: #### bun ```bash bunx prisma migrate dev --name init ``` #### pnpm ```bash pnpm prisma migrate dev --name init ``` #### yarn ```bash yarn prisma migrate dev --name init ``` #### npm ```bash npx prisma migrate dev --name init ``` After the migration is complete, generate the Prisma Client: #### bun ```bash bunx prisma generate ``` #### pnpm ```bash pnpm prisma generate ``` #### yarn ```bash yarn prisma generate ``` #### npm ```bash npx prisma generate ``` This generates the Prisma Client in the `src/generated/prisma` directory. ### 3.3. Create TypeScript environment definitions [#33-create-typescript-environment-definitions] Create an `env.d.ts` file in your `src` directory to provide TypeScript definitions for your environment variables: ```bash touch src/env.d.ts ``` Add type definitions for all the environment variables your application uses: ```typescript title="src/env.d.ts" interface ImportMetaEnv { readonly DATABASE_URL: string; readonly CLERK_WEBHOOK_SIGNING_SECRET: string; readonly CLERK_SECRET_KEY: string; readonly PUBLIC_CLERK_PUBLISHABLE_KEY: string; } interface ImportMeta { readonly env: ImportMetaEnv; } ``` ### 3.4. Create a reusable Prisma Client [#34-create-a-reusable-prisma-client] In the `src` directory, create a `lib` directory and a `prisma.ts` file inside it: ```bash mkdir src/lib touch src/lib/prisma.ts ``` Initialize the Prisma Client with the PostgreSQL adapter: ```typescript title="src/lib/prisma.ts" import { PrismaClient } from "../generated/prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; const adapter = new PrismaPg({ connectionString: import.meta.env.DATABASE_URL, }); const prisma = new PrismaClient({ adapter, }); export default prisma; ``` ## 4. Wire Clerk to the database [#4-wire-clerk-to-the-database] ### 4.1. Create a Clerk webhook endpoint [#41-create-a-clerk-webhook-endpoint] Webhooks allow Clerk to notify your application when events occur, such as when a user signs up. You'll create an API route to handle these webhooks and sync user data to your database. Create the directory structure and file for the webhook endpoint: ```bash mkdir -p src/pages/api/webhooks touch src/pages/api/webhooks/clerk.ts ``` Import the necessary dependencies: ```typescript title="src/pages/api/webhooks/clerk.ts" import { verifyWebhook } from "@clerk/astro/webhooks"; import type { APIRoute } from "astro"; import prisma from "../../../lib/prisma"; ``` Create the `POST` handler that Clerk will call. The `verifyWebhook` function validates that the request actually comes from Clerk using the signing secret: ```typescript title="src/pages/api/webhooks/clerk.ts" import { verifyWebhook } from "@clerk/astro/webhooks"; import type { APIRoute } from "astro"; import prisma from "../../../lib/prisma"; export const POST: APIRoute = async ({ request }) => { // [!code ++] try { // [!code ++] const evt = await verifyWebhook(request, { // [!code ++] signingSecret: import.meta.env.CLERK_WEBHOOK_SIGNING_SECRET, // [!code ++] }); // [!code ++] const { id } = evt.data; // [!code ++] const eventType = evt.type; // [!code ++] console.log( // [!code ++] `Received webhook with ID ${id} and event type of ${eventType}`, // [!code ++] ); // [!code ++] } catch (err) { // [!code ++] console.error("Error verifying webhook:", err); // [!code ++] return new Response("Error verifying webhook", { status: 400 }); // [!code ++] } // [!code ++] }; // [!code ++] ``` When a new user is created, they need to be stored in the database. You'll do that by checking if the event type is `user.created` and then using Prisma's `upsert` method to create a new user if they don't exist: ```typescript title="src/pages/api/webhooks/clerk.ts" import { verifyWebhook } from "@clerk/astro/webhooks"; import type { APIRoute } from "astro"; import prisma from "../../../lib/prisma"; export const POST: APIRoute = async ({ request }) => { try { const evt = await verifyWebhook(request, { signingSecret: import.meta.env.CLERK_WEBHOOK_SIGNING_SECRET, }); const { id } = evt.data; const eventType = evt.type; console.log(`Received webhook with ID ${id} and event type of ${eventType}`); if (eventType === "user.created") { // [!code ++] const { id, email_addresses, first_name, last_name } = evt.data; // [!code ++] await prisma.user.upsert({ // [!code ++] where: { clerkId: id }, // [!code ++] update: {}, // [!code ++] create: { // [!code ++] clerkId: id, // [!code ++] email: email_addresses[0].email_address, // [!code ++] name: `${first_name} ${last_name}`, // [!code ++] }, // [!code ++] }); // [!code ++] } // [!code ++] } catch (err) { console.error("Error verifying webhook:", err); return new Response("Error verifying webhook", { status: 400 }); } }; ``` Finally, return a response to Clerk to confirm the webhook was received: ```typescript title="src/pages/api/webhooks/clerk.ts" import { verifyWebhook } from "@clerk/astro/webhooks"; import type { APIRoute } from "astro"; import prisma from "../../../lib/prisma"; export const POST: APIRoute = async ({ request }) => { try { const evt = await verifyWebhook(request, { signingSecret: import.meta.env.CLERK_WEBHOOK_SIGNING_SECRET, }); const { id } = evt.data; const eventType = evt.type; console.log(`Received webhook with ID ${id} and event type of ${eventType}`); if (eventType === "user.created") { const { id, email_addresses, first_name, last_name } = evt.data; await prisma.user.upsert({ where: { clerkId: id }, update: {}, create: { clerkId: id, email: email_addresses[0].email_address, name: `${first_name} ${last_name}`, }, }); } return new Response("Webhook received", { status: 200 }); // [!code ++] } catch (err) { console.error("Error verifying webhook:", err); return new Response("Error verifying webhook", { status: 400 }); } }; ``` ### 4.2. Expose your local app for webhooks [#42-expose-your-local-app-for-webhooks] You'll need to expose your local app for webhooks with [ngrok](https://ngrok.com/). This will allow Clerk to reach your `/api/webhooks/clerk` route to push events like `user.created`. Start your development server: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` In a separate terminal window, install ngrok globally and expose your local app: #### bun ```bash bun add --global ngrok ngrok http 4321 ``` #### pnpm ```bash pnpm add --global ngrok ngrok http 4321 ``` #### yarn ```bash yarn global add ngrok ngrok http 4321 ``` #### npm ```bash npm install --global ngrok ngrok http 4321 ``` Copy the ngrok `Forwarding URL` (e.g., `https://a65a60261342.ngrok-free.app`). This will be used to configure the webhook URL in Clerk. ### 4.3. Configure Astro to allow ngrok connections [#43-configure-astro-to-allow-ngrok-connections] Astro needs to be configured to accept connections from the ngrok domain. Update your `astro.config.mjs` to include the ngrok host in the allowed hosts list: ```javascript title="astro.config.mjs" import { defineConfig } from "astro/config"; import node from "@astrojs/node"; import clerk from "@clerk/astro"; export default defineConfig({ integrations: [clerk()], adapter: node({ mode: "standalone" }), output: "server", server: { // [!code ++] allowedHosts: ["localhost", ".ngrok-free.app"], // [!code ++] }, // [!code ++] }); ``` > [!NOTE] > Replace `` with the subdomain from your ngrok URL. For example, if your ngrok URL is `https://a65a60261342.ngrok-free.app`, use `a65a60261342.ngrok-free.app`. ### 4.4. Register the webhook in Clerk [#44-register-the-webhook-in-clerk] Navigate to the ***Webhooks*** section of your Clerk application located near the bottom of the ***Configure*** tab under ***Developers***. Click ***Add Endpoint*** and paste the ngrok URL into the ***Endpoint URL*** field and add `/api/webhooks/clerk` to the end. It should look similar to this: ```text https://a65a60261342.ngrok-free.app/api/webhooks/clerk ``` Subscribe to the **user.created** event by checking the box next to it under ***Message Filtering***. Click ***Create*** to save the webhook endpoint. Copy the ***Signing Secret*** and add it to your `.env` file: ```bash title=".env" # Prisma DATABASE_URL= # Clerk PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= CLERK_WEBHOOK_SIGNING_SECRET= # [!code ++] ``` Restart your dev server to pick up the new environment variable: #### bun ```bash bun run dev ``` #### pnpm ```bash pnpm run dev ``` #### yarn ```bash yarn dev ``` #### npm ```bash npm run dev ``` ### 4.5. Test the integration [#45-test-the-integration] Navigate to `http://localhost:4321` in your browser and sign in using any of the sign-up options you configured in Clerk. Open Prisma Studio to verify that the user was created in your database: #### bun ```bash bunx prisma studio ``` #### pnpm ```bash pnpm prisma studio ``` #### yarn ```bash yarn prisma studio ``` #### npm ```bash npx prisma studio ``` You should see a new user record with the Clerk ID, email, and name from your sign-up. > [!NOTE] > If you don't see a user record, there are a few things to check: > > * Delete your user from the Users tab in Clerk and try signing up again. > * Check your ngrok URL and ensure it's correct *(it will change every time you restart ngrok)*. > * Verify your Clerk webhook is pointing to the correct ngrok URL. > * Make sure you've added `/api/webhooks/clerk` to the end of the webhook URL. > * Ensure you've subscribed to the **user.created** event in Clerk. > * Confirm you've added the ngrok host to `allowedHosts` in `astro.config.mjs` and removed `https://`. > * Check the terminal running `npm run dev` for any error messages. You now have an Astro application where Clerk handles authentication and every new sign-up is synced to your Prisma Postgres database through the webhook. ## Next steps [#next-steps] Now that you have a working Astro app with Clerk authentication and Prisma connected to a Prisma Postgres database, you can: * Add user profile management and update functionality * Build protected API routes that require authentication * Extend your schema with additional models related to users * Deploy to your preferred hosting platform and set your production webhook URL in Clerk ### More info [#more-info] * [Prisma Documentation](https://www.prisma.io/docs/orm/v7) * [Astro Documentation](https://docs.astro.build) * [Clerk Documentation](https://clerk.com/docs) ## Related pages - [`Clerk (with Next.js)`](https://www.prisma.io/docs/guides/authentication/clerk/nextjs): Learn how to use Prisma ORM in a Next.js app with Clerk Auth # Clerk (with Next.js) (/docs/guides/authentication/clerk/nextjs) > For the complete Prisma documentation index, see [llms.txt](https://www.prisma.io/docs/llms.txt). A markdown version of any docs page is available by appending `.md` to its URL. Learn how to use Prisma ORM in a Next.js app with Clerk Auth Location: Guides > Authentication > Clerk > Clerk (with Next.js) > [!NOTE] > This guide uses Prisma ORM 7 > > The commands and code on this page target Prisma ORM 7, which remains fully supported. To start a new Prisma ORM 8 project, see [Create a new app with PostgreSQL](https://www.prisma.io/docs/prisma-orm/quickstart/postgresql); to add Prisma ORM 8 to an existing app, see [Add Prisma ORM to an existing PostgreSQL database](https://www.prisma.io/docs/prisma-orm/add-to-existing-project/postgresql). A Prisma ORM 8 version of this guide is planned. ## Introduction [#introduction] [Clerk](https://clerk.com/) is a drop-in auth provider that handles sign-up, sign-in, user management, and webhooks so you don't have to. In this guide you'll wire Clerk into a brand-new [Next.js](https://nextjs.org/) app, persist users in a [Prisma Postgres](https://prisma.io/postgres) database, and expose a tiny posts API. You can find a complete example of this guide on [GitHub](https://github.com/prisma/prisma-examples/tree/latest/orm/clerk-nextjs). ## Prerequisites [#prerequisites] * [Node.js 20.19+ on 20.x, 22.12+ on 22.x, or 24+](https://nodejs.org) * [Clerk account](https://clerk.com) * [ngrok account](https://ngrok.com) ## 1. Set up your project [#1-set-up-your-project] Create the app: #### bun ```bash bunx create-next-app@latest clerk-nextjs-prisma ``` #### pnpm ```bash pnpm dlx create-next-app@latest clerk-nextjs-prisma ``` #### yarn ```bash yarn dlx create-next-app@latest clerk-nextjs-prisma ``` #### npm ```bash npx create-next-app@latest clerk-nextjs-prisma ``` It will prompt you to customize your setup. Choose the defaults: > [!NOTE] > * *Would you like to use TypeScript?* `Yes` > * *Would you like to use ESLint?* `Yes` > * *Would you like to use Tailwind CSS?* `Yes` > * *Would you like your code inside a `src/` directory?* `No` > * *Would you like to use App Router?* (recommended) `Yes` > * *Would you like to use Turbopack for `next dev`?* `Yes` > * _Would you like to customize the import alias (`@/_`by default)?\*`No` Navigate to the project directory: ```bash cd clerk-nextjs-prisma ``` ## 2. Set up Clerk [#2-set-up-clerk] ### 2.1. Create a new Clerk application [#21-create-a-new-clerk-application] [Sign in](https://dashboard.clerk.com/sign-in) to Clerk and navigate to the home page. From there, press the `Create Application` button to create a new application. Enter a title, select your sign-in options, and click `Create Application`. > [!NOTE] > For this guide, the Google, Github, and Email sign in options will be used. Install the Clerk Next.js SDK: #### bun ```bash bun add @clerk/nextjs ``` #### pnpm ```bash pnpm add @clerk/nextjs ``` #### yarn ```bash yarn add @clerk/nextjs ``` #### npm ```bash npm install @clerk/nextjs ``` Copy your Clerk keys and paste them into **.env** in the root of your project: ```bash title=".env" # Clerk # [!code ++] NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= # [!code ++] CLERK_SECRET_KEY= # [!code ++] ``` ### 2.2. Protect routes with Clerk middleware [#22-protect-routes-with-clerk-middleware] The `clerkMiddleware` helper enables authentication and is where you'll configure your protected routes. Create a `middleware.ts` file in the root directory of your project: ```tsx title="middleware.ts" showLineNumbers import { clerkMiddleware } from "@clerk/nextjs/server"; // [!code ++] // [!code ++] export default clerkMiddleware(); // [!code ++] // [!code ++] export const config = { // [!code ++] matcher: [ // [!code ++] "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", // [!code ++] "/(api|trpc)(.*)", // [!code ++] ], // [!code ++] }; // [!code ++] ``` ### 2.3. Add Clerk UI to your layout [#23-add-clerk-ui-to-your-layout] Next, you'll need to wrap your app in the `ClerkProvider` component to make authentication globally available. In your `layout.tsx` file, add the `ClerkProvider` component: ```tsx title="app/layout.tsx" showLineNumbers import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ClerkProvider } from "@clerk/nextjs"; // [!code ++] const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( {" "} // [!code ++] {children} // [!code ++] ); } ``` Create a `Navbar` component which will be used to display the Sign In and Sign Up buttons as well as the User Button once a user is signed in: ```tsx title="app/layout.tsx" showLineNumbers import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; import { ClerkProvider, UserButton, // [!code ++] SignInButton, // [!code ++] SignUpButton, // [!code ++] SignedOut, // [!code ++] SignedIn, // [!code ++] } from "@clerk/nextjs"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); const geistMono = Geist_Mono({ variable: "--font-geist-mono", subsets: ["latin"], }); export const metadata: Metadata = { title: "Create Next App", description: "Generated by create next app", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( // [!code ++] {children} ); } const Navbar = () => { // [!code ++] return ( // [!code ++]
{" "} // [!code ++] {" "} // [!code ++] // [!code ++] // [!code ++] {" "} // [!code ++] {" "} // [!code ++] // [!code ++] {" "} // [!code ++]
// [!code ++] ); // [!code ++] }; // [!code ++] ``` ## 3. Install and configure Prisma [#3-install-and-configure-prisma] ### 3.1. Install dependencies [#31-install-dependencies] To get started with Prisma, you'll need to install a few dependencies: #### bun ```bash bun add prisma@prev tsx @types/pg --dev ``` #### pnpm ```bash pnpm add prisma@prev tsx @types/pg --save-dev ``` #### yarn ```bash yarn add prisma@prev tsx @types/pg --dev ``` #### npm ```bash npm install prisma@prev tsx @types/pg --save-dev ``` #### bun ```bash bun add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### pnpm ```bash pnpm add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### yarn ```bash yarn add @prisma/client@7 @prisma/adapter-pg dotenv pg ``` #### npm ```bash npm install @prisma/client@7 @prisma/adapter-pg dotenv pg ``` > [!NOTE] > If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](https://www.prisma.io/docs/orm/v7/core-concepts/supported-databases/database-drivers). Once installed, initialize Prisma in your project: #### bun ```bash bunx --bun prisma init --output ../app/generated/prisma ``` #### pnpm ```bash pnpm prisma init --output ../app/generated/prisma ``` #### yarn ```bash yarn prisma init --output ../app/generated/prisma ``` #### npm ```bash npx prisma init --output ../app/generated/prisma ``` > [!NOTE] > `prisma init` creates the Prisma scaffolding and a local `DATABASE_URL`. In the next step, you will create a Prisma Postgres database and replace that value with a direct `postgres://...` connection string. This will create: * A `prisma/` directory with a `schema.prisma` file * A local `DATABASE_URL` in `.env` Create a Prisma Postgres database and replace the generated `DATABASE_URL` in your `.env` file with the `postgres://...` connection string from the CLI output: #### bun ```bash bunx create-db ``` #### pnpm ```bash pnpm dlx create-db ``` #### yarn ```bash yarn dlx create-db ``` #### npm ```bash npx create-db ``` ### 3.2. Define your Prisma Schema [#32-define-your-prisma-schema] In the `prisma/schema.prisma` file, add the following models: ```prisma title="prisma/schema.prisma" showLineNumbers generator client { provider = "prisma-client" output = "../app/generated/prisma" } datasource db { provider = "postgresql" } model User { // [!code ++] id Int @id @default(autoincrement()) // [!code ++] clerkId String @unique // [!code ++] email String @unique // [!code ++] name String? // [!code ++] posts Post[] // [!code ++] } // [!code ++] // [!code ++] model Post { // [!code ++] id Int @id @default(autoincrement()) // [!code ++] title String // [!code ++] content String? // [!code ++] published Boolean @default(false) // [!code ++] authorId Int // [!code ++] author User @relation(fields: [authorId], references: [id]) // [!code ++] createdAt DateTime @default(now()) // [!code ++] } // [!code ++] ``` This will create two models: `User` and `Post`, with a one-to-many relationship between them. Create a `prisma.config.ts` file to configure Prisma: ```typescript title="prisma.config.ts" showLineNumbers import "dotenv/config"; // [!code ++] import { defineConfig, env } from "prisma/config"; // [!code ++] // [!code ++] export default defineConfig({ // [!code ++] schema: "prisma/schema.prisma", // [!code ++] migrations: { // [!code ++] path: "prisma/migrations", // [!code ++] }, // [!code ++] datasource: { // [!code ++] url: env("DATABASE_URL"), // [!code ++] }, // [!code ++] }); // [!code ++] ``` > [!NOTE] > You'll need to install the `dotenv` package: > > > > > #### bun > ```bash > bun add dotenv > ``` > > > #### pnpm > ```bash > pnpm add dotenv > ``` > > > #### yarn > ```bash > yarn add dotenv > ``` > > > #### npm > ```bash > npm install dotenv > ``` > > Now, run the following command to create the database tables and generate the Prisma Client: #### bun ```bash bunx prisma migrate dev --name init ``` #### pnpm ```bash pnpm prisma migrate dev --name init ``` #### yarn ```bash yarn prisma migrate dev --name init ``` #### npm ```bash npx prisma migrate dev --name init ``` #### bun ```bash bunx prisma generate ``` #### pnpm ```bash pnpm prisma generate ``` #### yarn ```bash yarn prisma generate ``` #### npm ```bash npx prisma generate ``` > [!WARNING] > It is recommended that you add `/app/generated/prisma` to your `.gitignore` file. ### 3.3. Create a reusable Prisma Client [#33-create-a-reusable-prisma-client] In the root directory, create a `lib` directory and a `prisma.ts` file inside it: ```tsx title="lib/prisma.ts" showLineNumbers import { PrismaClient } from "../app/generated/prisma/client"; // [!code ++] import { PrismaPg } from "@prisma/adapter-pg"; // [!code ++] // [!code ++] const adapter = new PrismaPg({ // [!code ++] connectionString: process.env.DATABASE_URL!, // [!code ++] }); // [!code ++] // [!code ++] const globalForPrisma = global as unknown as { // [!code ++] prisma: PrismaClient; // [!code ++] }; // [!code ++] // [!code ++] const prisma = globalForPrisma.prisma || new PrismaClient({ // [!code ++] adapter, // [!code ++] }); // [!code ++] // [!code ++] if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; // [!code ++] // [!code ++] export default prisma; // [!code ++] ``` ## 4. Wire Clerk to the database [#4-wire-clerk-to-the-database] ### 4.1. Create a Clerk webhook endpoint [#41-create-a-clerk-webhook-endpoint] Create a new API route at `app/api/webhooks/clerk/route.ts`: Import the necessary dependencies: ```tsx title="app/api/webhooks/clerk/route.ts" showLineNumbers import { verifyWebhook } from "@clerk/nextjs/webhooks"; // [!code ++] import { NextRequest } from "next/server"; // [!code ++] import prisma from "@/lib/prisma"; // [!code ++] ``` Create the `POST` method that Clerk will call and verify the webhook: ```tsx title="app/api/webhooks/clerk/route.ts" showLineNumbers import { verifyWebhook } from "@clerk/nextjs/webhooks"; import { NextRequest } from "next/server"; import prisma from "@/lib/prisma"; export async function POST(req: NextRequest) { // [!code ++] try { // [!code ++] const evt = await verifyWebhook(req); // [!code ++] const { id } = evt.data; // [!code ++] const eventType = evt.type; // [!code ++] console.log( // [!code ++] `Received webhook with ID ${id} and event type of ${eventType}`, // [!code ++] ); // [!code ++] } catch (err) { // [!code ++] console.error("Error verifying webhook:", err); // [!code ++] return new Response("Error verifying webhook", { status: 400 }); // [!code ++] } // [!code ++] } // [!code ++] ``` When a new user is created, they need to be stored in the database. You'll do that by checking if the event type is `user.created` and then using Prisma's `upsert` method to create a new user if they don't exist: ```tsx title="app/api/webhooks/clerk/route.ts" showLineNumbers import { verifyWebhook } from "@clerk/nextjs/webhooks"; import { NextRequest } from "next/server"; import prisma from "@/lib/prisma"; export async function POST(req: NextRequest) { try { const evt = await verifyWebhook(req); const { id } = evt.data; const eventType = evt.type; console.log(`Received webhook with ID ${id} and event type of ${eventType}`); if (eventType === "user.created") { // [!code ++] const { id, email_addresses, first_name, last_name } = evt.data; // [!code ++] await prisma.user.upsert({ // [!code ++] where: { clerkId: id }, // [!code ++] update: {}, // [!code ++] create: { // [!code ++] clerkId: id, // [!code ++] email: email_addresses[0].email_address, // [!code ++] name: `${first_name} ${last_name}`, // [!code ++] }, // [!code ++] }); // [!code ++] } // [!code ++] } catch (err) { console.error("Error verifying webhook:", err); return new Response("Error verifying webhook", { status: 400 }); } } ``` Finally, return a response to Clerk to confirm the webhook was received: ```tsx title="app/api/webhooks/clerk/route.ts" showLineNumbers import { verifyWebhook } from "@clerk/nextjs/webhooks"; import { NextRequest } from "next/server"; import prisma from "@/lib/prisma"; export async function POST(req: NextRequest) { try { const evt = await verifyWebhook(req); const { id } = evt.data; const eventType = evt.type; console.log(`Received webhook with ID ${id} and event type of ${eventType}`); if (eventType === "user.created") { const { id, email_addresses, first_name, last_name } = evt.data; await prisma.user.upsert({ where: { clerkId: id }, update: {}, create: { clerkId: id, email: email_addresses[0].email_address, name: `${first_name} ${last_name}`, }, }); } return new Response("Webhook received", { status: 200 }); // [!code ++] } catch (err) { console.error("Error verifying webhook:", err); return new Response("Error verifying webhook", { status: 400 }); } } ``` ### 4.2. Expose your local app for webhooks [#42-expose-your-local-app-for-webhooks] You'll need to expose your local app for webhooks with [ngrok](https://ngrok.com/). This will allow Clerk to reach your `/api/webhooks/clerk` route to push events like `user.created`. Install ngrok and expose your local app: #### bun ```bash bun add --global ngrok ngrok http 3000 ``` #### pnpm ```bash pnpm add --global ngrok ngrok http 3000 ``` #### yarn ```bash yarn global add ngrok ngrok http 3000 ``` #### npm ```bash npm install --global ngrok ngrok http 3000 ``` Copy the ngrok `Forwarding URL`. This will be used to set the webhook URL in Clerk. Navigate to the ***Webhooks*** section of your Clerk application located near the bottom of the ***Configure*** tab under ***Developers***. Click ***Add Endpoint*** and paste the ngrok URL into the ***Endpoint URL*** field and add `/api/webhooks/clerk` to the end of the URL. It should look similar to this: ```text https://a60b-99-42-62-240.ngrok-free.app/api/webhooks/clerk ``` Copy the ***Signing Secret*** and add it to your `.env` file: ```bash title=".env" # Prisma DATABASE_URL= # Clerk NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= CLERK_SECRET_KEY= CLERK_WEBHOOK_SIGNING_SECRET= # [!code ++] ``` On the home page, press Sign Up and create an account using any of the sign-up options Open Prisma Studio and you should see a user record. #### bun ```bash bunx prisma studio ``` #### pnpm ```bash pnpm prisma studio ``` #### yarn ```bash yarn prisma studio ``` #### npm ```bash npx prisma studio ``` > [!NOTE] > If you don't see a user record, there are a few things to check: > > * Delete your user from the Users tab in Clerk and try again. > * Check your ngrok URL and ensure it's correct *(it will change everytime you restart ngrok)*. > * Check your Clerk webhook is pointing to the correct ngrok URL. > * Make sure you've added `/api/webhooks/clerk` to the end of the URL. ## 5. Build a posts API [#5-build-a-posts-api] To create posts under a user, you'll need to create a new API route at `app/api/posts/route.ts`: Start by importing the necessary dependencies: ```tsx title="app/api/posts/route.ts" showLineNumbers import { auth } from "@clerk/nextjs/server"; // [!code ++] import prisma from "@/lib/prisma"; // [!code ++] ``` Get the `clerkId` of the authenticated user. If there's no user, return a `401` Unauthorized response: ```tsx title="app/api/posts/route.ts" showLineNumbers import { auth } from "@clerk/nextjs/server"; import prisma from "@/lib/prisma"; export async function POST(req: Request) { // [!code ++] const { userId: clerkId } = await auth(); // [!code ++] if (!clerkId) return new Response("Unauthorized", { status: 401 }); // [!code ++] } // [!code ++] ``` Match the Clerk user to a user in the database. If none is found, return a `404` Not Found response: ```tsx title="app/api/posts/route.ts" showLineNumbers import { auth } from "@clerk/nextjs/server"; import prisma from "@/lib/prisma"; export async function POST(req: Request) { const { userId: clerkId } = await auth(); if (!clerkId) return new Response("Unauthorized", { status: 401 }); const user = await prisma.user.findUnique({ // [!code ++] where: { clerkId }, // [!code ++] }); // [!code ++] // [!code ++] if (!user) return new Response("User not found", { status: 404 }); // [!code ++] } ``` Destructure the title and content from the incoming request and create a post. Once done, return a `201` Created response: ```tsx title="app/api/posts/route.ts" showLineNumbers import { auth } from "@clerk/nextjs/server"; import prisma from "@/lib/prisma"; export async function POST(req: Request) { const { userId: clerkId } = await auth(); if (!clerkId) return new Response("Unauthorized", { status: 401 }); const { title, content } = await req.json(); // [!code ++] const user = await prisma.user.findUnique({ where: { clerkId }, }); if (!user) return new Response("User not found", { status: 404 }); const post = await prisma.post.create({ // [!code ++] data: { // [!code ++] title, // [!code ++] content, // [!code ++] authorId: user.id, // [!code ++] }, // [!code ++] }); // [!code ++] return new Response(JSON.stringify(post), { status: 201 }); // [!code ++] } ``` ## 6. Add a Post creation UI [#6-add-a-post-creation-ui] In `/app`, create a `/components` directory and a `PostInputs.tsx` file inside it: ```tsx title="app/components/PostInputs.tsx" showLineNumbers "use client"; // [!code ++] import { useState } from "react"; // [!code ++] export default function PostInputs() { // [!code ++] const [title, setTitle] = useState(""); // [!code ++] const [content, setContent] = useState(""); // [!code ++] } // [!code ++] ``` This component uses `"use client"` to ensure the component is rendered on the client. The title and content are stored in their own `useState` hooks. Create a function that will be called when a form is submitted: ```tsx title="app/components/PostInputs.tsx" showLineNumbers "use client"; import { useState } from "react"; export default function PostInputs() { const [title, setTitle] = useState(""); const [content, setContent] = useState(""); async function createPost(e: React.FormEvent) { // [!code ++] e.preventDefault(); // [!code ++] if (!title || !content) return; // [!code ++] // [!code ++] await fetch("/api/posts", { // [!code ++] method: "POST", // [!code ++] headers: { "Content-Type": "application/json" }, // [!code ++] body: JSON.stringify({ title, content }), // [!code ++] }); // [!code ++] // [!code ++] setTitle(""); // [!code ++] setContent(""); // [!code ++] location.reload(); // [!code ++] } // [!code ++] } ``` You'll be using a form to create a post and call the `POST` route you created earlier: ```tsx title="app/components/PostInputs.tsx" showLineNumbers "use client"; import { useState } from "react"; export default function PostInputs() { const [title, setTitle] = useState(""); const [content, setContent] = useState(""); async function createPost(e: React.FormEvent) { e.preventDefault(); if (!title || !content) return; await fetch("/api/posts", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title, content }), }); setTitle(""); setContent(""); location.reload(); } return ( // [!code ++]
{" "} // [!code ++] setTitle(e.target.value)} // [!code ++] className="w-full p-2 border border-zinc-800 rounded" // [!code ++] />{" "} // [!code ++]