Prisma
Schema language, migrate, generate, queries. Prisma Studio.
Prisma is the most-installed Node ORM in 2026. Its pitch: write your database schema in a clean DSL, run one command, get a fully-typed client that knows every table, every column, and every relation. Most teams pick it because the migration tooling is genuinely the best in the ecosystem and the autocomplete is hard to beat. Let's build with it.
Setup
npm install prisma @prisma/client
npx prisma init --datasource-provider postgresqlThis creates a prisma/ folder with schema.prisma and a .env with DATABASE_URL.
The schema.prisma language
schema.prismais Prisma's own DSL. Think of it as TypeScript's younger cousin for describing databases. Three things live in it: the generator (which language to emit a client for), the datasource (which DB to talk to), and your models (tables).
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String
createdAt DateTime @default(now())
orders Order[]
}
model Order {
id String @id @default(uuid())
userId String
amountCents Int @db.Integer
status String @default("pending")
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, createdAt(sort: Desc)])
}Field-level attributes start with @ (e.g. @id, @default, @unique). Model-level attributes start with @@ (e.g. @@index, @@map).
orders Order[] on User and user User @relation(...) on Order. Prisma infers the foreign-key constraint from the @relation side. The plural array side is purely a query convenience and adds no DB columns.Migrations: dev vs deploy
Prisma generates SQL migration files from changes to your schema. Two commands you need to know:
npx prisma migrate dev --name init- in development: generates a migration, applies it, regenerates the client. Interactive and may reset the DB if you have drift.npx prisma migrate deploy- in production/CI: applies pending migrations only. Non-interactive, safe for automation.
# 1. Edit schema.prisma
# 2. Generate + apply migration locally
npx prisma migrate dev --name add_orders_table
# This creates prisma/migrations/20260524103011_add_orders_table/migration.sql
# and applies it to your dev DB.
# In CI / production:
npx prisma migrate deployGenerating the client (Prisma 7 is explicit)
Before Prisma 7, the client was regenerated automatically on npm install. As of Prisma 7 (mid-2025), you run it explicitly so your CI is deterministic:
npx prisma generate
# Generates a fully-typed client into node_modules/.prisma/clientAdd this to your postinstall script if you want the old behavior:
{
"scripts": {
"postinstall": "prisma generate"
}
}Using the client
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
// Create
const user = await prisma.user.create({
data: { email: "ada@example.com", name: "Ada Lovelace" },
});
// Read - find by unique field
const same = await prisma.user.findUnique({
where: { email: "ada@example.com" },
});
// Read - filter and include relations
const usersWithOrders = await prisma.user.findMany({
where: { name: { contains: "Ada", mode: "insensitive" } },
include: { orders: { orderBy: { createdAt: "desc" }, take: 5 } },
orderBy: { createdAt: "desc" },
take: 20,
});
// Update
const updated = await prisma.user.update({
where: { id: user.id },
data: { name: "Ada King" },
});
// Delete
await prisma.user.delete({ where: { id: user.id } });
// Aggregate
const totals = await prisma.order.groupBy({
by: ["userId"],
_sum: { amountCents: true },
_count: true,
});The singleton pattern for hot reload
In dev mode with Next.js (or any hot-reloading framework), modules reload constantly. If you do new PrismaClient() at the top of a file, each reload opens new database connections until your DB hits its connection limit. The fix: stash the client on globalThis.
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log: process.env.NODE_ENV === "development" ? ["query", "error"] : ["error"],
});
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}Then every file does import { prisma } from "@/lib/prisma". One client, no leaks, hot reload survives.
Using Prisma in Server Components
Prisma is a Node-only library (it ships native binaries). In Next.js App Router, that means it works in Server Components, Route Handlers, and Server Actions, but not in Edge runtime or client components.
// Server Component - runs on the Node server
import { prisma } from "@/lib/prisma";
export default async function UsersPage() {
const users = await prisma.user.findMany({
orderBy: { createdAt: "desc" },
take: 20,
});
return (
<ul>
{users.map((u) => (
<li key={u.id}>{u.name}</li>
))}
</ul>
);
}Prisma Studio
Prisma ships a GUI browser for your database. Run it locally to inspect rows, edit data, and explore relations without writing SQL.
npx prisma studio
# Opens http://localhost:5555When Prisma stops being magic
Two cases where you'll feel the limits:
- Complex queries - window functions, CTEs, heavy analytics. Drop to raw SQL with
prisma.$queryRaw. - Edge runtime - Prisma needs Node. Use Accelerate (HTTP-based) or switch to Drizzle if your whole stack lives at the edge.
// Raw SQL when you need it - still type-safe via generics
const rows = await prisma.$queryRaw`
SELECT u.id, u.name, COUNT(o.id) AS order_count
FROM "User" u
LEFT JOIN "Order" o ON o.user_id = u.id
GROUP BY u.id, u.name
HAVING COUNT(o.id) >= ${10}
`;Quiz
Which command applies pending migrations safely in production?
Recap
- Schema lives in
prisma/schema.prisma. Models use@field attributes and@@model attributes. migrate devfor local changes (interactive);migrate deployfor production (non-interactive).prisma generateemits a typed client. Prisma 7 made it explicit - add it topostinstallif you miss the old auto-run.- Use a singleton via
globalThisto avoid connection leaks during dev hot reload. - Prisma is Node-only; pair with Accelerate for connection pooling and edge access. Drop to
$queryRawfor complex SQL. npx prisma studiois the built-in DB browser.