Back to projects

Next.js Dashboard App

Full-stack dashboard with secure authentication, PostgreSQL database management, and robust schema validation using Zod.

Next.jsTypeScriptTailwind CSSPostgreSQLAuth.js
Next.js Dashboard App

Problem

I wanted a serious reference implementation of the full Next.js App Router stack — not just a marketing page, but a real CRUD dashboard with auth, a typed database, schema-validated forms, and the streaming/Suspense patterns that Next 14+ enables. Most tutorials stop at the API-route layer; this one had to go all the way through to a deployed Postgres database.

Approach

App Router throughout, with server components doing the data fetching directly against Postgres via parameterised SQL. Forms are mutations via server actions, with Zod schemas catching invalid input before it touches the database. Auth.js v5 handles credentials-based login with JWT sessions and middleware-level route protection. Loading and error states use the App Router conventions (`loading.tsx`, `error.tsx`) so each route segment streams independently.

Stack & rationale

  • Next.js App Router
    Server components let the dashboard pages fetch and render database data without a separate API tier — fewer hops, lower latency, no client-side waterfall.
  • TypeScript
    Types flow from Zod schemas through server actions into components, so a mismatched form field surfaces as a build error rather than a runtime 500.
  • PostgreSQL
    Relational data (customers, invoices, revenue) maps cleanly to SQL joins. Parameterised queries keep input-validation honest.
  • Auth.js v5
    JWT sessions + middleware route protection is the path of least resistance for a single-user dashboard; no session store to operate.
  • Zod
    Single schema covers both form-side validation and the server-action payload check — one definition, two enforcement points.

Highlights

  • Server components fetch directly from Postgres — no internal REST/RPC layer
  • Server actions for create/update/delete with Zod-validated payloads
  • Middleware-level auth gating: unauthenticated requests redirect before any handler runs
  • Per-segment streaming with `loading.tsx` so the dashboard shell appears instantly while data fills in
  • Search and pagination handled via URL search params (back/forward buttons just work)