---
title: "Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide"
slug: "migrate-prisma-v7-nextjs-16-turbopack-fix"
published: "2025-12-01"
updated: "2026-03-07"
validated: "2026-03-07"
categories:
  - "Next.js"
tags:
  - "Prisma v7 migration"
  - "Next.js 16"
  - "Turbopack compatibility"
  - "prisma-client-js"
  - "@prisma/adapter-pg"
  - "pg pooling"
  - "prisma.config.ts"
  - "migrate prisma v6 to v7"
  - "cannot find module .prisma/client/default"
  - "prisma migrate deploy error"
  - "serverExternalPackages"
  - "PrismaClient adapter"
  - "cannot find module prisma/config"
llm-intent: "how-to"
audience-level: "intermediate"
llm-purpose: "Prisma v7 migration: fix Next.js 16 Turbopack module errors, configure @prisma/adapter-pg with pg pooling and prisma.config.ts — follow this concise guide."
llm-prereqs:
  - "Next.js 16"
  - "Turbopack"
  - "Prisma v7"
  - "@prisma/adapter-pg"
  - "pg"
  - "pnpm"
  - "TypeScript"
  - "PostgreSQL"
  - "dotenv"
---

**Summary Triples**
- (Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide, expresses-intent, how-to)
- (Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide, covers-topic, Prisma v7 migration)
- (Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide, provides-guidance-for, Prisma v7 migration: fix Next.js 16 Turbopack module errors, configure @prisma/adapter-pg with pg pooling and prisma.config.ts — follow this concise guide.)

### {GOAL}
Prisma v7 migration: fix Next.js 16 Turbopack module errors, configure @prisma/adapter-pg with pg pooling and prisma.config.ts — follow this concise guide.

### {PREREQS}
- Next.js 16
- Turbopack
- Prisma v7
- @prisma/adapter-pg
- pg
- pnpm
- TypeScript
- PostgreSQL
- dotenv

### {STEPS}
1. Upgrade Prisma packages
2. Adjust prisma schema
3. Create prisma.config.ts
4. Implement PostgreSQL adapter
5. Preserve generator output
6. Generate and run migrations
7. Test app and verify

<!-- llm:goal="Prisma v7 migration: fix Next.js 16 Turbopack module errors, configure @prisma/adapter-pg with pg pooling and prisma.config.ts — follow this concise guide." -->
<!-- llm:prereq="Next.js 16" -->
<!-- llm:prereq="Turbopack" -->
<!-- llm:prereq="Prisma v7" -->
<!-- llm:prereq="@prisma/adapter-pg" -->
<!-- llm:prereq="pg" -->
<!-- llm:prereq="pnpm" -->
<!-- llm:prereq="TypeScript" -->
<!-- llm:prereq="PostgreSQL" -->
<!-- llm:prereq="dotenv" -->

# Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide
> Prisma v7 migration: fix Next.js 16 Turbopack module errors, configure @prisma/adapter-pg with pg pooling and prisma.config.ts — follow this concise guide.
Matija Žiberna · 2025-12-01

I recently upgraded a production Next.js application to version 16, and with it came the need to migrate from Prisma v6 to v7. What should have been a straightforward package upgrade turned into several hours of debugging cryptic module resolution errors. The official Prisma v7 migration guide doesn't address the specific compatibility issues with Next.js 16's Turbopack bundler — and the fix isn't just one change, it's three.

After working through the issues and getting everything running smoothly, I wanted to document the exact process. This guide walks you through migrating from Prisma v6 to Prisma v7 (currently v7.4.2) in a Next.js 16 application using Turbopack, with proper PostgreSQL adapter configuration. By the end, you'll have a working Prisma v7 setup that's fully compatible with Turbopack's module bundling system.

> [!NOTE]
> **TL;DR — 4 things to get Prisma v7 running on Next.js 16 + Turbopack:**
> 1. Keep `provider = "prisma-client-js"` in `schema.prisma` (not `prisma-client`)
> 2. Add `serverExternalPackages: ['@prisma/client', 'pg']` to `next.config.ts`
> 3. Move all connection URLs from `schema.prisma` to `prisma.config.ts`
> 4. Instantiate `PrismaClient` with an adapter — bare `new PrismaClient()` is invalid in v7

## Understanding the Prisma v7 Changes

Prisma v7 introduces several breaking changes that fundamentally alter how the client is instantiated and configured. Here's a quick comparison of what changed:

