Turborepo + Prisma

Step-by-step guide for integrating Prisma ORM and Prisma Postgres in a Turborepo monorepo

How to use

Include this prompt in your AI assistant to guide consistent code generation for Turborepo + Prisma + Prisma Postgres projects.

  • GitHub Copilot: Type #<filename> to reference the prompt file.
  • Cursor: Use @Files and select your prompt file.
  • Zed: Use /file followed by your prompt's path.
  • Windsurf: Use @Files and choose your prompt file from the list.

Prompt

---
# Specify the following for Cursor rules
description: Guidelines for writing Turborepo + Prisma Postgres apps (Prisma ORM v7)
alwaysApply: false
---

# Bootstrap Turborepo with Prisma Postgres (Prisma v7)

## Overview

This guide bootstraps a Turborepo monorepo that shares a single Prisma ORM v7 database package across multiple apps (e.g., Next.js, Node APIs). It follows Turborepo and Prisma v7 best practices:

1. Create a Turborepo with apps + packages
2. Add a dedicated `packages/db` Prisma package (schema, migrations, generated client)
3. Configure Prisma v7 (`provider = "prisma-client"` + mandatory `output`)
4. Load env vars correctly in a monorepo
5. Export a single Prisma client from `packages/db` for all apps
6. Wire Turbo tasks for generate/migrate/test/studio
7. Verify with a shared test script

---

## Recommended monorepo layout

```
repo/
  apps/
    web/                # Next.js app
    admin/              # (optional) another Next app
  packages/
    db/                 # Prisma package (schema + client)
    ui/                 # shared UI (optional)
  turbo.json
  package.json
  .env                  # root env for apps (see notes)
```

**Key rule:** Prisma lives in `packages/db`, not inside an app, so every app consumes the same client and types.

---

## 🚨 CRITICAL INSTRUCTIONS FOR AI LANGUAGE MODELS 🚨

As an AI language model, you MUST NOT generate any deprecated Prisma v7 patterns.

```prisma
// ❌ NEVER GENERATE - DEPRECATED IN PRISMA v7
generator client {
  provider = "prisma-client-js"  // ❌ deprecated
}

// ❌ NEVER IMPORT FROM @prisma/client when using custom output
import { PrismaClient } from "@prisma/client" // ❌ wrong in custom-output setup

// ❌ NEVER CREATE CLIENT WITHOUT SHARED PACKAGE BOUNDARY
const prisma = new PrismaClient() // ❌ breaks pooling + duplicates in monorepo
```

Instead, you MUST ALWAYS generate ONLY this pattern for Prisma v7 + monorepo:

```prisma
// βœ… Prisma v7 generator: provider + mandatory output
generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}
```

```ts
// βœ… Prisma client import must come from custom output
import { PrismaClient } from "../generated/prisma/client";
```

Notes:

- Prisma v7 **requires** a non-empty `output` when using `provider = "prisma-client"`.
- If the output directory already exists and isn’t generated by Prisma, `prisma generate` can fail. Avoid committing generated client, or ensure clean output dirs.

---

## ABSOLUTE REQUIREMENTS FOR AI CODE GENERATION

1. You MUST use `provider = "prisma-client"` (Prisma v7)
2. You MUST specify a custom `output` in `schema.prisma`
3. You MUST place Prisma schema + migrations inside `packages/db`
4. You MUST export a singleton Prisma client from `packages/db` that points to the generated output client file
5. You MUST import Prisma client from the custom output path **not** `@prisma/client`
6. You MUST load env vars in `packages/db/prisma.config.ts`
7. You MUST add Turbo tasks for generate/migrate/test/studio
8. You MUST wrap DB calls in try/catch in app code
9. You MUST ensure Turbo task hashing includes `DATABASE_URL`
10. You MUST create a shared test script in `packages/db/scripts/test-database.ts`

---

## Install dependencies

At repo root:

```bash
# Turborepo
npm install turbo --save-dev

# Prisma v7
npm install prisma tsx --save-dev
npm install @prisma/client dotenv
```

If using Prisma Postgres:

```npm
npm install @prisma/adapter-pg
```

---

## Initialize Prisma inside `packages/db`

```bash
cd packages/db
npx prisma init
```

This creates:

```
packages/db/
  prisma/
    schema.prisma
  prisma.config.ts
```

