Topic 9 of 47
Database Integration with Prisma
Overview
Prisma is the most popular ORM for Next.js — it generates a type-safe client from your schema, handles migrations, and integrates perfectly with Next.js Server Components and API routes. It replaces raw SQL for most CRUD operations.
Syntax
typescript
// prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model User {
id String @id @default(cuid())
email String @unique
name String?
createdAt DateTime @default(now())
orders Order[]
}
model Product {
id String @id @default(cuid())
name String
price Decimal @db.Decimal(10, 2)
categoryId String
category Category @relation(fields: [categoryId], references: [id])
}
// lib/prisma.ts (singleton for dev)
import { PrismaClient } from '@prisma/client';
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;Common Pitfalls
- Always use a singleton Prisma client in development — Next.js hot reload creates a new client on every reload, exhausting connection pools.
- Prisma transactions (prisma.$transaction) ensure atomicity — if any operation fails, all are rolled back.
- Interview tip: Prisma generates TypeScript types from your schema — you get full type safety without writing interfaces manually.
Real-World Example
Using Prisma in a Next.js Server Action for e-commerce:
example
typescript
// app/actions/orders.ts
'use server';
import { prisma } from '@/lib/prisma';
import { auth } from '@/lib/auth';
import { revalidatePath } from 'next/cache';
export async function createOrder(formData: FormData) {
const session = await auth();
if (!session) throw new Error('Unauthorized');
const productId = formData.get('productId') as string;
const quantity = parseInt(formData.get('quantity') as string);
// Transaction: check stock and create order atomically
const result = await prisma.$transaction(async (tx) => {
const product = await tx.product.findUnique({
where: { id: productId },
select: { id: true, price: true, stock: true, name: true },
});
if (!product || product.stock < quantity) {
throw new Error('Insufficient stock');
}
// Decrement stock
await tx.product.update({
where: { id: productId },
data: { stock: { decrement: quantity } },
});
// Create order with line item
return tx.order.create({
data: {
userId: session.user.id,
totalAmount: product.price.mul(quantity),
items: {
create: {
productId: product.id,
quantity,
unitPrice: product.price,
},
},
},
include: { items: true },
});
});
revalidatePath('/orders');
return result;
}