sma 7 Dependencies
Prisma 7 requires the core CLI, the client library, and a specific driver adapter for PostgreSQL.
npm install prisma @types/pg --save-dev
npm install @prisma/client @prisma/adapter-pg pg dotenv
prisma: CLI for schema management and migrations.
@prisma/client: Type-safe query client.
@prisma/adapter-pg: Adapter bridging Prisma to the pg driver.
pg: The PostgreSQL driver for Node.js.
dotenv: Loads environment variables.
3. Configuration with prisma.config.ts
Run the initialization command to generate the schema and configuration files:
npx prisma init
This creates prisma/schema.prisma, .env, and the new prisma.config.ts. The configuration file defines project-level settings:
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'),
},
});
Rationale: Separating configuration from the schema allows for cleaner schema files and enables TypeScript-based configuration logic. The defineConfig helper provides autocomplete and validation for configuration options.
4. Defining the Data Model
Define your database structure in prisma/schema.prisma. This example models an inventory system with products and categories.
generator client {
provider = "prisma-client"
}
datasource db {
provider = "postgresql"
}
model Category {
id String @id @default(uuid())
name String @unique
description String?
products Product[]
createdAt DateTime @default(now())
}
model Product {
id String @id @default(uuid())
sku String @unique
name String
price Decimal
stock Int @default(0)
categoryId String
category Category @relation(fields: [categoryId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Key Design Decisions:
- UUID Primary Keys: Using
@default(uuid()) avoids sequential ID enumeration and is safer for public-facing APIs.
- Relations: The
@relation attribute explicitly maps foreign keys, ensuring referential integrity at the schema level.
- Timestamps:
@default(now()) and @updatedAt automate audit trails without application logic.
5. Database Provisioning and Migration
For rapid development, Prisma provides a provisioning utility that creates a temporary hosted PostgreSQL database:
npx create-db
This command provisions a database and outputs a connection string. Copy this string to your .env file:
DATABASE_URL="postgresql://user:password@host:5432/dbname"
Apply the schema to the database using migrations:
npx prisma migrate dev --name init-inventory
This command generates SQL based on the schema, applies it to the database, and creates a migration file in prisma/migrations/. Migrations serve as version control for your database structure, ensuring consistency across environments.
6. Type-Safe Client Implementation
In Prisma 7, instantiate the client with an adapter. This example demonstrates a singleton pattern to prevent connection exhaustion during hot reloading.
import { PrismaClient } from '@prisma/client';
import { Pool } from 'pg';
import { PrismaPg } from '@prisma/adapter-pg';
// Singleton pattern for development stability
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const db = globalForPrisma.prisma ?? new PrismaClient({
adapter: new PrismaPg(new Pool({
connectionString: process.env.DATABASE_URL,
})),
});
if (process.env.NODE_ENV !== 'production') {
globalForPrisma.prisma = db;
}
Usage Example:
import { db } from './db/client.js';
async function createProduct() {
const category = await db.category.create({
data: {
name: 'Electronics',
description: 'Consumer electronics and gadgets',
},
});
const product = await db.product.create({
data: {
sku: 'ELEC-001',
name: 'Wireless Headphones',
price: 99.99,
stock: 50,
categoryId: category.id,
},
include: {
category: true,
},
});
console.log('Created product:', product);
}
createProduct()
.catch(console.error)
.finally(() => db.$disconnect());
Rationale:
- Adapter Injection: The
PrismaPg adapter wraps the pg pool, allowing Prisma to manage queries while leveraging the driver's connection handling.
- Singleton Pattern: In development, module reloading can create multiple client instances, exhausting database connections. The singleton pattern mitigates this.
- Include Relations: The
include option fetches related data in a single query, reducing N+1 query problems.
Pitfall Guide
1. Missing Driver Adapter
Explanation: Prisma 7 will throw an error if you attempt to instantiate PrismaClient without an adapter. The direct connection method used in earlier versions is deprecated.
Fix: Always import the appropriate adapter package (e.g., @prisma/adapter-pg) and pass it to the adapter option during client instantiation.
2. ESM/CommonJS Mismatch
Explanation: Prisma 7 defaults to ESM. If your project uses CommonJS (require), you may encounter module resolution errors or syntax issues.
Fix: Ensure package.json has "type": "module" and tsconfig.json uses "module": "ESNext". Use import/export syntax exclusively.
3. Manual Schema Mutations
Explanation: Altering the database directly via SQL clients or GUI tools bypasses Prisma's migration system. This causes schema drift, where prisma migrate dev detects untracked changes and may fail or generate conflicting migrations.
Fix: Always modify the database through schema.prisma and run prisma migrate dev. Use prisma db push only for prototyping, not production.
4. Connection Pool Exhaustion
Explanation: Creating a new PrismaClient instance for every request or in hot-reload scenarios can exhaust the database connection pool, leading to timeout errors.
Fix: Implement a singleton pattern for the client instance. In production, rely on the adapter's connection pooling. In development, use the global singleton pattern shown in the Core Solution.
5. Ignoring Environment Variable Validation
Explanation: If DATABASE_URL is missing or malformed, the application may fail at runtime with cryptic errors.
Fix: Validate environment variables at startup. Use a library like zod to enforce schema validation on environment variables before initializing the database client.
6. Schema Output Path Conflicts
Explanation: The generated client code is output to node_modules/.prisma/client by default. Custom output paths can cause import resolution issues or conflicts with build tools.
Fix: Stick to the default output path unless you have a specific monorepo requirement. If using a custom path, ensure all imports reference the correct location and build tools are configured to handle it.
7. Overusing select vs include
Explanation: Developers often use select to limit fields but forget that relations are not included by default. This can lead to missing data or additional queries.
Fix: Use include when you need related records. Use select only when you need to restrict fields on the primary model or specific relations. Combine them carefully to avoid query complexity.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Local Development | npx create-db or Local PostgreSQL | Rapid provisioning; no external dependencies | Free |
| Production Deployment | Managed PostgreSQL (e.g., Neon, Supabase) | High availability, backups, scaling | $$ |
| Edge/Serverless Functions | Prisma Adapters with Connection Pooling | Low latency; efficient resource usage | $$ |
| Monorepo Architecture | Shared prisma.config.ts and Schema | Consistency across packages; single source of truth | Low |
| CI/CD Pipeline | prisma migrate deploy | Automated, non-interactive schema updates | Low |
Configuration Template
prisma.config.ts
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'),
},
// Optional: Custom generator settings
// generator: {
// name: 'client',
// provider: 'prisma-client',
// output: '../generated/client',
// },
});
tsconfig.json
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ES2023",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Quick Start Guide
-
Initialize Project:
mkdir prisma-app && cd prisma-app
npm init -y
npm install prisma @prisma/client @prisma/adapter-pg pg dotenv typescript tsx --save-dev
npx tsc --init
-
Configure ESM:
Add "type": "module" to package.json. Update tsconfig.json with ESM settings.
-
Setup Prisma:
npx prisma init
Edit prisma/schema.prisma to define your models.
-
Provision Database:
Run npx create-db to get a temporary database URL. Paste the URL into .env.
-
Migrate and Run:
npx prisma migrate dev --name init
npx tsx src/index.ts
This workflow establishes a type-safe, production-ready database layer using Prisma 7's modern architecture, leveraging driver adapters for performance and flexibility.