apps/docs/content/docs/guides/deployment/turborepo.mdx
Prisma is a powerful ORM for managing databases, and Turborepo simplifies monorepo workflows. By combining these tools, you can create a scalable, modular architecture for your projects.
This guide will show you how to set up Prisma as a standalone package in a Turborepo monorepo, enabling efficient configuration, type sharing, and database management across multiple apps.
To set up a Turborepo monorepo named turborepo-prisma, run the following command:
npx create-turbo@latest turborepo-prisma
You'll be prompted to select your package manager, this guide will use npm:
:::info
npm:::
After the setup, navigate to the project root directory:
cd turborepo-prisma
database package to the monorepoCreate a database directory inside packages and navigate into it:
mkdir -p packages/database
cd packages/database
Then initialize it with a package.json:
{
"name": "@repo/db",
"version": "0.0.0"
}
Then install the required Prisma ORM dependencies:
npm install prisma --save-dev
npm install @prisma/client @prisma/adapter-pg pg dotenv
:::info
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.
:::
Inside the database directory, initialize Prisma by running:
npx prisma init --db
You'll be prompted to authenticate in Prisma Console, choose a project name, and pick a region for your Prisma Postgres database.
This will create several files inside packages/database:
prisma directory with a schema.prisma file.prisma.config.ts file for configuring Prisma..env file containing the DATABASE_URL in the packages/database directory.In the packages/database/prisma/schema.prisma file, add the following models:
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
}
model User { // [!code ++]
id Int @id @default(autoincrement()) // [!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 ++]
} // [!code ++]
The prisma.config.ts file created in the packages/database directory should look like this:
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"),
},
});
:::warning
It is recommended to add packages/database/generated to your root .gitignore because generated Prisma Client code is a build artifact that can be recreated with db:generate.
:::
In the schema.prisma file, we specify a custom output path where Prisma will generate its types. This ensures Prisma's types are resolved correctly across different package managers.
:::info
In this guide, the types will be generated in the database/generated/prisma directory.
:::
Let's add some scripts to the package.json inside packages/database:
{
"name": "@repo/db",
"version": "0.0.0",
"type": "module", // [!code ++]
"scripts": {
// [!code ++]
"db:generate": "prisma generate", // [!code ++]
"db:migrate": "prisma migrate dev", // [!code ++]
"db:deploy": "prisma migrate deploy" // [!code ++]
}, // [!code ++]
"devDependencies": {
"prisma": "^7.0.0"
},
"dependencies": {
"@prisma/client": "^7.0.0",
"@prisma/adapter-pg": "^7.0.0",
"pg": "^8.0.0",
"dotenv": "^16.0.0"
}
}
Let's also add these scripts to turbo.json in the root and ensure that DATABASE_URL is added to the environment:
{
"$schema": "https://turborepo.dev/schema.json",
"ui": "tui",
"globalEnv": ["DATABASE_URL"], // [!code ++]
"tasks": {
"build": {
"dependsOn": ["^build"],
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"check-types": {
"dependsOn": ["^check-types"]
},
"dev": {
"cache": false,
"persistent": true
},
"db:generate": { // [!code ++]
"cache": false // [!code ++]
}, // [!code ++]
"db:migrate": { // [!code ++]
"cache": false // [!code ++]
}, // [!code ++]
"db:deploy": { // [!code ++]
"cache": false // [!code ++]
} // [!code ++]
}
}
Run your first migration and generate Prisma Client
Navigate to the project root and run the following command to create and apply your first migration:
npx turbo run db:migrate -- --name init
In Prisma 7, migrate dev no longer runs prisma generate automatically, so run generate explicitly:
npx turbo run db:generate
Use the same npx turbo run db:generate command after future schema changes.
Next, export the generated types and an instance of PrismaClient so it can be used in your applications.
In the packages/database directory, create a src folder and add a client.ts file. This file will define an instance of PrismaClient:
import { PrismaClient } from "../generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
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;
Then create an index.ts file in the src folder to re-export the generated prisma types and the PrismaClient instance:
export { prisma } from "./client"; // exports instance of prisma
export * from "../generated/prisma/client"; // exports generated types from prisma
Follow the Just-in-Time packaging pattern and create an entrypoint to the package inside packages/database/package.json:
:::warning
If you're not using a bundler, use the Compiled Packages strategy instead.
:::
{
"name": "@repo/db",
"version": "0.0.0",
"type": "module",
"scripts": {
"db:generate": "prisma generate",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy"
},
"devDependencies": {
"prisma": "^7.0.0"
},
"dependencies": {
"@prisma/client": "^7.0.0",
"@prisma/adapter-pg": "^7.0.0",
"pg": "^8.0.0",
"dotenv": "^16.0.0"
},
"exports": {
// [!code ++]
".": "./src/index.ts" // [!code ++]
} // [!code ++]
}
By completing these steps, you'll make the Prisma types and PrismaClient instance accessible throughout the monorepo.
database package in the web appThe turborepo-prisma project should have an app called web at apps/web. Add the database dependency to apps/web/package.json:
{
// ...
"dependencies": {
"@repo/db": "*" // [!code ++]
// ...
}
// ...
}
{
// ...
"dependencies": {
"@repo/db": "workspace:*" // [!code ++]
// ...
}
// ...
}
{
// ...
"dependencies": {
"@repo/db": "workspace:*" // [!code ++]
// ...
}
// ...
}
Run your package manager's install command from the project root to link the workspace dependency:
npm install
Let's import the instantiated prisma client from the database package in the web app.
In the apps/web/app directory, open the page.tsx file and add the following code:
import styles from "./page.module.css";
import { prisma } from "@repo/db";
export default async function Home() {
const user = await prisma.user.findFirst();
return <div className={styles.page}>{user?.name ?? "No user added yet"}</div>;
}
Then, create a .env file in the web directory and copy into it the contents of the .env file from the /database directory containing the DATABASE_URL:
DATABASE_URL="Same database URL as used in the database directory"
:::note
If you want to use a single .env file in the root directory across your apps and packages in a Turborepo setup, consider using a package like dotenvx.
To implement this, update the package.json files for each package or app to ensure they load the required environment variables from the shared .env file. For detailed instructions, refer to the dotenvx guide for Turborepo.
Keep in mind that Turborepo recommends using separate .env files for each package to promote modularity and avoid potential conflicts.
:::
The db:generate script is essential for dev and build tasks in a monorepo setup.
If a new developer runs turbo dev on an application without first running db:generate, they will encounter errors.
To prevent this, ensure that db:generate is always executed before running dev or build. Keep db:deploy uncached for staging/production migration runs in CI. Here's how to configure this in your turbo.json file:
{
"$schema": "https://turborepo.dev/schema.json",
"ui": "tui",
"globalEnv": ["DATABASE_URL"],
"tasks": {
"build": {
"dependsOn": ["^build", "^db:generate"], // [!code highlight]
"inputs": ["$TURBO_DEFAULT$", ".env*"],
"outputs": [".next/**", "!.next/cache/**"]
},
"lint": {
"dependsOn": ["^lint"]
},
"check-types": {
"dependsOn": ["^check-types"]
},
"dev": {
"dependsOn": ["^db:generate"], // [!code ++]
"cache": false,
"persistent": true
},
"db:generate": {
"cache": false
},
"db:migrate": {
"cache": false
},
"db:deploy": {
"cache": false
}
}
}
Then from the project root run the project:
npx turbo run dev --filter=web
Navigate to the http://localhost:3000 and you should see the message:
No user added yet
:::note
You can add users to your database by creating a seed script or manually by using Prisma Studio.
To use Prisma Studio to add manually data via a GUI, navigate inside the packages/database directory and run prisma studio using your package manager:
npx prisma studio
This command starts a server with a GUI at http://localhost:5555, allowing you to view and modify your data.
:::
Congratulations, you're done setting up Prisma for Turborepo!