# Core concepts (/docs/orm/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 Prisma ORM command and API builds on: contracts, emitting, plans, the database signature, codecs, and the migration graph.

Location: ORM > Core concepts

Prisma ORM 8 is the current major version. To meet the evolving needs of developers, it has been rebuilt in TypeScript according to one idea: your application and database follow an explicit, checkable agreement of how all data is structured.

This idea relies upon a small vocabulary which repeats throughout: in the CLI, in the query APIs, and in error messages. The sections below define each term in plain language; each also links to relevant in-depth information.

## The contract and the schema [#the-contract-and-the-schema]

The contract is your description of the data your application needs: the models, their fields, how they relate, and how they map to database tables or collections. You author it in PSL (the Prisma schema language) in a `.prisma` file, or in TypeScript:

```prisma title="prisma/contract.prisma"
model User {
  id    Int    @id @default(autoincrement())
  email String @unique
  posts Post[]
}

model Post {
  id        Int     @id @default(autoincrement())
  title     String
  published Boolean @default(false)
  userId    Int

  user User @relation(fields: [userId], references: [id])
}
```

The schema differs in that it is the actual structure of the database: the tables or collections, plus their indexes, that exist right now. The contract lives in your repository; the schema lives in the database. Everything Prisma ORM does is a relationship between the two: queries are typed against the contract, migrations move the schema toward the contract, and verification checks that the schema still satisfies it.

> [!NOTE]
> Contract vs. schema
> 
> Other tools use "schema" to refer to the file that you write. However, in Prisma ORM, you author a **contract** and the **schema** is held by the database; therefore, be aware that when a command or error message mentions the "schema", this refers to the database side.

