An ORM turns SQL into type-safe function calls. Here's the full CRUD surface in Prisma.
Create one client and reuse it. In serverless/Next.js, guard against hot-reload duplicates:
1// lib/prisma.ts
2import { PrismaClient } from "@prisma/client";
3
4const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
5export const prisma = globalForPrisma.prisma ?? new PrismaClient();
6if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;1// CREATE
2const task = await prisma.task.create({
3 data: { title: "Write docs", ownerId },
4});
5
6// READ many (with relation + filter + order + page)
7const tasks = await prisma.task.findMany({
8 where: { status: "TODO" },
9 include: { owner: true },
10 orderBy: { createdAt: "desc" },
11 take: 20,
12 skip: 0,
13});
14
15// READ one
16const one = await prisma.task.findUnique({ where: { id } });
17
18// UPDATE
19const updated = await prisma.task.update({
20 where: { id },
21 data: { status: "DONE" },
22});
23
24// DELETE
25await prisma.task.delete({ where: { id } });update and delete throw if the row doesn't exist. Catch the known error code and translate to 404:
1import { Prisma } from "@prisma/client";
2
3try {
4 await prisma.task.delete({ where: { id } });
5 return NextResponse.json(null, { status: 204 });
6} catch (e) {
7 if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === "P2025") {
8 return NextResponse.json({ error: "Not found" }, { status: 404 });
9 }
10 throw e;
11}Return only what the client needs and never leak internal columns:
1await prisma.user.findMany({
2 select: { id: true, name: true }, // no passwordHash!
3});The ORM is the bridge between Chapter 3's SQL and a clean, typed API. Next we wire up Create and Read end to end.