| | Prisma v6 | Prisma v7 |
|---|---|---|
| Generator provider | `prisma-client-js` | `prisma-client` (new) |
| Connection config | `url` in `schema.prisma` | `datasource.url` in `prisma.config.ts` |
| Client instantiation | `new PrismaClient()` | `new PrismaClient({ adapter })` |
| Env loading | Auto-loaded from `.env` | Requires `import 'dotenv/config'` |
| Query caching | Not available | Built-in (introduced in v7.4.0) |

The most significant change is the requirement for driver adapters. In previous versions, Prisma handled database connections internally through its query engine. Version 7 moves to an adapter-based architecture where you explicitly provide a database driver, making the main Prisma Client leaner and more flexible.


## Upgrading the Prisma Packages

The first step is upgrading both the Prisma CLI and client packages to version 7. The CLI is a dev dependency used for migrations and client generation, while the client package is the runtime dependency your application uses to query the database.

```bash
pnpm add @prisma/client@7.4.2
pnpm add -D prisma@7.4.2
```

Along with Prisma itself, you'll need to install the PostgreSQL adapter and driver packages, as well as dotenv for environment variable management in the new config system.

```bash
pnpm add @prisma/adapter-pg@^7.0.0 pg@^8.11.x dotenv
pnpm add -D @types/pg
```

These packages work together to provide the connection layer that Prisma v7 requires. The adapter translates between Prisma's query interface and the pg driver's native connection pooling.

## Configuring the Prisma Schema

The schema file structure changes in Prisma v7. The datasource block no longer contains the database URL directly, as this configuration moves to the new `prisma.config.ts` file. This separation allows for more flexible configuration patterns and better alignment with TypeScript-based configuration systems.

Update your schema file to remove the URL configuration:

```prisma
// File: prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
}

// Your models remain unchanged
model User {
  id    String @id @default(uuid())
  email String @unique
  name  String
  // ... rest of your schema
}

datasource db {
  provider = "postgresql"
}

// Your models remain unchanged
model User {
  id    String @id @default(uuid())
  email String @unique
  name  String
  // ... rest of your schema
}
```

The key change here is removing the `url` and `directUrl` properties from the datasource block. Notice we're using `prisma-client-js` as the provider rather than the new `prisma-client` name mentioned in the official docs. This is intentional and critical for Turbopack compatibility, which I'll explain in detail later.

> [!DANGER]
> Remove `url`, `directUrl`, **and** `shadowDatabaseUrl` from the datasource block — not just `url`. Leaving any of them will trigger: `error: the datasource property url is no longer supported in schema files`

## Creating the Prisma Configuration File

Prisma v7 introduces a new TypeScript configuration file that handles runtime settings including database URLs, migration paths, and other CLI behaviors. This file uses the `defineConfig` helper and supports type-safe environment variable access.

> [!WARNING]
> `import 'dotenv/config'` at the top of this file is **mandatory**. Prisma v7 no longer auto-loads `.env` files for the CLI. If you skip this, environment variables will be undefined when running migrations. (Exception: Bun auto-loads env vars, so Bun users can omit it.)

Create the configuration file at your project root:

```typescript
// File: prisma.config.ts

import 'dotenv/config';
import { defineConfig, env } from 'prisma/config';

export default defineConfig({
  schema: 'prisma/schema.prisma',
  datasource: {
    url: env('DATABASE_URL_UNPOOLED'),
  },
  migrations: {
    path: 'prisma/migrations',
  },
});
```

This configuration tells Prisma where to find your schema file and where to generate migrations. The `env()` helper provides type-safe access to environment variables. I'm using `DATABASE_URL_UNPOOLED` here because my project uses connection pooling with a service like Neon or Supabase, where the unpooled URL is needed for migrations. If you're running a standard PostgreSQL instance, you can use `env('DATABASE_URL')` instead.

If you're running `prisma generate` in a Docker build stage that doesn't have a live database available, you can bypass the strict `env()` helper and use `process.env.DATABASE_URL!` to avoid errors at build time when the variable may not be set:

```typescript
// For CI/Docker build stages without a live database
export default defineConfig({
  schema: 'prisma/schema.prisma',
  datasource: {
    url: process.env.DATABASE_URL!,
  },
  migrations: {
    path: 'prisma/migrations',
  },
});
```

## Updating the Prisma Client Instantiation

The way you create and export your Prisma Client changes significantly in version 7. Instead of directly instantiating PrismaClient, you now create a database adapter first, then pass it to the client constructor.

The way you create and export your Prisma Client changes significantly in version 7. Instead of directly instantiating PrismaClient, you now create a database adapter first, then pass it to the client constructor.

