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

Sessions, JWTs & Cookies

Reading16m
43

Protecting Server Components & Server Actions

Reading12m
44

Chapter 8 — Quiz

Quiz10m
45

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 8 of 10·Chapter 8 — Authentication & Middleware
Lesson 42 of 54Reading16 min

Sessions, JWTs & Cookies

#Sessions, JWTs & Cookies¶

A pragmatic auth setup for a new Next.js 16 app: signed JWT sessions stored in HTTP-only cookies. Simple, secure, no third-party service required.

The Library: jose¶

bash
1 line
1npm i jose

jose works in both Node and Edge runtimes — important since middleware runs at the edge.

Signing a Session¶

ts
41 lines
1// lib/session.ts
2import "server-only";
3import { SignJWT, jwtVerify, type JWTPayload } from "jose";
4import { cookies } from "next/headers";
5
6const SECRET = new TextEncoder().encode(process.env.SESSION_SECRET!);
7const COOKIE = "session";
8const MAX_AGE = 60 * 60 * 24 * 30; // 30 days
9
10export type SessionPayload = JWTPayload & { uid: string; role: "USER" | "ADMIN" };
11
12export async function createSession(payload: SessionPayload) {
13  const token = await new SignJWT(payload)
14    .setProtectedHeader({ alg: "HS256" })
15    .setIssuedAt()
16    .setExpirationTime("30d")
17    .sign(SECRET);
18
19  (await cookies()).set(COOKIE, token, {
20    httpOnly: true,
21    secure: process.env.NODE_ENV === "production",
22    sameSite: "lax",
23    path: "/",
24    maxAge: MAX_AGE,
25  });
26}
27
28export async function readSession(): Promise<SessionPayload | null> {
29  const token = (await cookies()).get(COOKIE)?.value;
30  if (!token) return null;
31  try {
32    const { payload } = await jwtVerify(token, SECRET);
33    return payload as SessionPayload;
34  } catch {
35    return null;
36  }
37}
38
39export async function destroySession() {
40  (await cookies()).delete(COOKIE);
41}

Logging In¶

ts
19 lines
1// app/(auth)/login/actions.ts
2"use server";
3import { redirect } from "next/navigation";
4import bcrypt from "bcryptjs";
5import { db } from "@/lib/db";
6import { createSession } from "@/lib/session";
7
8export async function login(prev: unknown, formData: FormData) {
9  const email = formData.get("email")?.toString() ?? "";
10  const password = formData.get("password")?.toString() ?? "";
11
12  const user = await db.user.findUnique({ where: { email } });
13  if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
14    return { error: "Invalid credentials" };
15  }
16
17  await createSession({ uid: user.id, role: user.role });
18  redirect("/dashboard");
19}

Logging Out¶

ts
8 lines
1"use server";
2import { destroySession } from "@/lib/session";
3import { redirect } from "next/navigation";
4
5export async function logout() {
6  await destroySession();
7  redirect("/login");
8}

Reading the Current User¶

ts
10 lines
1// lib/auth.ts
2import { cache } from "react";
3import { readSession } from "@/lib/session";
4import { db } from "@/lib/db";
5
6export const getCurrentUser = cache(async () => {
7  const session = await readSession();
8  if (!session) return null;
9  return db.user.findUnique({ where: { id: session.uid } });
10});

cache() ensures a single DB lookup per request, even if many components call getCurrentUser().

Verifying in Middleware¶

ts
15 lines
1// middleware.ts
2import { jwtVerify } from "jose";
3
4const SECRET = new TextEncoder().encode(process.env.SESSION_SECRET!);
5
6export async function middleware(req: NextRequest) {
7  const token = req.cookies.get("session")?.value;
8  if (!token) return NextResponse.redirect(new URL("/login", req.url));
9  try {
10    await jwtVerify(token, SECRET);
11    return NextResponse.next();
12  } catch {
13    return NextResponse.redirect(new URL("/login", req.url));
14  }
15}

Things To Get Right¶

  • HttpOnly: cookies can't be read by JS — prevents XSS token theft
  • SameSite=lax: prevents CSRF on cross-site GETs
  • Secure in production: cookies only travel over HTTPS
  • Path=/: cookie sent on every request
  • Rotate the SESSION_SECRET on a known cadence (every 90 days is reasonable)
  • Short expiration + refresh for high-security apps; long expiration is fine for most consumer apps

When To Use a Library Instead¶

For OAuth providers (Google, GitHub, Apple), magic links, and account linking, use Auth.js (next-auth v5). It plugs into the same cookie/session model and saves you weeks of work.

Previous

Middleware Fundamentals

Next

Protecting Server Components & Server Actions

Use ← → arrow keys to navigate between lessons