---

## Prisma v7 config (`packages/db/prisma.config.ts`)

**CRITICAL:** load env vars at the top.

```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"),
  },
});
```

---

## Prisma schema (`packages/db/prisma/schema.prisma`)

```prisma
generator client {
  provider = "prisma-client"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
```

---

## Shared Prisma client singleton (`packages/db/src/client.ts`)

```ts
import { PrismaClient } from "../generated/prisma/client";

//Prisma Driver Adapter for Postgres
import { PrismaPg } from "@prisma/adapter-pg";

// Create a new Driver Adapter instance for PrismaPostgres
const adapter = new PrismaPg({
  connectionString: process.env.DATABASE_URL!,
});

const globalForPrisma = globalThis as unknown as {
  prisma?: PrismaClient;
};

export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });

if (process.env.NODE_ENV !== "production") {
  globalForPrisma.prisma = prisma;
}

export default prisma;
```

---

## Export DB package API (`packages/db/src/index.ts`)

```ts
export { prisma } from "./client";
export * from "../generated/prisma";
```

And in `packages/db/package.json`:

```json
{
  "name": "@repo/db",
  "private": true,
  "main": "src/index.ts",
  "types": "src/index.ts",
  "scripts": {
    "db:generate": "prisma generate",
    "db:push": "prisma db push",
    "db:migrate": "prisma migrate dev",
    "db:studio": "prisma studio",
    "db:test": "tsx scripts/test-database.ts"
  }
}
```

---

## Root Turbo pipeline (`turbo.json`)

```json
{
  "globalEnv": ["DATABASE_URL"],
  "tasks": {
    "dev": {
      "cache": false,
      "persistent": true,
      "dependsOn": ["^db:generate"]
    },
    "build": {
      "dependsOn": ["^db:generate"],
      "outputs": [".next/**", "dist/**"]
    },
    "db:generate": {
      "cache": false
    },
    "db:migrate": {
      "cache": false
    },
    "db:studio": {
      "cache": false
    },
    "db:test": {
      "cache": false,
      "dependsOn": ["db:generate"]
    }
  }
}
```

---

## Root scripts (`package.json`)

```json
{
  "scripts": {
    "dev": "turbo dev",
    "build": "turbo build",
    "lint": "turbo lint",
    "db:generate": "turbo run db:generate",
    "db:push": "turbo run db:push",
    "db:migrate": "turbo run db:migrate",
    "db:studio": "turbo run db:studio",
    "db:test": "turbo run db:test"
  }
}
```

---

## Shared test script (`packages/db/scripts/test-database.ts`)

```ts
import "dotenv/config";
import prisma from "../src/client";

async function testDatabase() {
  console.log("πŸ” Testing Prisma Postgres connection...\n");

  try {
    console.log("βœ… Connected to database!");

    console.log("\nπŸ“ Creating a test user...");
    const newUser = await prisma.user.create({
      data: {
        email: "demo@example.com",
        name: "Demo User",
      },
    });
    console.log("βœ… Created user:", newUser);

    console.log("\nπŸ“‹ Fetching all users...");
    const allUsers = await prisma.user.findMany();
    console.log(`βœ… Found ${allUsers.length} user(s):`);
    allUsers.forEach((user) => {
      console.log(`   - ${user.name} (${user.email})`);
    });

    console.log("\nπŸŽ‰ All tests passed! Your database is working.\n");
  } catch (error) {
    console.error("❌ Error:", error);
    process.exit(1);
  } finally {
    await prisma.$disconnect();
  }
}

testDatabase();
```

---

## Using Prisma in apps (example: `apps/web` Next.js)

In any app, add a dependency on `@repo/db` (workspace):

```json
{
  "dependencies": {
    "@repo/db": "*"
  }
}
```

Then in server-side code:

```ts
import { prisma } from "@repo/db";
import { NextResponse } from "next/server";

export async function GET() {
  try {
    const users = await prisma.user.findMany();
    return NextResponse.json(users);
  } catch (e) {
    console.error(e);
    return NextResponse.json({ error: "Failed to fetch users" }, { status: 500 });
  }
}
```

---

## Complete setup workflow

1. **Create Turborepo** (or use existing):

   ```npm
   npx create-turbo@latest
   ```