Read more in [The data contract](https://www.prisma.io/docs/orm/contract-authoring/the-data-contract), and author it [in PSL](https://www.prisma.io/docs/orm/contract-authoring/psl-syntax) or [in TypeScript](https://www.prisma.io/docs/orm/contract-authoring/typescript-schema-builder).

## Emitting: from source to artifacts [#emitting-from-source-to-artifacts]

Emitting is the build step that compiles your contract source into two plain files:

  

#### bun

```bash
bunx prisma@latest contract emit
```

#### pnpm

```bash
pnpm dlx prisma@latest contract emit
```

#### yarn

```bash
yarn dlx prisma@latest contract emit
```

#### npm

```bash
npx prisma@latest contract emit
```

1. `contract.json`: a canonical JSON description of your models, storage layout, and required capabilities.
2. `contract.d.ts`: the TypeScript types derived from it, which is what makes your queries type-safe.

Every other part of the toolchain reads these artifacts, not your source file: the query APIs read `contract.d.ts` for types, the migration planner diffs two `contract.json` files, and the runtime verifies `contract.json` against the database. That is why `contract emit` comes first in almost every workflow: after any contract change, emit before you plan, migrate, or run.

Emission is deterministic: the same source always produces byte-identical artifacts, so both files are committed to version control and diff cleanly in code review. Think of the pair like `package.json` and `package-lock.json`: the source is what you ask for, the artifacts are the exact resolved result.

See [contract.json and contract.d.ts](https://www.prisma.io/docs/orm/contract-authoring/the-contract-artifact) for what is inside each file.

## Hashes and the database signature [#hashes-and-the-database-signature]

A hash is a short fingerprint computed from a file's content: the same content always produces the same hash, while any change will produce a different one. Because emission is deterministic, hashing `contract.json` gives an identifier for that exact contract state, the way a Git commit hash identifies an exact state of your code. Contract hashes appear throughout the CLI; a migration, for example, records the hash it starts from and the hash it produces.

The database carries the other half of the agreement: a **signature**, a small marker record stored in the database itself that names the contract hash the database currently satisfies. [`db sign`](https://www.prisma.io/docs/cli/db-sign) writes it, and [`db migrate`](https://www.prisma.io/docs/cli/db-migrate) updates it each time it applies a migration.

The two halves make the agreement checkable from either side:

1. Before executing queries, the runtime can compare the contract that your application was built with against the database's signature and stop when it encounters a mismatch, for example a deploy against an unmigrated database, before it produces incorrect results.
2. Before applying a migration, the runner checks that the database's signature matches the contract hash the migration starts from.

When the contract and the database disagree, the resulting state is called **drift**. [`db verify`](https://www.prisma.io/docs/cli/db-verify) is the read-only command that reports it.

## Queries compile to plans [#queries-compile-to-plans]

A **plan** is the compiled form of a query: a plain data object holding the statement to run, its parameters, and metadata about what the query touches. Every query, whichever API produced it, becomes a plan before it executes; running the plan is a separate step.

With the SQL query builder, the two steps are visible in your code:

```typescript
import { db } from "./prisma/db";

const plan = db.sql.public.post
  .select("id", "title", "userId")
  .where((f, fns) => fns.eq(f.published, true))
  .limit(10)
  .build();

const publishedPosts = await db.runtime().query(plan);
```

Plans matter for two reasons:

1. **Every query goes through the same pipeline.** However a query is written (the ORM client, a query builder, a raw fragment, or an API that an extension added), it reaches the database as a plan. Middleware sees every query in the same shape, execution works the same way for all of them, and the query APIs can be mixed freely. In addition, one policy (an authorization check, for instance) can sit in one place and see everything.
2. **A plan is data.** The statement and its parameters exist as an object before anything touches the database: middleware can check them, telemetry can record them, and a failed query can report exactly what it ran.

## The query APIs [#the-query-apis]

All query APIs are typed against the contract and all produce plans. They differ in how much of the statement you write yourself.

The **ORM client** is where you start on both databases: model-based queries such as `db.orm.public.User.where(...)`. It is more than a query builder; for example, the `.include()` operation coordinates several queries on your behalf to serve higher-order needs, relation traversal above all, and hands back one typed result. For more information, start with [Reading data](https://www.prisma.io/docs/orm/fundamentals/reading-data).

Beneath it, each database family has a typed builder for the queries that the ORM client cannot express, and a raw escape hatch below that. A builder plan compiles to exactly one statement, so what you build is what runs:

|                  | PostgreSQL                                                                                                                             | MongoDB                                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Typed builder    | [The SQL query builder](https://www.prisma.io/docs/orm/fundamentals/advanced-queries): composable joins, grouping, projections                                   | [The pipeline builder](https://www.prisma.io/docs/orm/reference/pipeline-builder): typed aggregation pipelines |
| Raw escape hatch | [Raw SQL](https://www.prisma.io/docs/orm/reference/raw-queries): `fns.raw` fragments spliced into builder queries, or whole statements written with `db.raw.sql` | [Raw commands](https://www.prisma.io/docs/orm/reference/raw-queries) sent to the driver                        |

Raw queries are still plans, so middleware and telemetry see them as any other query. A whole `db.raw.sql` statement declares its row shape with `.returnsRow(spec)`, so its rows come back decoded. `fns.raw` fragments and MongoDB raw commands carry no result shape, so you must handle those values yourself; the [raw queries reference](https://www.prisma.io/docs/orm/reference/raw-queries) explains what that means for different databases.

## The stack behind one package [#the-stack-behind-one-package]

One facade package connects your code to your database. A PostgreSQL project installs `@prisma/orm-postgres`, and its config helper wires up everything underneath: the database **family** (SQL), the **target** dialect (PostgreSQL), the **adapter** that translates plans into that dialect, and the **driver** that holds the network connection. These four names recur in error messages and extension docs; day to day, you configure the one facade package and move on.

Prisma ORM's layering exists for extensibility. Our core remains small, because it is focused; everything around it, including PostgreSQL support, plugs in through the same public interfaces. A new database is supported by writing a new target, adapter and driver, without any need to change the core.

## Capabilities [#capabilities]

A **capability** is a specific feature that a database may or may not support, such as `RETURNING` clauses or vector indexes. Your contract declares the capabilities it needs; the adapter reports what the connected database provides. Prisma ORM compares the two at startup, so a missing feature surfaces as one clear error when the app boots, instead of as a failed query later. See [Supported database features](https://www.prisma.io/docs/orm/contract-authoring/capabilities) for more information.

## Codecs [#codecs]

A **codec** converts values between JavaScript and the database's wire format, in both directions. Every column type in your contract is backed by one: a PostgreSQL `timestamptz` column has a codec that produces a JavaScript `Date` when you read and encodes it back when you write. This means that when you pick a column type in PSL, you are also picking the codec that will handle every value that column carries.

Extensions bring codecs for the types they add: with pgvector installed, a `Vector(1536)` column comes back as a typed vector rather than a string. Raw fragments and raw commands skip this conversion step, so with those you convert the values yourself.

## Extensions [#extensions]

An extension is an installable package that plugs new pieces into the toolchain: column types and their codecs, query operations, index kinds, capabilities, and, at the widest, support for an entire database. One package extends the contract language, the emitted types, the query builders, and migrations together.

Extensions are declared in `prisma.config.ts` and registered on the client:

```ts title="prisma.config.ts"
import { definePrismaConfig } from 'prisma/config';
import pgvector from '@prisma/orm-extension-pgvector/control';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';

export default definePrismaConfig({
  orm: ormConfig({
    contract: './src/prisma/contract.prisma',
    extensions: [pgvector],
    db: {
      connection: process.env['DATABASE_URL']!,
    },
  }),
});
```

Subsequently, `pgvector.Vector(1536)` is a column type in your contract, vector operators appear in the query builder, and `migration plan` knows how to create vector indexes. See [Using extensions](https://www.prisma.io/docs/orm/extensions/using-extensions).

## Middleware [#middleware]

A middleware is a plain object with a name and one or more hooks that run around every query, following the same principles as in Express or Koa. You need only register it once, in the `middleware` option of your client setup. Because every query is a plan, middleware gets a structured object to inspect: it can log it, enforce limits on it, or reject it, without changing how queries are written. That makes middleware the place for one policy that must cover the whole app, such as an authorization rule that examines every plan before it runs.

Three middleware ship with Prisma ORM at present, and they are in the early stages: treat them as working demonstrations of the pattern rather than finished products. [Budgets](https://www.prisma.io/docs/orm/middleware/built-in-budgets) caps row counts and surfaces slow queries, [lints](https://www.prisma.io/docs/orm/middleware/built-in-lints) blocks risky query shapes, and [cache](https://www.prisma.io/docs/orm/middleware/built-in-cache) serves repeated reads from memory. For policy that your app depends on, [write your own](https://www.prisma.io/docs/orm/middleware/authoring-custom-middleware); the middleware API is the durable surface. Start by exploring [How middleware works](https://www.prisma.io/docs/orm/middleware/how-middleware-works).

## Migrations: a graph of contracts [#migrations-a-graph-of-contracts]

A **migration** is a recorded change to your database. Most migrations change the database schema from what one contract describes to what another describes, so each one records which contract hash it starts `from` and which it ends at, `to`. A migration that only changes rows, such as a backfill, starts and ends at the same contract state. On disk, a migration is a directory in your repository that holds the change as editable TypeScript (`migration.ts`), the compiled operations that Prisma ORM runs (`ops.json`), and that `from` and `to` metadata (`migration.json`).

Only one of these commands changes a database: [`db migrate`](https://www.prisma.io/docs/cli/db-migrate) applies the recorded migrations to it. The other `migration ...` commands create and inspect the migration files in your repository, and never connect to a database.

Because every migration records its `from` and `to` hashes, the migrations in a repository form a **graph**: contracts are the nodes, migrations are the edges. When two branches each add a migration and both merge, the graph has a fork and a join, and `db migrate` works out which migrations to run, given the contract state the database matches and the one you name. No renumbering, no rebasing migration files.

A **ref** is a named pointer at a contract, such as `production` or `staging`, managed with [`migration ref`](https://www.prisma.io/docs/cli/migration-ref). Refs let deployment commands target an environment by name: `db migrate --to production`.

If you know Git, the whole vocabulary maps across:

| Git                     | Prisma ORM                         |
| ----------------------- | ---------------------------------- |
| A commit                | A contract, identified by its hash |
| A patch between commits | A migration                        |
| A branch or tag         | A ref                              |
| `HEAD`                  | The database signature             |
| `git checkout <commit>` | `db migrate --to <contract>`       |

Start with [How migrations work](https://www.prisma.io/docs/orm/migrations/how-migrations-work), then [The migration graph](https://www.prisma.io/docs/orm/migrations/the-migration-graph) for the branching story.

## How the CLI commands combine [#how-the-cli-commands-combine]

One rule divides the whole [CLI](https://www.prisma.io/docs/cli): `db ...` commands connect to a live database and can change it, while `contract ...` and `migration ...` commands work on the files in your repository. The one exception is `contract infer`, which reads a live database, changing nothing in it, to write a starter contract file. If you are unsure what a command might touch, its first word provides the answer: only `db` can change a database.

The commands compose into four everyday workflows.

**The development loop.** Edit your contract, emit it, turn the change into a reviewable migration, apply it:

  

#### bun

```bash
bunx prisma@latest contract emit
bunx prisma@latest migration plan --name add_user_phone
bunx prisma@latest db migrate
```

#### pnpm

```bash
pnpm dlx prisma@latest contract emit
pnpm dlx prisma@latest migration plan --name add_user_phone
pnpm dlx prisma@latest db migrate
```

#### yarn

```bash
yarn dlx prisma@latest contract emit
yarn dlx prisma@latest migration plan --name add_user_phone
yarn dlx prisma@latest db migrate
```

#### npm

```bash
npx prisma@latest contract emit
npx prisma@latest migration plan --name add_user_phone
npx prisma@latest db migrate
```

**Prototyping without migration files.** While a schema is still in flux, skip the migration directory and reconcile the database directly. [`db update`](https://www.prisma.io/docs/cli/db-update) diffs the live schema against the emitted contract and applies the difference; `--dry-run` previews it first:

  

#### bun

```bash
bunx prisma@latest contract emit
bunx prisma@latest db update --db "$DATABASE_URL" --dry-run
bunx prisma@latest db update --db "$DATABASE_URL"
```

#### pnpm

```bash
pnpm dlx prisma@latest contract emit
pnpm dlx prisma@latest db update --db "$DATABASE_URL" --dry-run
pnpm dlx prisma@latest db update --db "$DATABASE_URL"
```

#### yarn

```bash
yarn dlx prisma@latest contract emit
yarn dlx prisma@latest db update --db "$DATABASE_URL" --dry-run
yarn dlx prisma@latest db update --db "$DATABASE_URL"
```

#### npm

```bash
npx prisma@latest contract emit
npx prisma@latest db update --db "$DATABASE_URL" --dry-run
npx prisma@latest db update --db "$DATABASE_URL"
```

When the shape settles, switch to `migration plan` so changes become reviewable files.

**Adopting an existing database.** [`contract infer`](https://www.prisma.io/docs/cli/contract-infer) writes a starter contract from a live schema. Review and edit it, emit, then bring the database under contract management: [`db init`](https://www.prisma.io/docs/cli/db-init) applies only additive changes and writes the first signature. If the database already matches the contract exactly, [`db sign`](https://www.prisma.io/docs/cli/db-sign) records the signature without changing anything:

  

#### bun

```bash
bunx prisma@latest contract infer --db "$DATABASE_URL"
bunx prisma@latest contract emit
bunx prisma@latest db init --db "$DATABASE_URL"
```

#### pnpm

```bash
pnpm dlx prisma@latest contract infer --db "$DATABASE_URL"
pnpm dlx prisma@latest contract emit
pnpm dlx prisma@latest db init --db "$DATABASE_URL"
```

#### yarn

```bash
yarn dlx prisma@latest contract infer --db "$DATABASE_URL"
yarn dlx prisma@latest contract emit
yarn dlx prisma@latest db init --db "$DATABASE_URL"
```

#### npm

```bash
npx prisma@latest contract infer --db "$DATABASE_URL"
npx prisma@latest contract emit
npx prisma@latest db init --db "$DATABASE_URL"
```

**Checking in CI, deploying in CD.** [`migration check`](https://www.prisma.io/docs/cli#other-commands) verifies the migration files and graph offline, so it runs in CI with no database. [`db verify`](https://www.prisma.io/docs/cli/db-verify) is its live counterpart: a read-only check that a database satisfies the contract. A deploy pipeline pins environments with refs and migrates to them by name:

  

#### bun

```bash
bunx prisma@latest migration check
bunx prisma@latest db verify --db "$DATABASE_URL"
bunx prisma@latest db migrate --db "$DATABASE_URL" --to production
```

#### pnpm

```bash
pnpm dlx prisma@latest migration check
pnpm dlx prisma@latest db verify --db "$DATABASE_URL"
pnpm dlx prisma@latest db migrate --db "$DATABASE_URL" --to production
```

#### yarn

```bash
yarn dlx prisma@latest migration check
yarn dlx prisma@latest db verify --db "$DATABASE_URL"
yarn dlx prisma@latest db migrate --db "$DATABASE_URL" --to production
```

#### npm

```bash
npx prisma@latest migration check
npx prisma@latest db verify --db "$DATABASE_URL"
npx prisma@latest db migrate --db "$DATABASE_URL" --to production
```

## Prompt your coding agent [#prompt-your-coding-agent]

Projects scaffolded with `create-prisma@latest` install [Prisma ORM skills](https://www.prisma.io/docs/ai/tools/skills#available-skills-for-prisma-8) for your coding agent. Ask your agent to:

* "Using the prisma-8 skill, explain the difference between our contract and the database schema."
* "Show me the plan the SQL query builder produces for this query."
* "Which of our CLI scripts touch the live database, and which are offline?"

## Next steps [#next-steps]

* [The data contract](https://www.prisma.io/docs/orm/contract-authoring/the-data-contract): the concept that this whole page hangs off, in depth.
* [Reading data](https://www.prisma.io/docs/orm/fundamentals/reading-data): put the ORM client to work against your contract.
* [How migrations work](https://www.prisma.io/docs/orm/migrations/how-migrations-work): change your contract, then plan, review, and apply the migration, hands-on.

## Related pages

- [`Coming from Prisma ORM 7`](https://www.prisma.io/docs/orm/coming-from-prisma-orm-7): What each Prisma ORM 7 schema attribute, command, and query is called in Prisma ORM 8, and what is not available.
- [`Extensions`](https://www.prisma.io/docs/orm/extensions): Every package that plugs into Prisma ORM: database packages, column types, indexes, query operations, and middleware, by Prisma and the community.
- [`Overview`](https://www.prisma.io/docs/orm/data-modeling): Describe the data your application needs with models, primary keys, scalar fields, and relations.
- [`Prisma 7`](https://www.prisma.io/docs/orm/v7): Prisma ORM is a next-generation Node.js and TypeScript ORM that provides type-safe database access, migrations, and a visual data editor.
- [`Prisma ORM`](https://www.prisma.io/docs/orm/v6): Learn about Prisma ORM