Search across all documentation pages
npm install prisma @prisma/client
npx prisma init// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}npx prisma migrate dev --name init
npx prisma generate// lib/prisma.ts
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.When to reach for this: You need type-safe database queries in a Next.js app with auto-generated types, relation handling, and schema migrations.
// app/posts/page.tsx (Server Component)
import { prisma } from "@/lib/prisma";
export default async function PostsPage() {
const posts = await prisma.post.findMany({
where: { published: true },
include: { author: { select: { name: true, email: true } } },
orderBy: { createdAt:
// app/posts/actions.ts
"use server";
import { prisma } from "@/lib/prisma";
import { revalidatePath } from "next/cache";
export async function createPost(formData: FormData) {
const title = formData.get("title"
// app/posts/new/page.tsx
"use client";
import { createPost } from "../actions";
export default function NewPostPage() {
return (
<form action={createPost} className="max-w-md mx-auto p-6 space-y-4">
<input type="hidden"
What this demonstrates:
revalidatePathschema.prisma file, providing auto-completed model fields, relation traversal, and query filtersnode_modules/.prisma/client and is regenerated on prisma generate or prisma migrate devinclude and select control which related data is fetchedprisma/migrations/, tracked by a _prisma_migrations table in your databaseglobalForPrisma) prevents Next.js hot reload from creating new PrismaClient instances, which would exhaust database connectionsFiltering and pagination:
const results = await prisma.post.findMany({
where: {
AND: [
{ published: true },
{ title: { contains: searchQuery, mode: "insensitive" } },
],
},
skip: (page - 1) * pageSize,
take: pageSize,
orderBy: { createdAt: "desc" },
Transactions:
const [post, user] = await prisma.$transaction([
prisma.post.create({ data: { title: "Hello", authorId: 1 } }),
prisma.user.update({
where: { id: 1 },
data: { name: "Updated Name" },
}),
]);
Upsert (create or update):
const user = await prisma.user.upsert({
where: { email: "alice@example.com" },
update: { name: "Alice Updated" },
create: { email: "alice@example.com", name: "Alice" },
});Raw SQL for complex queries:
const results = await prisma.$queryRaw<
{ id: number; title: string }[]
>`SELECT id, title FROM "Post" WHERE "published" = true LIMIT ${limit}`;User, Post, etc.include and select — no manual type definitions neededPrisma.PostCreateInput for create data types, Prisma.PostWhereInput for filtersPrisma.PostGetPayload<{ include: { author: true } }> gives you the exact return type for a query with includesimport { Prisma } from "@prisma/client";
type PostWithAuthor = Prisma.PostGetPayload<{
include: { author: true };
}>;
function renderPost(post: PostWithAuthor) {
return `${
Connection exhaustion in dev — Next.js hot reload creates new PrismaClient instances. Fix: Use the singleton pattern shown in the Recipe section. Store the client on globalThis.
Stale generated client — After changing schema.prisma, queries may not reflect new fields. Fix: Run npx prisma generate after schema changes. prisma migrate dev does this automatically.
N+1 queries — Accessing relations in a loop without include triggers a query per iteration. Fix: Use include or select to eagerly load relations in the initial query.
BigInt serialization — BigInt fields cannot be serialized to JSON for client components. Fix: Convert BigInt to Number or String before passing to client components: Number(post.viewCount).
Enum changes require migration — Adding values to a Prisma enum requires a migration, not just prisma generate. Fix: Run npx prisma migrate dev --name add-enum-value.
DateTime timezone confusion — Prisma stores DateTime as UTC. Fix: Always handle timezone conversion on the client side, not in queries.
| Library | Best For | Trade-off |
|---|---|---|
| Prisma | Type-safe ORM with migrations | Heavier runtime, generated client |
| Drizzle ORM | Lightweight, SQL-like syntax | Less abstraction, manual migrations |
| Kysely | Type-safe query builder | No schema management or migrations |
| TypeORM | Decorator-based ORM | Less type safety, heavier |
| Knex.js | Raw SQL query builder | No type generation, manual types |
PrismaClient, opening fresh database connectionsglobalThis ensures only one instance persists across hot reloadsinclude and select in Prisma queries?include fetches all scalar fields on the parent model plus the specified relationsselect fetches only the fields you explicitly list, including relationsinclude and select at the same top levelselect when you want to minimize the data returnedimport { prisma } from "@/lib/prisma";
export default async function Page() {
const users = await prisma.user.findMany();
return <ul>{users.map(u => <li key
prisma.$transaction and what are the two forms?prisma.$transaction([query1, query2]) runs queries in orderprisma.$transaction(async (tx) => { ... }) lets you use intermediate resultsschema.prisma?node_modules/.prisma/clientschema.prisma alone does not regenerate the clientnpx prisma generate after every schema changenpx prisma migrate dev runs generate automaticallyconst page = 2;
const pageSize = 10;
const [posts, total] = await Promise.all([
prisma.post.findMany({
skip: (page - 1) *
BigInt values cannot be serialized to JSONNumber or String before passing: Number(post.viewCount)Number.MAX_SAFE_INTEGER, use String() insteadimport { Prisma } from "@prisma/client";
type PostWithAuthor = Prisma.PostGetPayload<{
include: { author: true };
}>;Prisma.PostGetPayload infers the exact shape based on include/selectPrisma.PostCreateInput and similar generated types?import { Prisma } from "@prisma/client";
const data: Prisma.PostCreateInput = {
title: "Hello",
author: { connect: { id: 1 } },
};*CreateInput, *UpdateInput, *WhereInput, and *OrderByInput for every modelprisma migrate dev and prisma db push?migrate dev creates a SQL migration file, applies it, and regenerates the clientdb push applies schema changes directly without creating migration filesmigrate dev for production workflows where you need migration historydb push for rapid prototyping or when you do not need a migration trailconst results = await prisma.$queryRaw<
{ id: number; title: string }[]
>`SELECT id, title FROM "Post" WHERE published = true`;post.author in a loop without include triggers one query per postinclude: { author: true } in the initial findMany to eagerly load relationsselect to fetch only the specific author fields you needNo API layer is needed -- Server Components run on the server and can query the database directly.