Topic 11 / 13
Working with Databases — Prisma
The Stack
Server components and actions can talk to a database directly — you just need an ORM. Prisma is the ecosystem favourite: schema-first, generated types, great DX:
npm install prisma @prisma/client
npx prisma init --datasource-provider sqlite # postgres in productionDefine the Schema
// prisma/schema.prisma
model Course {
id Int @id @default(autoincrement())
title String
slug String @unique
price Int
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt DateTime @default(now())
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String
courses Course[]
}Migrate
npx prisma migrate dev --name init # creates SQL migration + applies it
npx prisma studio # GUI to browse your dataMigrations are versioned files in prisma/migrations/ — commit them, exactly like Django’s migrations.
The Client Singleton
Dev hot-reload re-imports modules; without a guard you leak connections:
// lib/db.js
import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis;
export const db = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = db;
}Queries — Fully Typed
import { db } from "@/lib/db";
// in a server component
const courses = await db.course.findMany({
where: { published: true },
orderBy: { createdAt: "desc" },
include: { author: true }, // JOIN — typed result includes author
take: 10,
});
const course = await db.course.findUnique({ where: { slug } });
// aggregate
const stats = await db.course.aggregate({
_count: true,
_avg: { price: true },
});// in a server action
"use server";
export async function publishCourse(id) {
const session = await auth();
if (!session) throw new Error("unauthorised");
await db.course.update({
where: { id },
data: { published: true },
});
revalidatePath("/courses");
}Everything autocompletes and type-errors at build time — rename a field in the schema and TypeScript flags every stale query.
Production Notes
- Use Postgres in production (Neon, Supabase, RDS); keep
DATABASE_URLin env vars. - Serverless deployments need a connection-pooling URL (Neon/Supabase provide one) — each lambda would otherwise open its own connections.
- Deploy schema changes with
npx prisma migrate deployin CI before the app starts. - DB queries aren’t fetch-cached — use route
revalidateorunstable_cache(data-fetching topic) for hot reads.
Drizzle is the lighter SQL-flavoured alternative gaining ground; the architecture (schema → migrate → typed queries from server code) is identical.