BuildWithMatija
Get In Touch
  1. Home
  2. Blog
  3. Next.js
  4. Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide

Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide

Step-by-step migration from Prisma v6 to v7 in Next.js 16, resolving Turbopack module errors and configuring…

1st December 2025·Updated on:7th March 2026·MŽMatija Žiberna·
Next.js
Prisma v7 Migration on Next.js 16 — Turbopack Fix Guide

⚡ Next.js Implementation Guides

In-depth Next.js guides covering App Router, RSC, ISR, and deployment. Get code examples, optimization checklists, and prompts to accelerate development.

No spam. Unsubscribe anytime.

Related Posts:

  • •How to Fix "Couldn't find next-intl config file" Error in Next.js 15
  • •Next.js searchParams Disables Static Generation — Here's the Architectural Fix
  • •Payload CMS + Next.js 16.2: Compatibility Status & Upgrade Guide

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.

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 v6Prisma v7
Generator providerprisma-client-jsprisma-client (new)
Connection configurl in schema.prismadatasource.url in prisma.config.ts
Client instantiationnew PrismaClient()new PrismaClient({ adapter })
Env loadingAuto-loaded from .envRequires import 'dotenv/config'
Query cachingNot availableBuilt-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.

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.

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:

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

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.

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:

// 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:

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

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

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:

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:

// 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:

// 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:

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:

npx prisma migrate deploy

For new migrations during development, use:

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:

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:

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:

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:

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 for a similar debugging approach. If you're also migrating your CMS layer, the Payload CMS & Next.js 16 compatibility guide 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

📄View markdown version
4

Frequently Asked Questions

Comments

Leave a Comment

Your email will not be published

Stay updated! Get our weekly digest with the latest learnings on NextJS, React, AI, and web development tips delivered straight to your inbox.

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

No comments yet

Be the first to share your thoughts on this post!

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.

You might be interested in

How to Fix "Couldn't find next-intl config file" Error in Next.js 15
How to Fix "Couldn't find next-intl config file" Error in Next.js 15

23rd September 2025

Next.js searchParams Disables Static Generation — Here's the Architectural Fix
Next.js searchParams Disables Static Generation — Here's the Architectural Fix

5th October 2025

Payload CMS + Next.js 16.2: Compatibility Status & Upgrade Guide
Payload CMS + Next.js 16.2: Compatibility Status & Upgrade Guide

2nd December 2025

Table of Contents

  • Understanding the Prisma v7 Changes
  • Upgrading the Prisma Packages
  • Configuring the Prisma Schema
  • Creating the Prisma Configuration File
  • Updating the Prisma Client Instantiation
  • The Triple-Layered Turbopack Fix
  • Generating and Testing the Client
  • Why This Configuration Works
  • Troubleshooting Common Errors
  • `error: The datasource.url property is required in your Prisma config file when using prisma migrate deploy`
  • `cannot find module 'prisma/config'`
  • `` `PrismaClient` needs to be constructed with a non-empty, valid `PrismaClientOptions` ``
  • `error: the datasource property url is no longer supported in schema files`
  • Verifying Your Migration
  • Summary
On this page:
  • Understanding the Prisma v7 Changes
  • Upgrading the Prisma Packages
  • Configuring the Prisma Schema
  • Creating the Prisma Configuration File
  • Updating the Prisma Client Instantiation
Build With Matija Logo

Build with Matija

Modern websites, content systems, and AI workflows built for long-term growth.

Services

  • Headless CMS Websites
  • Next.js & Headless CMS Advisory
  • AI Systems & Automation
  • Website & Content Audit
  • Resources

    • Case Studies
    • How I Work
    • Blog
    • CMS Hub
    • E-commerce Hub
    • Dashboard

    Headless CMS

    • Payload CMS Developer
    • CMS Migration
    • Payload vs Sanity
    • Payload vs WordPress
    • Payload vs Contentful

    Get in Touch

    Ready to modernize your stack? Let's talk about what you're building.

    Book a discovery callContact me →
    © 2026BuildWithMatija•All rights reserved