> [!DANGER]
> In Prisma v7, `new PrismaClient()` without arguments throws: **`PrismaClient needs to be constructed with a non-empty, valid PrismaClientOptions`**. The internal connection engine has been removed. You must pass `{ adapter }` — there is no fallback.
Update your Prisma client file:

```typescript
// File: lib/db/prisma.ts

import { PrismaClient } from '@prisma/client';
import { PrismaPg } from '@prisma/adapter-pg';
import pg from 'pg';

const { Pool } = pg;

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined;
  pool: pg.Pool | undefined;
};

// Create connection pool for the adapter
const pool = globalForPrisma.pool ?? new Pool({
  connectionString: process.env.DATABASE_URL,
});

if (process.env.NODE_ENV !== 'production') {
  globalForPrisma.pool = pool;
}

// Create Prisma adapter
const adapter = new PrismaPg(pool);

// Create Prisma Client with adapter
export const prisma =
  globalForPrisma.prisma ??
  new PrismaClient({
    adapter,
    log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
  });

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

export default prisma;
```

This code creates a PostgreSQL connection pool using the pg driver, wraps it in the Prisma adapter, and passes that adapter to the PrismaClient constructor. The global caching pattern prevents Next.js hot reload from creating multiple client instances during development. Notice we're caching both the pool and the prisma client to the global object, ensuring connection reuse across hot reloads.

The adapter acts as a bridge between Prisma's query interface and the native PostgreSQL driver. When Prisma needs to execute a query, it translates it into the format expected by the pg driver, which then handles the actual database communication through the connection pool.

## The Triple-Layered Turbopack Fix

Here's where things get tricky. The official Prisma v7 migration guide recommends changing your generator provider from `prisma-client-js` to `prisma-client`. However, this creates a module resolution error with Next.js 16's Turbopack bundler. The new `prisma-client` provider generates client code in an ESM-optimized structure outside of `node_modules`, and Turbopack's module hashing loses reference to the internal runtime during SSR — trapping you in a `Cannot find module '.prisma/client/default'` loop.

The fix requires three changes, not one. Using only the generator workaround without the `next.config.ts` changes will still fail in some SSR configurations.

> [!WARNING]
> Switching to `prisma-client-js` alone is often not enough. All three steps below are required for reliable Turbopack compatibility.

**Step 1 — Keep `prisma-client-js` as the generator provider**

Your schema's generator block should look exactly like this:

```prisma
generator client {
  provider = "prisma-client-js"
}
```

Do not add an `output` field, and do not use `prisma-client` as the provider.

**Step 2 — Externalize Prisma packages in `next.config.ts`**

Tell Next.js to treat these packages as server-external so Turbopack doesn't try to bundle them:

```typescript
// File: next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  serverExternalPackages: ['@prisma/client', 'pg'],
};

export default nextConfig;
```

**Step 3 — Add a Turbopack `resolveAlias`**

If you're still hitting module resolution errors after steps 1 and 2, add an explicit alias pointing Turbopack to the generated Prisma client:

```typescript
// File: next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
  serverExternalPackages: ['@prisma/client', 'pg'],
  experimental: {
    turbo: {
      resolveAlias: {
        '.prisma/client/default': './node_modules/.prisma/client/default.js',
      },
    },
  },
};

export default nextConfig;
```

This is likely a temporary workaround that will be resolved as Turbopack matures and Prisma updates its generator to better support different bundler configurations.

## Generating and Testing the Client

With your schema and configuration files updated, generate the Prisma client:

```bash
npx prisma generate
```

You should see output indicating the client was generated successfully. The output will show the path where the client was created, typically in your node_modules under the `.pnpm/@prisma+client@7.x.x/node_modules/@prisma/client` directory when using pnpm.

If you have existing migrations, apply them with:

```bash
npx prisma migrate deploy
```

For new migrations during development, use:

```bash
npx prisma migrate dev --name your_migration_name
```

The migration commands now read their configuration from `prisma.config.ts`, using the database URL you specified there. This means you can have different URLs for migrations versus runtime queries, which is useful when working with connection pooling services.

Start your development server and test that database queries work correctly:

```bash
pnpm dev
```

Navigate to any route that performs database operations. If you see the "Cannot find module" error at this point, double-check that your generator uses `prisma-client-js` and doesn't have an output path specified. That combination is what makes it work with Turbopack.

## Why This Configuration Works

