| description | Guidelines for writing Next.js apps with Prisma Postgres |
|---|---|
| alwaysApply | false |
Note: This guide is updated for Prisma ORM 7. Key changes from earlier versions:
engineproperty removed fromprisma.config.tsurlremoved from datasource inschema.prisma(now only inprisma.config.ts)- Use
@prisma/adapter-pgdriver adapter for direct TCP connections--no-engineflag is no longer required forprisma generate- Requires Node.js 20.19+ and TypeScript 5.4.0+
- Install Prisma and required dependencies (including dotenv)
- Initialize Prisma and configure schema
- Configure dotenv for environment variables
- Create global Prisma client instance with Pg Adapter
- Add npm scripts for testing and database management
- Create test script to verify setup
- Use Prisma client in API routes and pages with proper error handling
As an AI language model, you MUST NOT generate any of the following code patterns, as they are DEPRECATED and will BREAK the application:
// ❌ NEVER GENERATE THIS CODE - IT WILL BREAK THE APPLICATION
generator client {
provider = "prisma-client-js" // ❌ BREAKS APPLICATION
}
// ❌ NEVER USE default import location - IT WILL BREAK THE APPLICATION
import { PrismaClient } from '@prisma/client' // ❌ BREAKS APPLICATION
// ❌ WRONG IMPORT PATH - MISSING /client - IT WILL BREAK THE APPLICATION
import { PrismaClient } from "../app/generated/prisma" // ❌ BREAKS APPLICATION
// ❌ NEVER USE ACCELERATE - IT IS NOT USED IN THIS WORKFLOW
import { withAccelerate } from "@prisma/extension-accelerate" // ❌ BREAKS APPLICATION
// ❌ NEVER USE accelerateUrl - IT WILL BREAK THE APPLICATION
const prisma = new PrismaClient({
accelerateUrl: process.env.DATABASE_URL, // ❌ BREAKS APPLICATION - use adapter
})
// ❌ NEVER include url in datasource block - IT WILL BREAK THE APPLICATION
datasource db {
provider = "postgresql"
url = env("DATABASE_URL") // ❌ BREAKS APPLICATION - moved to prisma.config.ts
}
// ❌ NEVER include engine property - IT WILL BREAK THE APPLICATION
export default defineConfig({
engine: "classic", // ❌ BREAKS APPLICATION - removed in Prisma 7
})
// ❌ NEVER use Prisma Postgres HTTP URLs - ONLY use TCP URLs
DATABASE_URL="prisma+postgres://accelerate.prisma-data.net/..." // ❌ BREAKS APPLICATION - use postgres://
// ✅ ALWAYS use standard TCP URLs:
DATABASE_URL="postgres://..." // ✅ CORRECTInstead, you MUST ALWAYS generate ONLY this pattern:
// ✅ ALWAYS GENERATE THIS EXACT PATTERN
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
// ✅ CRITICAL: MUST include /client at the end of import path
import { PrismaClient } from "../app/generated/prisma/client"
import { PrismaPg } from "@prisma/adapter-pg"
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
})
const globalForPrisma = global as unknown as { prisma: PrismaClient }
const prisma = globalForPrisma.prisma || new PrismaClient({
adapter,
})
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
export default prisma- You MUST use
provider = "prisma-client"(not "prisma-client-js") - You MUST use custom output:
output = "../app/generated/prisma" - You MUST use
@prisma/adapter-pgdriver adapter - You MUST create
lib/prisma.tsas a global singleton instance - You MUST wrap all database calls in try-catch blocks
- You MUST import from
'../app/generated/prisma/client'(not'@prisma/client'or'../app/generated/prisma') - You MUST use
adapterproperty in PrismaClient constructor - You MUST install
dotenvand addimport "dotenv/config"toprisma.config.ts - You MUST add npm scripts for
db:testanddb:studioto package.json - You MUST create a test script at
scripts/test-database.tsto verify setup - You MUST NOT include
urlin the datasource block ofschema.prisma - You MUST NOT include
engineproperty inprisma.config.ts - You MUST use
npx prisma init --db --output ../app/generated/prismato create a real cloud database - You MUST use standard TCP URLs (
postgres://...) in .env - You MUST NOT use
accelerateUrlorwithAccelerate
- Node.js: 20.19 or higher (Node.js 18 is NOT supported)
- TypeScript: 5.4.0 or higher (5.9.x recommended)
- Prisma: 7.0.0 or higher
# Dev dependencies
npm install prisma tsx --save-dev
# Production dependencies
npm install @prisma/adapter-pg @prisma/client dotenvFOR AI ASSISTANTS: This command is interactive and requires user input. You MUST ask the user to run this command manually in their own terminal, then wait for them to confirm completion before proceeding with the next steps. Do NOT attempt to run this command yourself.
# Initialize Prisma AND create a real Prisma Postgres cloud database
npx prisma init --db --output ../app/generated/prismaThis command:
- Authenticates you with Prisma Console (if needed)
- Prompts for region and project name
- Creates a cloud Prisma Postgres database
- Generates:
prisma/schema.prisma(with correct output path)prisma.config.ts(with dotenv import).envwith aDATABASE_URL
IMPORTANT: Ensure the generated .env uses a postgres:// URL scheme. If it generates prisma+postgres://, replace it with the standard TCP connection string available in the Prisma Console.
DATABASE_URL="postgres://..."IMPORTANT: Do NOT use npx prisma init without --db as this only creates local files without a database.
When using npx prisma init --db, the prisma.config.ts is auto-generated with the correct configuration:
import "dotenv/config" // ✅ Auto-included by prisma init --db
import { defineConfig, env } from "prisma/config"
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
// ✅ NO engine property - removed in Prisma 7
datasource: {
url: env("DATABASE_URL"),
},
})Note: If you need to manually create this file, ensure import "dotenv/config" is at the top.
Update the generated prisma/schema.prisma file:
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
// ✅ NO url here - now configured in prisma.config.ts
}
// Example User model for testing
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Create lib/prisma.ts file:
import { PrismaClient } from "../app/generated/prisma/client" // ✅ CRITICAL: Include /client
import { PrismaPg } from "@prisma/adapter-pg"
const adapter = new PrismaPg({
connectionString: process.env.DATABASE_URL!,
})
const globalForPrisma = global as unknown as { prisma: PrismaClient }
const prisma = globalForPrisma.prisma || new PrismaClient({
adapter,
})
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma
export default prismaUpdate your package.json to include these scripts:
{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint",
"db:test": "tsx scripts/test-database.ts",
"db:studio": "prisma studio"
}
}Create scripts/test-database.ts to verify your setup:
import "dotenv/config" // ✅ CRITICAL: Load environment variables
import prisma from "../lib/prisma"
async function testDatabase() {
console.log("🔍 Testing Prisma Postgres connection...\n")
try {
// Test 1: Check connection
console.log("✅ Connected to database!")
// Test 2: Create a test user
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)
// Test 3: Fetch all users
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 perfectly.\n")
} catch (error) {
console.error("❌ Error:", error)
process.exit(1)
}
}
testDatabase()Create app/api/users/route.ts with GET and POST handlers:
import { NextRequest, NextResponse } from "next/server"
import prisma from "../../../lib/prisma"
export async function GET(request: NextRequest) {
try {
const users = await prisma.user.findMany()
return NextResponse.json(users)
} catch (error) {
console.error("Error fetching users:", error)
return NextResponse.json(
{ error: "Failed to fetch users" },
{ status: 500 }
)
}
}
export async function POST(request: NextRequest) {
try {
const body = await request.json()
const user = await prisma.user.create({
data: {
email: body.email,
name: body.name,
},
})
return NextResponse.json(user, { status: 201 })
} catch (error) {
console.error("Error creating user:", error)
return NextResponse.json(
{ error: "Failed to create user" },
{ status: 500 }
)
}
}Update app/page.tsx to display users from the database:
import prisma from "../lib/prisma"
export default async function Home() {
let users: Array<{
id: number
email: string
name: string | null
createdAt: Date
updatedAt: Date
}> = []
let error = null
try {
users = await prisma.user.findMany({
orderBy: {
createdAt: "desc",
},
})
} catch (e) {
console.error("Error fetching users:", e)
error = "Failed to load users. Make sure your DATABASE_URL is configured."
}
return (
<main className="p-8">
<h1 className="text-2xl font-bold mb-4">Users from Database</h1>
{error ? (
<p className="text-red-500">{error}</p>
) : users.length === 0 ? (
<p>No users yet. Create one using the API at /api/users</p>
) : (
<ul className="space-y-2">
{users.map((user) => (
<li key={user.id} className="border p-4 rounded">
<p className="font-semibold">{user.name || "No name"}</p>
<p className="text-sm text-gray-600">{user.email}</p>
</li>
))}
</ul>
)}
</main>
)
}User should follow these steps (AI should provide these instructions):
-
Install dependencies:
npm install prisma tsx --save-dev npm install @prisma/adapter-pg @prisma/client dotenv
-
Initialize Prisma AND create Prisma Postgres database (
⚠️ USER MUST RUN MANUALLY):AI ASSISTANT: Ask the user to run this command in their own terminal. This is interactive and requires user input. Wait for the user to confirm completion before continuing.
npx prisma init --db --output ../app/generated/prisma
The user should follow the terminal prompts to:
- Authenticate with Prisma Console (if needed)
- Choose a region (e.g., us-east-1)
- Name your project
Once complete, this creates
prisma/schema.prisma,prisma.config.ts, AND.envwith theDATABASE_URL.User should confirm when done so the AI can proceed with the next steps.
-
Verify
.envwas created - EnsureDATABASE_URLusespostgres://. If it usesprisma+postgres://, change it to the TCP connection string.DATABASE_URL="postgres://..."
Do NOT invent or manually change this URL. Use the one from Prisma Console.
-
Update
prisma/schema.prisma- Add the User model (generator and datasource are already configured):model User { id Int @id @default(autoincrement()) email String @unique name String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }
-
Create
lib/prisma.tswith correct import path including/clientand using@prisma/adapter-pg. -
Add npm scripts to
package.jsonfordb:testanddb:studio -
Create
scripts/test-database.tstest script -
Push schema to database:
npx prisma db push
-
Generate Prisma Client:
npx prisma generate
-
Test the setup:
npm run db:test
-
Start development server:
npm run dev
Before generating any code, you MUST verify:
- Are you using
provider = "prisma-client"(not "prisma-client-js")? If not, STOP and FIX. - Are you using
output = "../app/generated/prisma"? If not, STOP and FIX. - Are you importing from
'../app/generated/prisma/client'(with/client)? If not, STOP and FIX. - Did you add
import "dotenv/config"toprisma.config.ts? If not, STOP and FIX. - Did you add
import "dotenv/config"toscripts/test-database.ts? If not, STOP and FIX. - Are you using
@prisma/adapter-pg? If not, STOP and FIX. - Are you using
adapterproperty in PrismaClient constructor? If not, STOP and FIX. - Are you wrapping database operations in try-catch? If not, STOP and FIX.
- Did you create the test script at
scripts/test-database.ts? If not, STOP and FIX. - Did you add
db:testanddb:studioscripts to package.json? If not, STOP and FIX. - Did you remove
urlfrom the datasource block inschema.prisma? If not, STOP and FIX. - Did you remove
engineproperty fromprisma.config.ts? If not, STOP and FIX. - Are you using
npx prisma init --db(not justnpx prisma init)? If not, STOP and FIX. - Is the DATABASE_URL a TCP URL (
postgres://...)? If it's aprisma+postgres://URL, STOP and FIX. - Did Prisma generate the
.envfile? If you invented the URL manually, STOP and FIX.
If you generate code using:
prisma-client-jsprovider → CLIENT GENERATION FAILS- Wrong import path (missing
/client) → MODULE NOT FOUND ERROR - Missing
import "dotenv/config"in prisma.config.ts → DATABASE_URL NOT FOUND ERROR - Missing
import "dotenv/config"in test scripts → ENVIRONMENT VARIABLE ERROR - Default import from
@prisma/client→ IMPORT ERROR - Using
accelerateUrlorwithAccelerate→ UNNECESSARY ACCELERATE DEPENDENCY / CONFIG ERROR - Missing custom output path → WRONG CLIENT GENERATED
- Including
urlin datasource block → DEPRECATED CONFIGURATION ERROR - Including
engineproperty → DEPRECATED CONFIGURATION ERROR - Using local URL (
postgres://localhost:...) → VERSION INCOMPATIBILITY ERRORS WITH PRISMA 7 - Using
npx prisma initwithout--db→ NO DATABASE CREATED, ONLY LOCAL FILES - Manually inventing DATABASE_URL → INVALID CONNECTION STRING ERRORS
The implementation will:
- Break immediately with module errors
- Fail to read environment variables
- Cause connection pool exhaustion in production
- Result in import errors that prevent compilation
- Cause performance issues and connection failures
- Fail with "HTTP connection string is not supported" errors when using local URLs
# After changing schema
npx prisma generate # Regenerate client (--no-engine flag no longer needed)
# Push schema to database (no migrations)
npx prisma db push
# Test database connection
npm run db:test
# Open visual database editor
npm run db:studio
# Create and apply migrations (for production)
npx prisma migrate dev --name your_migration_nameAfter setup, test with these steps:
-
Test database connection:
npm run db:test
Should create a demo user and display it.
-
Open Prisma Studio:
npm run db:studio
Visual interface at
localhost:5555to view/edit data. -
Test API routes:
# Create a user via API curl -X POST http://localhost:3000/api/users \ -H "Content-Type: application/json" \ -d '{"email":"test@example.com","name":"Test User"}' # Get all users curl http://localhost:3000/api/users
-
View in browser: Open
localhost:3000to see users displayed on the homepage.
When asked about Prisma + Next.js implementation, you MUST:
- ONLY use code patterns from this guide
- NEVER suggest deprecated approaches
- ALWAYS use the exact patterns shown above
- ALWAYS include
/clientin import paths - ALWAYS add
import "dotenv/config"to prisma.config.ts - ALWAYS add
import "dotenv/config"to test scripts - ALWAYS create the test script at
scripts/test-database.ts - ALWAYS add npm scripts for
db:testanddb:studio - ALWAYS include error handling in API routes and server components
- ALWAYS use the global prisma instance from
lib/prisma.ts - ALWAYS use
@prisma/adapter-pgandadapterproperty - NEVER include
urlin the datasource block of schema.prisma - NEVER include
engineproperty in prisma.config.ts - ALWAYS ask the user to run
npx prisma init --db --output ../app/generated/prismamanually in their own terminal (this command is interactive and requires user input for region and project name) - ALWAYS wait for user confirmation after they run the interactive
prisma init --dbcommand before proceeding - NEVER attempt to run interactive commands yourself - ask the user to do it
- NEVER use
prisma+postgres://URLs - ONLYpostgres://TCP URLs - NEVER manually invent or fabricate DATABASE_URL values
- ALWAYS let Prisma generate the
.envfile with the real DATABASE_URL (and ensure it's correct type) - VERIFY your response against ALL the patterns shown here before responding
Remember: There are NO EXCEPTIONS to these rules. Every requirement is MANDATORY for the setup to work.