Prisma ORM Cheat Sheet
Covers Prisma schema syntax, migrations, the type-safe query client, and relation queries for building Node.js/TypeScript data layers.
Prisma Schema File
Datasource, generator, enum, and relation definitions.
datasource db { provider = "postgresql" url = env("DATABASE_URL")}generator client { provider = "prisma-client-js"}enum Role { USER ADMIN}model User { id Int @id @default(autoincrement()) email String @unique role Role @default(USER) posts Post[]}model Post { id Int @id @default(autoincrement()) title String published Boolean @default(false) author User @relation(fields: [authorId], references: [id]) authorId Int @@index([authorId])}
Prisma CLI Workflow
Common commands for schema, migrations, and tooling.
# Create and apply a migration, generating SQL from schema changesnpx prisma migrate dev --name add_user_role# Regenerate the type-safe client after any schema changenpx prisma generate# Push schema to DB without creating a migration (prototyping only)npx prisma db push# Open a GUI to browse/edit datanpx prisma studio# Apply pending migrations in production (no shadow DB / no prompts)npx prisma migrate deploy
Prisma Client Queries
Create, read with relations, and transactions.
import { PrismaClient } from '@prisma/client';const prisma = new PrismaClient();// Create with a nested relation writeconst user = await prisma.user.create({ data: { email: '[email protected]', posts: { create: [{ title: 'Hello World' }] }, },});// Query with relation include and filteringconst published = await prisma.post.findMany({ where: { published: true }, include: { author: true }, orderBy: { id: 'desc' }, take: 10,});// Interactive transaction across multiple writesawait prisma.$transaction(async (tx) => { await tx.user.update({ where: { id: 1 }, data: { role: 'ADMIN' } }); await tx.post.updateMany({ where: { authorId: 1 }, data: { published: true } });});
Key Concepts
Core pieces of the Prisma toolchain.
- schema.prisma- Single source of truth defining the datasource, generator, and data models; migrations and the client are generated from it
- Migration history- Stored as timestamped SQL files under prisma/migrations, applied in order and tracked in a _prisma_migrations table
- Prisma Client- Auto-generated, fully typed query builder based on your schema; regenerate with `prisma generate` after every schema edit
- Relation queries- `include` eagerly loads related records; `select` picks specific fields, both prevent over-fetching or under-fetching
- $transaction- Batches multiple queries atomically; sequential array form or the interactive callback form for dependent operations
- Shadow database- A temporary database Prisma uses in dev to detect drift and generate migrations safely without touching your real data
Client Extensions (Middleware Replacement)
Intercept queries for soft deletes, auditing, or logging.
// $extends replaces the deprecated $use middleware API (Prisma 5+)const prisma = new PrismaClient().$extends({ query: { post: { async delete({ args, query }) { // Turn hard deletes into soft deletes return prisma.post.update({ ...args, data: { deletedAt: new Date() }, }); }, }, }, result: { user: { fullName: { needs: { firstName: true, lastName: true }, compute(user) { return `${user.firstName} ${user.lastName}`; }, }, }, },});
Raw Queries & Aggregations
Escape hatches for complex SQL and native aggregate helpers.
// Parameterized raw query (safe against SQL injection)const rows = await prisma.$queryRaw` SELECT date_trunc('day', "createdAt") AS day, COUNT(*) AS total FROM "Post" WHERE "authorId" = ${authorId} GROUP BY day ORDER BY day DESC`;// Native aggregation helpers avoid raw SQL for common casesconst stats = await prisma.post.aggregate({ where: { published: true }, _count: { _all: true }, _avg: { views: true }, _max: { createdAt: true },});const byAuthor = await prisma.post.groupBy({ by: ['authorId'], _count: { _all: true }, having: { _count: { _all: { gt: 5 } } },});
Composite Keys, Views & Multi-Schema
Advanced schema.prisma features beyond basic models.
model OrderItem { orderId Int productId Int quantity Int @@id([orderId, productId])}// Prisma can read from database VIEWs (introspection-only, PostgreSQL)view ActiveUsers { id Int @unique email String}// Multi-schema support (previewFeature: multiSchema)model Tenant { id Int @id @default(autoincrement()) name String @@schema("tenancy")}// Referential actions beyond default cascademodel Comment { id Int @id @default(autoincrement()) postId Int post Post @relation(fields: [postId], references: [id], onDelete: Cascade, onUpdate: Restrict)}
Advanced Toolchain Concepts
Beyond the basic CRUD workflow, for production Prisma usage.
- Prisma Accelerate / Data Proxy- Connection pooling + edge caching layer for serverless/edge runtimes where direct TCP connections to Postgres aren't viable
- Interactive vs sequential transactions- `$transaction([...])` array form batches independent queries; the callback form supports read-then-write logic but holds a DB connection open for its duration — keep it short
- Preview features flag- Features like `multiSchema`, `views`, and `relationJoins` must be enabled under `generator client { previewFeatures = [...] }` before use
- Migration diffing- `prisma migrate diff` compares two schema states (or a schema vs a live DB) and emits SQL without applying it, useful for CI drift checks
- Seed scripts- `prisma db seed` runs the script configured under `prisma.seed` in package.json, commonly used after `migrate reset` in CI/test setup
- Referential integrity mode- `relationMode = "prisma"` emulates foreign keys in application code instead of the DB, needed for databases like PlanetScale that don't support FKs
Never run `prisma db push` against production — it bypasses the migration history entirely and can silently drop columns/data on divergence; reserve it for local prototyping and always use `migrate dev` / `migrate deploy` for anything that needs a reviewable, reversible migration trail.