The reason `prisma-client-js` works while `prisma-client` doesn't comes down to module resolution paths and how different bundlers handle them. The newer `prisma-client` provider generates its output in an ESM-optimized structure outside of `node_modules` — which is great for modern bundlers but currently causes Turbopack to lose track of the generated client during the SSR phase. The older `prisma-client-js` provider generates the client in the traditional `node_modules/.prisma/client` location that Turbopack's resolution algorithm handles reliably.

The `serverExternalPackages` config tells Next.js not to bundle `@prisma/client` and `pg` at all — they're loaded at runtime from `node_modules` instead. This sidesteps the resolution conflict entirely. The `resolveAlias` is a further safety net that gives Turbopack an explicit path when its heuristic resolution fails.

Prisma v7's client is fully functional using the old provider name. The internal implementation is identical — it's purely a difference in output structure. By combining all three layers, you get all the performance improvements of v7 (including the query caching layer introduced in v7.4.0) while maintaining full compatibility with Next.js 16's bundler.

## Troubleshooting Common Errors

Here are the most common errors developers hit during this migration and their exact fixes.

### `error: The datasource.url property is required in your Prisma config file when using prisma migrate deploy`

This occurs during production deployments or Docker builds when the CLI can't resolve the connection string. Either `prisma.config.ts` wasn't copied into the build image, or the `env()` helper failed because the environment variable is undefined at that build stage.

**Fix:** In Docker environments, explicitly copy the config file in your Dockerfile:

```dockerfile
COPY prisma.config.ts ./prisma.config.ts
```

If the error occurs during a `prisma generate` step that doesn't actually need a database connection, replace `env('DATABASE_URL')` with `process.env.DATABASE_URL!` in `prisma.config.ts` to bypass the strict env validation.

### `cannot find module 'prisma/config'`

This almost always appears in monorepos or CI pipelines where Prisma is run via a global `npx prisma` install. The `@prisma/config` sub-module doesn't hoist correctly in that case.

**Fix:** Ensure `prisma` is installed as a local `devDependency` in the specific project workspace, not relied on globally:

```bash
pnpm add -D prisma@7.4.2
```

Then run `npx prisma` from the project directory with the local install in scope.

### `` `PrismaClient` needs to be constructed with a non-empty, valid `PrismaClientOptions` ``

This happens when you call `new PrismaClient()` without passing a driver adapter. In v7, the internal connection engine was removed — there is no fallback.

**Fix:** Always instantiate with an adapter:

```typescript
import { Pool } from 'pg';
import { PrismaPg } from '@prisma/adapter-pg';
import { PrismaClient } from '@prisma/client';

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
const adapter = new PrismaPg(pool);
const prisma = new PrismaClient({ adapter });
```

### `error: the datasource property url is no longer supported in schema files`

This is a hard breaking change in v7. The CLI rejects any schema that defines connection variables locally.

**Fix:** Remove `url`, `directUrl`, **and** `shadowDatabaseUrl` from your `schema.prisma` datasource block entirely — not just `url`. Then define them all inside the `datasource` object of `prisma.config.ts`.

## Verifying Your Migration

After completing the migration, verify that everything works correctly by testing your application's database operations. Check that queries execute successfully, that connections are properly pooled, and that there are no runtime errors in your server logs. Pay particular attention to server actions and API routes, as these are where Prisma client usage is most common.

You should also verify that your development workflow still functions as expected. Hot reload should work without creating connection pool exhaustion, migrations should apply cleanly, and the Prisma Studio should connect properly if you use it.

## Summary

Migrating to Prisma v7 on Next.js 16 with Turbopack requires more than just a package upgrade. The key changes are: keeping `prisma-client-js` as the generator provider, externalizing `@prisma/client` and `pg` via `serverExternalPackages`, moving all connection URLs to `prisma.config.ts`, and instantiating `PrismaClient` with the `@prisma/adapter-pg` adapter. Together these four changes give you stable Turbopack compatibility while unlocking all of v7's improvements — faster queries, smaller bundles, and the query caching layer introduced in v7.4.0.

If you're running into other Next.js 16 compatibility issues, the same Turbopack-related module resolution patterns come up with other packages too — see the [fix for the "Couldn't find next-intl config file" error](/blog/fix-nextintl-turbopack-error) for a similar debugging approach. If you're also migrating your CMS layer, the [Payload CMS & Next.js 16 compatibility guide](/blog/payload-cms-nextjs-16-compatibility-breakthrough) covers the parallel upgrade path.

If you run into issues during your migration or have questions about specific configuration scenarios, let me know in the comments.

Thanks, Matija