Skip to main content
Prisma Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

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, which copy Prisma's instructions for AI coding agents into your project.

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:

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 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 explains. Pass --config to use a config file in another place, such as a subdirectory:

title="bun"
bunx prisma contract emit --config ./config/prisma.config.ts
pnpm
pnpm prisma contract emit --config ./config/prisma.config.ts
yarn
yarn prisma contract emit --config ./config/prisma.config.ts
npm
npx prisma contract emit --config ./config/prisma.config.ts

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.

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 shows, in each project's own config.

To stop a project's config from inheriting anything, add parent: false to it. The search then ends at that file:

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:

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.

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:

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 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.

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:

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. 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 installs next to Prisma ORM 8.

Once the config is in place, run npx prisma contract emit and then npx prisma 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.

On MongoDB, Prisma ORM 8 can read a Prisma ORM 6 schema.prisma directly as its contract source. Wrap the path in prisma6Schema:

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, which checks that the database Prisma ORM 6 built matches the contract.

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 lists each code and what to change.

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.

contract emit does not connect to a database, so the orm section can omit db.connection:

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.

Add extension control descriptors to the orm section when your contract uses extension-provided types:

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.

The skills section controls the agent skills commands and the staleness check:

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 scaffolds this section for you.

Database commands accept --db <url>. If you omit it, Prisma ORM uses the database connection from prisma.config.ts.

title="bun"
bunx prisma db verify --db "$DATABASE_URL"
pnpm
pnpm prisma db verify --db "$DATABASE_URL"
yarn
yarn prisma db verify --db "$DATABASE_URL"
npm
npx prisma db verify --db "$DATABASE_URL"

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. DATABASE_URL is not one of them: your config file reads it, as in the example above.

Use the default text output when running commands locally. Use --json in CI or automation:

bunx prisma db verify --db "$DATABASE_URL" --json
Bash
pnpm prisma db verify --db "$DATABASE_URL" --json
Bash
yarn prisma db verify --db "$DATABASE_URL" --json
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.

Use --no-interactive for scripts that must never pause for user input. Use --confirm <token> to grant a consent prompt non-interactively. For example, db update asks for the database name before a destructive change.

For an AI agent that reads the output as text rather than parsing JSON, use --format markdown; see Global flags.

Use --no-interactive for scripts that must never pause for user input. Use --confirm <token> to grant a consent prompt non-interactively. For example, db update asks for the database name before a destructive change.

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.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu