Next.js App Router in Production · Capstone

Capstone: A Course Platform on Next.js

Design a Next.js App Router application for selling and delivering online courses: rendering and caching, auth, payments, security and operations.

Create a free account to save this page, take notes and track your progress. Sign in

Scenario

An education company is building its course platform on Next.js. Visitors browse a public catalogue (thousands of course pages that must rank in search), buy courses through a payment provider, and then watch lessons and track progress. Instructors edit course content in a CMS and expect changes live within a minute. Launch days bring sudden traffic spikes.

Requirements

  • Catalogue and course pages are fast and indexable; prices and availability are correct.
  • Lesson pages are only visible to people who bought the course.
  • Purchases are recorded reliably even if the payment webhook arrives late or twice.
  • Instructors' edits appear within a minute; learners' progress updates show immediately.

Constraints

  • Next.js 16 App Router with Cache Components, PostgreSQL, a headless CMS with webhooks, a payment provider with signed webhooks.
  • Self-hosted on Kubernetes, 6 instances, Redis available.

Deliverables

  1. Rendering and caching plan per page type.
  2. Authentication and authorization.
  3. Purchases and mutations.
  4. Security.
  5. Operations.

Hints

  • Different parts of one page can have different cache lifetimes.
  • Webhooks from the CMS and the payment provider both arrive at Route Handlers.

Draft each deliverable below before revealing the model answer and rubric.

Further reading: Caching · Self-hosting · Data security

Deliverables

Describe the rendering and caching plan.

Model answer

  • Catalogue and course pages: "use cache: remote" content with cacheLife("max") and tags (course:<id>, catalogue), prerendered for popular courses with generateStaticParams, others rendered on first visit; metadata and JSON-LD from the same cached functions.
  • Price and availability: a separate cached component with a short cacheLife (minutes) or per-request read in Suspense, so content stays static while prices are current.
  • The "Buy" / "Continue learning" button depends on the user: a Suspense-wrapped component reading the session.
  • Lesson pages and progress: per-user, read in Suspense through the DAL; not shared-cached.

Rubric

Look for per-section caching with tags, shared remote caches for 6 instances, and per-user parts streamed in Suspense.

Describe authentication and authorization.

Model answer

  • An auth library (Auth.js, Better Auth) or signed httpOnly session cookies backed by a sessions table; login via OAuth and email links.
  • Proxy redirects anonymous users away from /learn/* optimistically.
  • The DAL checks entitlement (hasPurchased(userId, courseId)) for every lesson read and every Server Action (progress updates), returning DTOs.
  • Instructor tools require an instructor role checked in the DAL; admin routes separate.

Rubric

Strong answers combine optimistic Proxy redirects with authoritative entitlement checks in the DAL and every action.

Describe purchases and mutations.

Model answer

  • Checkout is created by a Server Action (validated, authenticated) that calls the payment provider and redirects to its hosted page with an idempotency key.
  • The payment webhook Route Handler verifies the signature over the raw body, deduplicates by event ID (unique constraint), records the purchase in a transaction, and calls revalidateTag("user:<id>:purchases", "max").
  • The success page shows "processing" until the purchase is recorded (polling or refresh), so late webhooks are handled.
  • Progress updates: Server Action + updateTag for read-your-own-writes. CMS webhook verifies its signature and calls revalidateTag("course:<id>", "max").

Rubric

Look for idempotent, signed webhooks as the source of truth for purchases, and tag-based invalidation from both CMS and user actions.

Describe the security measures.

Model answer

  • DTOs only to Client Components; server-only on DAL and database modules; video URLs are short-lived signed URLs generated after entitlement checks.
  • Every Server Action validates input with zod and checks session and ownership; no IDs trusted from forms.
  • Security headers and a CSP (nonces) in next.config or Proxy; secrets only in server environment variables, none with NEXT_PUBLIC_.
  • Rate limiting for login and checkout at the gateway or with Redis.

Rubric

Strong answers prevent payload leaks, authorize every action, protect content URLs, and set headers and rate limits.

Describe operations.

Model answer

  • Standalone output in a container; 6 replicas with a Redis-backed cache handler for "use cache: remote" and tag invalidation shared across instances; deploymentId per release for version skew.
  • Health endpoint (per request, no-store) for readiness; graceful shutdown with a drain period so in-flight requests and after() work finish.
  • onRequestError and OpenTelemetry (instrumentation.ts) for errors and traces; after() for analytics and emails.
  • Load test launch-day traffic against cached pages and the checkout path; autoscale on CPU and request rate.

Rubric

Look for a shared cache across instances, version-skew handling, health checks, graceful shutdown and observability.

Open in the app to mark lessons complete, take notes and track your progress.

Was this page helpful?