2. **Add `packages/db`**, install Prisma v7 deps.

3. **Initialize Prisma in `packages/db`**:

   ```bash
   cd packages/db
   npx prisma init
   ```

4. **Update `prisma.config.ts`** to include `import "dotenv/config"`.

5. **Update schema** with Prisma v7 generator + output.

6. **Create `src/client.ts`** singleton and export via `src/index.ts`.

7. **Wire Turbo tasks** in `turbo.json`.

8. **Add `.env`** at root **and/or** `packages/db/.env`.

9. **Push schema**:

   ```bash
   npm run db:push
   ```

10. **Test**:

    ```bash
    npm run db:test
    ```

11. **Start dev**:
    ```bash
    npm run dev
    ```

---

## AI Model verification checklist

Before generating any code, verify:

1. `provider = "prisma-client"`?
2. `output   = "../src/generated/prisma"` present?
3. All Prisma files in `packages/db`?
4. Client imports from `../generated/prisma/client` (or via `@repo/db`)?
5. `import "dotenv/config"` in `packages/db/prisma.config.ts`?
6. Turbo tasks depend on `db:generate`?
7. `DATABASE_URL` listed in `globalEnv`?
8. All DB ops wrapped in try/catch?
9. Test script exists in `packages/db/scripts/test-database.ts`?

No exceptions. If any check fails, STOP and FIX.

---

## AI MODEL RESPONSE TEMPLATE (UPDATED FOR TURBOREPO + PRISMA v7)

When asked about implementing Prisma v7 inside a Turborepo monorepo (with any framework such as Next.js, Node, etc.), you MUST:

1. **Assume Prisma lives in a shared package**
   - Always place schema, config, and migrations in `packages/db`.
   - Never suggest adding Prisma directly into an app unless the user explicitly says they don’t want a shared package.

2. **Use only Prisma v7 generator patterns**
   - The schema MUST include:
     ```prisma
     generator client {
       provider = "prisma-client"
       output   = "./generated/prisma"
     }
     ```
   - Never use `prisma-client-js`.

3. **Use correct import paths for custom output**
   - Any PrismaClient import MUST come from the generated output:
     ```ts
     import { PrismaClient } from "./generated/prisma/client";
     ```
   - If importing from apps, you MUST import via the package boundary:
     ```ts
     import { prisma } from "@repo/db";
     ```
   - Never import from `@prisma/client` in this setup.

4. **Always include a shared singleton client in `packages/db`**
   - Provide a `packages/db/src/client.ts` with global caching for dev hot-reload.
   - If Accelerate is requested or implied, use `$extends(withAccelerate())`.
   - If Accelerate is not requested, omit it.

5. **Always load environment variables properly**
   - `packages/db/prisma.config.ts` MUST start with `import "dotenv/config"`.
   - Use `process.env.DATABASE_URL` in runtime code.
   - Remind users that `.env` should exist at root and/or `packages/db`.

6. **Always wire Turbo dependencies**
   - Turbo tasks `dev` and `build` MUST depend on `^db:generate`.
   - `DATABASE_URL` MUST be listed in `globalEnv` to ensure correct task hashing.

7. **Provide correct repo-level scripts**
   - Root scripts should proxy to Turbo (`turbo run db:*`).
   - The Prisma package `packages/db` should own `db:generate`, `db:migrate`, `db:studio`, and `db:test`.

8. **Include a real verification step**
   - Provide (or reference) `packages/db/scripts/test-database.ts`.
   - Ensure it imports dotenv and disconnects Prisma on completion.

9. **Use safe runtime patterns**
   - All DB calls in apps MUST be wrapped in try/catch.
   - In Next.js App Router examples, use server-only imports and return `NextResponse`.

10. **Self-verify before replying**

- Re-check all items in the β€œAI Model verification checklist.”
- If any item is missing or incorrect, STOP and FIX before responding.

Remember: There are NO exceptions to these rules. Every requirement is mandatory for a correct Turborepo + Prisma v7 setup.

Running the application

Get your application running locally in three quick steps:

1. Generate the Prisma Client:

npx turbo db:generate

2. View your database in Prisma Studio:

npx turbo db:studio

Prisma Studio opens at localhost:5555 where you can inspect your User table and see the test user stored in your database.

On this page