# Turborepo (/docs/guides/deployment/turborepo)

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

Share one Prisma 8 database package across the apps in a Turborepo monorepo, with contract emit and migrations wired into turbo tasks.

Location: Guides > Deployment > Turborepo

## Introduction [#introduction]

This guide shows you how to set up Prisma 8 as a standalone package in a [Turborepo](https://turborepo.dev/docs) monorepo, so that every app shares one database client, one contract, and one migration workflow. You scaffold a monorepo, add a `packages/database` package that owns the Prisma 8 setup, wire its tasks into `turbo.json`, and render users from the `web` Next.js app.

Every command and output below was run end to end against a local PostgreSQL database.

> [!NOTE]
> Using Prisma 7?
> 
> Prisma 8 is the current release of Prisma ORM. Prisma 7 remains fully supported; the Prisma 7 version of this guide is at [/guides/v7/deployment/turborepo](https://www.prisma.io/docs/guides/v7/deployment/turborepo).

## Prerequisites [#prerequisites]

* [Node.js](https://nodejs.org) 24 or later
* [pnpm](https://pnpm.io) (this guide uses pnpm; the Turborepo scaffold also supports npm, yarn, and Bun)
* A PostgreSQL connection string, or nothing at all: `npx 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
Set up Prisma 8 as a shared database package in a new Turborepo monorepo and render users from the web app.

1. Scaffold: `npx create-turbo@latest turborepo-prisma` with pnpm. Then run `npx prisma@latest init` at the monorepo root so the Prisma agent skills are installed and stay current, and use them.
2. Create `packages/database` with `{ "name": "@repo/db", "version": "0.0.0", "private": true }` as its package.json. Because pnpm refuses to run dependency build scripts until they are allowed, add `allowBuilds: { esbuild: true, msgpackr-extract: true, workerd: true }` to `pnpm-workspace.yaml` first. Then run `npx prisma@latest orm init --yes --target postgres --authoring psl` inside `packages/database`, and move the package to the current CLI with `pnpm add -D prisma@latest --filter @repo/db`.
3. 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. Write it as `DATABASE_URL` into `packages/database/.env`.
4. In `packages/database/package.json`, add `"exports": { ".": "./src/index.ts" }` and the scripts `contract:emit` (`prisma contract emit`), `migration:plan` (`prisma migration plan`), `db:migrate` (`prisma db migrate --advance-ref db`), `migration:status` (`prisma migration status`), and `check-types` (`tsc --noEmit`). Create `src/index.ts` that re-exports `db` from `./prisma/db` and the `Contract` type from `./prisma/contract.d`.
5. In `turbo.json`, add `"globalEnv": ["DATABASE_URL"]`, a cached `contract:emit` task with inputs `src/prisma/contract.prisma` and `prisma.config.ts` and outputs `src/prisma/contract.json` and `src/prisma/contract.d.ts`, make `build`, `dev`, and `check-types` depend on `^contract:emit`, add `migration:plan` (dependsOn `contract:emit`, cache false) and `db:migrate` (cache false).
6. Run `pnpm turbo run migration:plan --filter=@repo/db -- --name init`, review the DDL preview, then `pnpm turbo run db:migrate --filter=@repo/db`.
7. Add `"@repo/db": "workspace:*"` to `apps/web/package.json`, run `pnpm install`, copy `packages/database/.env` to `apps/web/.env`, and replace `apps/web/app/page.tsx` with a server component that exports `const dynamic = "force-dynamic"` and renders `await db.orm.public.User.select("id", "email", "name").all()` as a list, following https://www.prisma.io/docs/guides/deployment/turborepo.md.
8. Start `pnpm turbo run dev --filter=web` in the background, wait until it reports ready, verify http://localhost:3000 renders, then stop the dev server.
```

## 1. Scaffold the monorepo [#1-scaffold-the-monorepo]

Create a Turborepo monorepo named `turborepo-prisma`:

  

#### bun

```bash
bunx create-turbo@latest turborepo-prisma
```

#### pnpm

```bash
pnpm dlx create-turbo@latest turborepo-prisma
```

#### yarn

```bash
yarn dlx create-turbo@latest turborepo-prisma
```

#### npm

```bash
npx create-turbo@latest turborepo-prisma
```

When asked which package manager to use, pick `pnpm`. The scaffold creates two Next.js apps and three shared packages, then installs dependencies:

```text no-copy
>>> Creating a new Turborepo with:

Application packages
 - apps/docs
 - apps/web
Library packages
 - packages/eslint-config
 - packages/typescript-config
 - packages/ui

>>> Success! Created your Turborepo at turborepo-prisma
```

Move into the project root:

```bash
cd turborepo-prisma
```

## 2. Create the `database` package [#2-create-the-database-package]

### 2.1. Create the package [#21-create-the-package]

Create a `database` directory inside `packages` with a minimal `package.json`:

```bash
mkdir -p packages/database
```

```json title="packages/database/package.json"
{
  "name": "@repo/db",
  "version": "0.0.0",
  "private": true
}
```

### 2.2. Allow the build scripts Prisma needs [#22-allow-the-build-scripts-prisma-needs]

pnpm does not run dependency build scripts until you allow them, and pnpm 11 and later fail the install when they find scripts they were not told about (the `allowBuilds` key exists since pnpm 10.26). Prisma 8's toolchain ships three packages with build scripts, so declare them in `pnpm-workspace.yaml` before you install anything:

```yaml title="pnpm-workspace.yaml"
packages:
  - "apps/*"
  - "packages/*"
allowBuilds: # [!code ++]
  esbuild: true # [!code ++]
  msgpackr-extract: true # [!code ++]
  workerd: true # [!code ++]
```

Skip this step on npm, yarn, or Bun. If you forget it, `orm init` in the next step stops with `CLI.INIT_INSTALL_FAILED`; run `pnpm approve-builds`, then re-run the `pnpm add` commands it printed and `pnpm contract:emit`.

### 2.3. Initialize Prisma 8 [#23-initialize-prisma-8]

Run `orm init` inside the package. It is the existing-project path: it adds Prisma 8 to the package you just created instead of scaffolding a new app.

```bash
cd packages/database
```

  

#### 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
```

`--yes` accepts the defaults for the remaining prompts (the Prisma schema language for the contract, and `src/prisma/contract.prisma` as its path); drop it to answer them yourself.

```text no-copy
✔ pnpm add @prisma/orm-postgres dotenv
✔ pnpm add -D prisma@latest @types/node
✔ pnpm add -D @prisma/cli-engine
✔ Emit the contract
│  target:     postgres
│  authoring:  psl
│  schema:     src/prisma/contract.prisma

written
├─ src/prisma/contract.prisma
├─ prisma.config.ts
├─ src/prisma/db.ts
├─ prisma-8.md
├─ .env.example
├─ tsconfig.json
├─ .gitignore
├─ .gitattributes
└─ package.json

✔ Done. Open prisma-8.md to get started.
```

`orm init` detects pnpm from the workspace, adds the packages to `@repo/db`, sets `"type": "module"`, and writes four files that matter for the rest of this guide:

* `src/prisma/contract.prisma`: your schema. It starts with `User` and `Post` models.
* `src/prisma/contract.json` and `src/prisma/contract.d.ts`: the emitted contract the runtime and the CLI read. They replace Prisma 7's generated client: there is no `prisma generate` step and no generated directory to ignore. Commit both files; `.gitattributes` already marks them as generated so they collapse in diffs.
* `src/prisma/db.ts`: the client instance, typed by the contract.
* `prisma.config.ts`: where the CLI finds the contract and the database connection.

The generated `db.ts` reads `DATABASE_URL` from the environment and is the module every app will import:

```typescript title="packages/database/src/prisma/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 db = postgres<Contract>({
  contractJson,
  url: process.env['DATABASE_URL']!,
});
```

### 2.4. Set the database connection [#24-set-the-database-connection]

Create `packages/database/.env` with your PostgreSQL connection string. Use your own, or create a Prisma Postgres database with `npx create-db@latest`; it prints a connection string and a claim URL you can open to keep the database.

```bash title="packages/database/.env"
DATABASE_URL="postgres://user:password@localhost:5432/turborepo_prisma"
```

`prisma.config.ts` and `db.ts` both load this file through `dotenv/config`, so every CLI command in this package and every query at runtime picks it up. The root `.gitignore` from the scaffold already ignores `.env` files.

### 2.5. Review the contract [#25-review-the-contract]

Open `src/prisma/contract.prisma`. The starter contract already has the two models this guide renders, so leave it as it is for now; you will change it in step 7.

```prisma title="packages/database/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()
}
```

### 2.6. Add scripts and export the client [#26-add-scripts-and-export-the-client]

Add the database scripts and a package entrypoint to `packages/database/package.json`. `orm init` already added `contract:emit`; the rest map to the Prisma 8 migration loop:

```json title="packages/database/package.json"
{
  "name": "@repo/db",
  "type": "module",
  "version": "0.0.0",
  "private": true,
  "exports": { // [!code ++]
    ".": "./src/index.ts" // [!code ++]
  }, // [!code ++]
  "scripts": {
    "contract:emit": "prisma contract emit",
    "migration:plan": "prisma migration plan", // [!code ++]
    "migration:status": "prisma migration status", // [!code ++]
    "db:migrate": "prisma db migrate --advance-ref db", // [!code ++]
    "db:verify": "prisma db verify", // [!code ++]
    "check-types": "tsc --noEmit" // [!code ++]
  },
  "dependencies": {
    "@prisma/orm-postgres": "8.0.0",
    "dotenv": "^17.4.2"
  },
  "devDependencies": {
    "@prisma/cli-engine": "0.3.0",
    "@types/node": "26.4.1",
    "prisma": "8.0.0"
  }
}
```

Keep the versions `orm init` and the upgrade wrote for you; the ones shown are placeholders. The scripts replace the Prisma 7 trio of `db:generate`, `db:migrate`, and `db:deploy`:

* `contract:emit` compiles `contract.prisma` into `contract.json` and `contract.d.ts`. It is offline and deterministic, which is what makes it cacheable in Turborepo.
* `migration:plan` diffs the emitted contract against your migration history and writes a reviewable migration directory. Also offline.
* `db:migrate` applies pending migrations to `DATABASE_URL`, in development and in CI alike. `--advance-ref db` records the applied state in `migrations/app/refs/db.json`, so the next `migration:plan` knows where to start from and plans only the delta.

Then create the package entrypoint. It re-exports the client and the contract type so apps import one module:

```typescript title="packages/database/src/index.ts"
export { db } from "./prisma/db";
export type { Contract } from "./prisma/contract.d";
```

This follows Turborepo's [Just-in-Time packaging](https://turborepo.dev/docs/core-concepts/internal-packages#just-in-time-packages) pattern: the package exports TypeScript source and the consuming app's bundler compiles it. Next.js handles this for workspace packages without extra configuration.

> [!WARNING]
> If a consumer is not using a bundler, use the [Compiled Packages](https://turborepo.dev/docs/core-concepts/internal-packages#compiled-packages) strategy instead, and add `src/prisma/contract.json` to the files you ship.

## 3. Configure tasks in `turbo.json` [#3-configure-tasks-in-turbojson]

Go back to the project root and wire the database tasks into `turbo.json`:

```bash
cd ../..
```

```json title="turbo.json"
{
  "$schema": "https://turborepo.dev/schema.json",
  "ui": "tui",
  "globalEnv": ["DATABASE_URL"], // [!code ++]
  "tasks": {
    "build": {
      "dependsOn": ["^build", "^contract:emit"], // [!code highlight]
      "inputs": ["$TURBO_DEFAULT$", ".env*"],
      "outputs": [".next/**", "!.next/cache/**", "!.next/dev/**"]
    },
    "lint": {
      "dependsOn": ["^lint"]
    },
    "check-types": {
      "dependsOn": ["^check-types", "^contract:emit"] // [!code highlight]
    },
    "dev": {
      "dependsOn": ["^contract:emit"], // [!code ++]
      "cache": false,
      "persistent": true
    },
    "contract:emit": { // [!code ++]
      "inputs": ["src/prisma/contract.prisma", "prisma.config.ts"], // [!code ++]
      "outputs": ["src/prisma/contract.json", "src/prisma/contract.d.ts"] // [!code ++]
    }, // [!code ++]
    "migration:plan": { // [!code ++]
      "dependsOn": ["contract:emit"], // [!code ++]
      "cache": false // [!code ++]
    }, // [!code ++]
    "db:migrate": { // [!code ++]
      "cache": false // [!code ++]
    } // [!code ++]
  }
}
```

What each entry does:

* `contract:emit` is a normal cached task. Its inputs are the contract source and the Prisma config, and its outputs are the two emitted files. When nothing changed, Turborepo replays the cache hit; when the contract changed, it re-emits before anything depends on it.
* `build`, `dev`, and `check-types` depend on `^contract:emit`, so any app that depends on `@repo/db` gets a fresh contract before it compiles. In Prisma 7 this slot held `db:generate`; here the artifacts are committed, and the dependency keeps them from going stale when someone edits the contract and forgets to emit.
* `migration:plan` depends on the package's own `contract:emit`, so a plan always diffs the current contract. Planning and migrating are never cached because they change files on disk and the database.
* `globalEnv` lets `DATABASE_URL` from your shell reach every task and makes it part of the task hash. The `.env*` input on `build` does the same for the per-app `.env` files.

## 4. Plan and apply the first migration [#4-plan-and-apply-the-first-migration]

Plan the first migration from the project root. Turborepo runs `contract:emit` first, then passes `--name init` through to `migration plan`:

```bash
pnpm turbo run migration:plan --filter=@repo/db -- --name init
```

```text no-copy
@repo/db:contract:emit: cache miss, executing
@repo/db:contract:emit: ✔ Emitted contract.json and contract.d.ts
@repo/db:migration:plan: $ prisma migration plan --name init
@repo/db:migration:plan: ✔ Planned 6 operation(s)
@repo/db:migration:plan: migrations/app/20260910T1601_init
@repo/db:migration:plan: ├─ Create schema "public"
@repo/db:migration:plan: ├─ Create table "post"
@repo/db:migration:plan: ├─ Create table "user"
@repo/db:migration:plan: ├─ Add unique constraint on "user" (email)
@repo/db:migration:plan: ├─ Create index "post_authorId_idx_e47547ed" on "post"
@repo/db:migration:plan: └─ Add foreign key "post_authorId_fkey" on "post"
@repo/db:migration:plan: from:       (baseline)
@repo/db:migration:plan: to:         1e8412e162dbbe69f4bb3bf8d07f0280ae67eaab15c34dcf201e67468315428d
@repo/db:migration:plan: ℹ DDL preview
@repo/db:migration:plan: CREATE SCHEMA IF NOT EXISTS "public";
@repo/db:migration:plan: CREATE TABLE "public"."post" (
...
```

The command is offline: it writes `packages/database/migrations/app/<timestamp>_init/` with the migration as TypeScript (`migration.ts`), the compiled operations (`ops.json`), and the history marker (`migration.json`), and prints the exact DDL it will run. Review it, then apply it:

```bash
pnpm turbo run db:migrate --filter=@repo/db
```

```text no-copy
@repo/db:db:migrate: $ prisma db migrate --advance-ref db
@repo/db:db:migrate: ✔ Applied 1 migration(s) (6 operation(s)) across 1 contract space(s)
@repo/db:db:migrate: App space
@repo/db:db:migrate: ├─ Create schema "public"
@repo/db:db:migrate: ├─ Create table "post"
@repo/db:db:migrate: ├─ Create table "user"
@repo/db:db:migrate: ├─ Add unique constraint on "user" (email)
@repo/db:db:migrate: ├─ Create index "post_authorId_idx_e47547ed" on "post"
@repo/db:db:migrate: ├─ Add foreign key "post_authorId_fkey" on "post"
@repo/db:db:migrate: └─ marker 1e8412e162dbbe69f4bb3bf8d07f0280ae67eaab15c34dcf201e67468315428d
@repo/db:db:migrate: ✔ Advanced ref "db" → 1e8412e162dbbe69f4bb3bf8d07f0280ae67eaab15c34dcf201e67468315428d
```

Commit the `migrations/` directory with the rest of the package. The same `db:migrate` task is what CI or a deploy hook runs against staging and production; there is no separate `migrate deploy` command in Prisma 8.

## 5. Use the `database` package in the web app [#5-use-the-database-package-in-the-web-app]

### 5.1. Add the dependency [#51-add-the-dependency]

Add `@repo/db` to `apps/web/package.json`:

```json title="apps/web/package.json"
{
  // ...
  "dependencies": {
    "@repo/db": "workspace:*", // [!code ++]
    "@repo/ui": "workspace:*",
    // ...
  }
  // ...
}
```

Link it from the project root:

```bash
pnpm install
```

### 5.2. Render users in a server component [#52-render-users-in-a-server-component]

Replace `apps/web/app/page.tsx` with a server component that queries the database through the shared client:

```tsx title="apps/web/app/page.tsx"
import { db } from "@repo/db";
import styles from "./page.module.css";

export const dynamic = "force-dynamic";

export default async function Home() {
  const users = await db.orm.public.User.select("id", "email", "name").all();

  return (
    <div className={styles.page}>
      <main className={styles.main}>
        <h1>Users</h1>
        {users.length === 0 ? (
          <p>No users added yet</p>
        ) : (
          <ul>
            {users.map((user) => (
              <li key={user.id}>
                {user.name ?? "Anonymous"} ({user.email})
              </li>
            ))}
          </ul>
        )}
      </main>
    </div>
  );
}
```

Model access is namespace-qualified on PostgreSQL, so the `User` model lives at `db.orm.public.User`, and `select(...)` narrows both the query and the row type. `dynamic = "force-dynamic"` tells Next.js to run the query per request; without it, `next build` prerenders the page once and freezes the user list at build time.

### 5.3. Give the app its connection string [#53-give-the-app-its-connection-string]

Each app reads its own `.env`, so copy the one from the database package:

```bash
cp packages/database/.env apps/web/.env
```

If you would rather keep a single value, export `DATABASE_URL` in your shell instead; the `globalEnv` entry from step 3 passes it into every task. Turborepo [recommends per-package `.env` files](https://turborepo.dev/docs/crafting-your-repository/using-environment-variables#use-env-files-in-packages); for one shared file across the monorepo, see the [`dotenvx` guide for Turborepo](https://dotenvx.com/docs/monorepos/turborepo).

### 5.4. Seed a couple of users [#54-seed-a-couple-of-users]

The page will render "No users added yet" against an empty database. Add a small seed script to the database package so there is something to see. It runs with `tsx`, because Node.js needs file extensions on imports and the package uses extensionless, bundler-style imports:

```bash
pnpm add -D tsx --filter @repo/db
```

```typescript title="packages/database/src/seed.ts"
import { db } from "./index";

const existing = await db.orm.public.User.select("id").all();
if (existing.length === 0) {
  await db.orm.public.User.create({ email: "alice@prisma.io", name: "Alice" });
  await db.orm.public.User.create({ email: "bob@prisma.io", name: "Bob" });
}
console.log(await db.orm.public.User.select("id", "email", "name").all());
await db.runtime().close();
```

Add it as a script and run it:

```json title="packages/database/package.json"
{
  "scripts": {
    // ...
    "seed": "tsx src/seed.ts" // [!code ++]
  }
}
```

```bash
pnpm --filter @repo/db seed
```

```text no-copy
[
  { id: 1, email: 'alice@prisma.io', name: 'Alice' },
  { id: 2, email: 'bob@prisma.io', name: 'Bob' }
]
```

The script closes the client at the end because it is a one-off process; the web app never does, since its connection pool lives for the life of the server.

## 6. Run the project [#6-run-the-project]

Start the web app from the project root:

```bash
pnpm turbo run dev --filter=web
```

```text no-copy
@repo/db:contract:emit: cache hit, replaying logs
web:dev: ▲ Next.js 16.3.4 (Turbopack)
web:dev: - Local:         http://localhost:3000
web:dev: - Environments: .env
web:dev: ✓ Ready in 1005ms
```

Turborepo replays the cached `contract:emit` for `@repo/db`, then starts Next.js. Open [http://localhost:3000](http://localhost:3000): the page lists the seeded users.

```text no-copy
Users
Alice (alice@prisma.io)
Bob (bob@prisma.io)
```

`pnpm turbo run build --filter=web` and `pnpm turbo run check-types` go through the same `^contract:emit` dependency, so a production build or a type check never sees a stale contract.

## 7. Change the schema [#7-change-the-schema]

The loop for every later change is: edit the contract, plan, review, apply. Add a `bio` field to `User`:

```prisma title="packages/database/src/prisma/contract.prisma"
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  username  String?
  name      String?
  bio       String? // [!code ++]
  posts     Post[]
  createdAt TimestamptzString @default(now())
  updatedAt temporal.updatedAtString()
}
```

Plan it. `contract:emit` sees the changed input and re-emits, and because `db:migrate` advanced the `db` ref, the planner starts from the applied state and plans only the delta:

```bash
pnpm turbo run migration:plan --filter=@repo/db -- --name add-user-bio
```

```text no-copy
@repo/db:contract:emit: cache miss, executing
@repo/db:migration:plan: ✔ Planned 1 operation(s)
@repo/db:migration:plan: migrations/app/20260910T1601_add_user_bio
@repo/db:migration:plan: └─ Add column "bio" to "user"
@repo/db:migration:plan: from:       1e8412e162dbbe69f4bb3bf8d07f0280ae67eaab15c34dcf201e67468315428d
@repo/db:migration:plan: to:         155ebf55586e71f917e8b534544f97d97523544e862ae74b8e70d2af933df807
@repo/db:migration:plan: ℹ DDL preview
@repo/db:migration:plan: ALTER TABLE "public"."user" ADD COLUMN "bio" text;
```

Apply it, then check where the database stands:

```bash
pnpm turbo run db:migrate --filter=@repo/db
pnpm --filter @repo/db migration:status
```

```text no-copy
○   155ebf5  @contract @db (db)
│↑  20260910T1601_add_user_bio  1e8412e → 155ebf5  1 ops  ✓ applied
○   1e8412e
│↑  20260910T1601_init                ∅ → 1e8412e  6 ops  ✓ applied
○   ∅
✔ Up to date
```

The next `pnpm turbo run dev --filter=web` re-emits the contract before Next.js starts, and `user.bio` is available in the page's types. For a local change you do not want to keep as a migration, `npx prisma@latest db update` reconciles the database with the contract directly; see [db update](https://www.prisma.io/docs/cli/db-update).

## Common gotchas [#common-gotchas]

> [!WARNING]
> Without a `db` ref or `--from`, `migration plan` has no origin and refuses with `MIGRATION.PLAN_ORIGIN_UNKNOWN` once migrations exist, because a plan from an empty database would recreate every table. The `--advance-ref db` flag on `db:migrate` keeps the ref current. If you applied a migration without it, point the ref at that migration once: `npx prisma@latest migration ref set db <migration directory name>`.

> [!WARNING]
> In the web app, never call `db.runtime().close()` in a page or route handler; the client's connection pool is shared across requests. Close it only in one-off scripts like the seed.

> [!NOTE]
> `orm init` picks the package manager from the workspace. Run it after `create-turbo` has installed dependencies, so it finds `pnpm-lock.yaml` and adds packages with `pnpm add` instead of guessing.

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

Run [`npx prisma@latest init`](https://www.prisma.io/docs/cli/init) once at the monorepo root to install the [Prisma 8 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. It adds a root `prisma.config.ts` that only configures the skills, a `prisma` dev dependency, and a `postinstall` hook that re-syncs them. Prompts that map to this guide:

* "Using the prisma-8 skill, add a `apps/docs` page that lists posts with their authors through `@repo/db`."
* "Add a `published Boolean @default(false)` field to Post, plan the migration, and show me the DDL before applying it."
* "Add a `db:verify` task to turbo.json and run it in CI after `db:migrate`."

## Next steps [#next-steps]

* [Learn the fundamentals](https://www.prisma.io/docs/orm/fundamentals/reading-data): filtering, sorting, pagination, and writes.
* [How migrations work](https://www.prisma.io/docs/orm/migrations/how-migrations-work): the plan, review, apply loop your `turbo.json` tasks now run.
* [Read the Prisma 8 overview](https://www.prisma.io/docs/orm) for the concepts behind contracts and typed queries.
* [Turborepo docs](https://turborepo.dev/docs) and [Next.js docs](https://nextjs.org/docs) for the rest of the monorepo.

## Related pages

- [`Bun workspaces`](https://www.prisma.io/docs/guides/deployment/bun-workspaces): Set up Prisma 8 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.
- [`Cloudflare Workers`](https://www.prisma.io/docs/guides/deployment/cloudflare-workers): Add Prisma 8 to a Cloudflare Worker, query PostgreSQL from the fetch handler with the nodejs_compat flag, and deploy it with Wrangler.
- [`Docker`](https://www.prisma.io/docs/guides/deployment/docker): Build an Express app on Prisma 8, run PostgreSQL from Docker Compose, then run the app and the database together in containers.
- [`pnpm workspaces`](https://www.prisma.io/docs/guides/deployment/pnpm-workspaces): Set up Prisma 8 in a shared database package inside a pnpm workspaces monorepo and query it from a Next.js app.