Learn / Frameworks / Next.js / Databases with Prisma

Next.js · Lesson 9 of 15

Databases with Prisma

Query Postgres from Server Components and mutate with server actions.

  • Intermediate
  • 17 min read
  • 3 objectives

Before this lessonLesson 8: Middleware, Cookies and Sessions

What you will learn

  • Define a Prisma schema
  • Query in a page
  • Mutate and revalidate

Your Progress

0 of 15 lessons 0%

  • Lessons0 / 15
  • Completed0
  • Est. time left~ 4 hours

Create a free account to keep your progress on every device.

Prisma is a typed ORM that fits the App Router: generate a client, query from Server Components, mutate from server actions.

Schema and client

npm install prisma @prisma/client
npx prisma init --datasource-provider postgresql
// prisma/schema.prisma
model Post {
  id        Int      @id @default(autoincrement())
  title     String
  body      String
  published Boolean  @default(false)
  createdAt DateTime @default(now())
}
// lib/db.ts
import { PrismaClient } from "@prisma/client";

const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
export const db = globalForPrisma.prisma ?? new PrismaClient();
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;

The global cache stops Next.js hot reload from opening a new connection on every save.

Read on the server

// app/posts/page.tsx
import { db } from "@/lib/db";

export default async function Posts() {
  const posts = await db.post.findMany({ where: { published: true }, orderBy: { createdAt: "desc" } });
  return <ul>{posts.map((p) => <li key={p.id}>{p.title}</li>)}</ul>;
}

Write with a server action

"use server";
import { db } from "@/lib/db";
import { revalidatePath } from "next/cache";
import { z } from "zod";

const Input = z.object({ title: z.string().min(1).max(120) });

export async function createPost(formData: FormData) {
  const parsed = Input.safeParse({ title: formData.get("title") });
  if (!parsed.success) return { error: "Title required" };
  await db.post.create({ data: { title: parsed.data.title, body: "" } });
  revalidatePath("/posts");
}
Up next · Lesson 10Errors, Parallel and Intercepting Routeserror.tsx, slots for parallel UI and intercepting routes for modals.