Turborepo

Prisma

Prisma unlocks a new level of developer experience when working with databases thanks to its intuitive data model, automated migrations, type-safety & auto-completion.

Good to know:

This guide assumes you're using create-turbo or a repository with a similar structure.

This guide shows you how to:

  1. Set up Prisma in a monorepo
  2. Handle migration and code generation scripts
  3. Ensure that they're always run whenever dev or build is run

If you've already got Prisma set up in your database, you can skip to step 4.

Create your monorepo

If you don't have an existing project, use our quickstart to create a new monorepo.

Add a new database package

Create a new folder called database inside packages with a package.json inside:

packages/database/package.json
{
  "name": "@repo/db",
  "version": "0.0.0",
  "dependencies": {
    "@prisma/client": "latest" // Replace with latest version
  },
  "devDependencies": {
    "prisma": "latest" // Replace with latest version
  }
}

Run your package manager's install step to install the new dependencies.

Run prisma init

cd into packages/database:

Terminal
cd packages/database

Run npx prisma init.

This should create several files inside packages/database:

  • schema.prisma is where your Prisma schema lives. Here, you'll be able to modify the shape of your database.
  • .gitignore adds some ignored files to git
  • .env lets you manually specify your DATABASE_URL for prisma.

At this point, you should refer to the Prisma docs for connecting your database to Prisma.

Once you've got a database connected and have a few data tables to work with, you can move on.

Create scripts

Let's add some scripts to the package.json inside packages/database:

packages/database/package.json
{
  "scripts": {
    "db:generate": "prisma generate",
    "db:push": "prisma db push --skip-generate"
  }
}

Let's also add these scripts to turbo.json in the root:

./turbo.json
{
  "tasks": {
    "db:generate": {
      "cache": false
    },
    "db:push": {
      "cache": false
    }
  }
}

Now, run turbo db:push db:generate from the root of our repository to automatically migrate our database and generate our type-safe Prisma client.

Use the --skip-generate flag on db:push to ensure it doesn't automatically run prisma generate after migrating the database. This ends up being faster when using Turborepo because it automatically parallelizes the tasks.

Exporting your client

Next, export the @prisma/client so it can used in your applications. Let's add an index.ts file to packages/database:

packages/database/src/index.ts
export * from '@prisma/client';

Following the Just-in-Time packaging pattern, you'll also create an entrypoint to the package inside packages/database/package.json.

packages/database/package.json
{
  "exports": {

    ".": "./src/index.ts"
  } 
}

Note that you're using the Just-in-Time Package pattern here, which assumes your application can consume TypeScript directly. You may need to adjust to a different strategy as if needed.

Importing database

Import the database package into one of our apps.

Let's say you have an app at apps/web. Add the dependency to apps/web/package.json:

apps/web/package.json
{
  "dependencies": {
    "@repo/db": "*"
  }
}

Run your package manager's install command.

You can now import PrismaClient from database anywhere in your app:

./apps/web/app/page.tsx
import { PrismaClient } from '@repo/db';
 
const client = new PrismaClient();

Figuring out the scripts

You now have a reusable @repo/db package that you can import into any of your applications and a turbo db:push script to push schema changes

However, your db:generate scripts aren't optimized yet. They provide crucial code to our dev and build tasks. If a new developer runs turbo dev on an application without running db:generate first, they'll get errors.

So, let's make sure that db:generate is always run before you run dev:

./turbo.json
{
  "tasks": {
    "dev": {
      "dependsOn": ["^db:generate"],
      "cache": false
    },
    "build": {
      "dependsOn": ["^db:generate"],
      "outputs": ["your-outputs-here"]
    },
    "db:generate": {
      "cache": false
    }
  }
}

Check out the section on running tasks to learn more about the ^db:generate syntax.

Caching the results of prisma generate

prisma generate outputs files to the filesystem, usually inside node_modules. In theory, it should be possible to cache the output of prisma generate with Turborepo to save a few seconds.

However, Prisma behaves differently with different package managers. This can lead to unpredictable results, which might lead to broken deployments in some situations. Instead of documenting the intricacies of each approach, we recommend not caching the results of prisma generate. Since prisma generate usually only takes 5-6 seconds, and tends not to take longer with larger schema files, this seems like a fine trade-off.

You may also wish to experiment with caching the generated files in a way that satisfies the constraints for your repository.

Going to production

Now that you've made it this far, you're ready to deploy your application. Depending on where your database lives, you'll want to design your deployment pipeline according to the documentation for your database's setup.

There are many factors to take into consideration from this point on, so we can't provide a one-size-fits-all solution. You likely want to visit the documentation for your database and its deployment platform to learn more.

hours

Total Compute Saved
Get started with
Remote Caching →

On this page