CoachnestCoachnest
Sign InGet Started
Back to course

Next.js 16: The Complete Developer Guide

…
—
Contents
1

What's New in Next.js 16

Reading14mFree
2

Installation, CLI & Your First Project

Reading12mFree
3

Project Structure & Conventions Deep Dive

Reading16m
4

Turbopack — The New Default Bundler

Video18m
5

Configuring TypeScript, ESLint & next.config.ts

Reading14m
6

Chapter 1 — Quiz

Quiz10m
7

App Router Fundamentals

Reading16m
8

Dynamic Routes, Catch-Alls & Type-Safe Params

Reading14m
9

Route Groups & Parallel Routes

Reading16m
10

Intercepting Routes & Modal Patterns

Reading12m
11

Loading, Error & Not-Found UI

Reading12m
12

Chapter 2 — Routing Quiz

Quiz12m
13

Understanding the Server/Client Boundary

Reading18m
14

Choosing When to use "use client"

Reading14m
15

Composing Server & Client Components

Reading14m
16

server-only, client-only & Code Splitting

Reading12m
17

Chapter 3 — Quiz

Quiz10m
18

Fetching Data in Server Components

Reading14m
19

The Next.js 16 Cache Model

Reading16m
20

Revalidation: revalidateTag, revalidatePath & On-Demand

Reading12m
21

Cache Components — Building Reusable Cached Functions

Reading14m
22

Search Params, Cookies & Dynamic APIs

Reading12m
23

Chapter 4 — Quiz

Quiz12m
24

Server Actions From First Principles

Reading16m
25

Forms with useActionState & Progressive Enhancement

Reading14m
26

Optimistic UI with useOptimistic

Reading12m
27

Validation with Zod + Server Actions

Reading12m
28

Chapter 5 — Quiz

Quiz10m
29

Static vs Dynamic vs Streaming

Reading14m
30

generateStaticParams & Pre-Rendering Dynamic Routes

Reading12m
31

Incremental Static Regeneration (ISR)

Reading12m
32

Partial Prerendering (PPR)

Reading16m
33

Edge Runtime vs Node.js Runtime

Reading12m
34

Rendering Strategies Deep Dive

Video22m
35

Chapter 6 — Quiz

Quiz12m
36

Tailwind CSS v4 with Next.js 16

Reading14m
37

CSS Modules, Global Styles & Scoped CSS

Reading10m
38

next/image — Smart, Fast Images

Reading14m
39

Fonts, Icons & Metadata

Reading12m
40

Chapter 7 — Quiz

Quiz10m
41

Middleware Fundamentals

Reading14m
42

Sessions, JWTs & Cookies

Reading16m
43

Protecting Server Components & Server Actions

Reading12m
44

Chapter 8 — Quiz

Quiz10m

Prisma with Next.js — The Production Setup

Reading14m
46

Mutations: Server Actions + Database Writes

Reading12m
47

Route Handlers & REST APIs

Reading12m
48

Chapter 9 — Quiz

Quiz10m
49

Unit & Component Testing with Vitest

Reading12m
50

End-to-End Testing with Playwright

Reading14m
51

Deploying to Vercel

Reading12m
52

Self-Hosting with Docker

Reading14m
53

Production Performance Checklist

Reading12m
54

Final Assessment — Next.js 16 Mastery

Quiz20m
←→navigate lessons
Chapter 9 of 10·Chapter 9 — Database Integration & APIs
Lesson 45 of 54Reading14 min

Prisma with Next.js — The Production Setup

#Prisma with Next.js — The Production Setup¶

Prisma is the most popular ORM for Next.js. It gives you a type-safe query builder, schema migrations, and a great DX.

Install¶

bash
3 lines
1npm i @prisma/client
2npm i -D prisma
3npx prisma init

Choose Postgres in the prompts (most flexible).

A Real Schema¶

prisma
57 lines
1// prisma/schema.prisma
2generator client {
3  provider = "prisma-client-js"
4}
5
6datasource db {
7  provider = "postgresql"
8  url      = env("DATABASE_URL")
9}
10
11model User {
12  id           String   @id @default(cuid())
13  email        String   @unique
14  name         String?
15  passwordHash String
16  role         Role     @default(STUDENT)
17  createdAt    DateTime @default(now())
18
19  courses      Course[]
20  enrollments  Enrollment[]
21}
22
23enum Role { STUDENT INSTRUCTOR ADMIN }
24
25model Course {
26  id          String  @id @default(cuid())
27  slug        String  @unique
28  title       String
29  description String  @db.Text
30  createdById String
31
32  createdBy   User    @relation(fields: [createdById], references: [id])
33  lessons     Lesson[]
34  enrollments Enrollment[]
35}
36
37model Lesson {
38  id       String @id @default(cuid())
39  courseId String
40  title    String
41  content  String @db.Text
42  order    Int
43
44  course Course @relation(fields: [courseId], references: [id], onDelete: Cascade)
45}
46
47model Enrollment {
48  id        String   @id @default(cuid())
49  userId    String
50  courseId  String
51  createdAt DateTime @default(now())
52
53  user   User   @relation(fields: [userId], references: [id])
54  course Course @relation(fields: [courseId], references: [id])
55
56  @@unique([userId, courseId])
57}

The Singleton Client¶

In dev, hot-reload would create a new PrismaClient on every save → DB connection exhaustion.

ts
11 lines
1// lib/db.ts
2import "server-only";
3import { PrismaClient } from "@prisma/client";
4
5const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
6
7export const db =
8  globalForPrisma.prisma ??
9  new PrismaClient({ log: process.env.NODE_ENV === "development" ? ["query", "error"] : ["error"] });
10
11if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = db;

Generating the Client¶

After every schema change:

bash
2 lines
1npx prisma generate            # regenerates the typed client
2npx prisma migrate dev --name <reason>   # applies a migration in dev

Add to package.json so it runs on install:

json
7 lines
1{
2  "scripts": {
3    "postinstall": "prisma generate",
4    "db:push":   "prisma db push",
5    "db:studio": "prisma studio"
6  }
7}

Migrations vs db push¶

CommandUse For
prisma migrate devVersioned migrations, committed to git
prisma db pushQuick prototyping, no migration files

Use migrate for any project that will reach production. Use db push for fast experimentation only.

Querying¶

ts
18 lines
1// Find with relations
2const course = await db.course.findUnique({
3  where: { slug },
4  include: { lessons: { orderBy: { order: "asc" } } },
5});
6
7// Aggregate
8const stats = await db.enrollment.groupBy({
9  by: ["courseId"],
10  _count: { _all: true },
11  having: { courseId: { _count: { _all: { gt: 50 } } } },
12});
13
14// Transactions
15await db.$transaction(async (tx) => {
16  await tx.course.update({ where: { id }, data: { status: "PUBLISHED" } });
17  await tx.notification.create({ data: { /* … */ } });
18});

Connection Pooling¶

In serverless (Vercel), each invocation creates a fresh process. You must front Postgres with a pooler:

  • Neon has a built-in pooler — use the -pooler URL.
  • Supabase offers Supavisor.
  • PgBouncer for self-hosted.

For Prisma + serverless, also enable directUrl for migrations:

prisma
4 lines
1datasource db {
2  url      = env("DATABASE_URL")        // pooled, used at runtime
3  directUrl = env("DIRECT_URL")          // direct, used only for migrations
4}

Previous

Chapter 8 — Quiz

Next

Mutations: Server Actions + Database Writes

Use ← → arrow keys to navigate between lessons