Skip to content
Back to Stack Recommender

Next.js App Router Architecture

Next.js 15 App Router with RSC, Server Actions, Drizzle ORM, and streaming — the modern full-stack architecture.

Full-StackComplex

Stack Components

FrameworkNext.js 15 (App Router)
DatabasePostgreSQL + Drizzle ORM
AuthAuth.js v5 (cookies)
ValidationZod + conform
StylingTailwindCSS 4
DeployVercel / Fly.io

Why

App Router's React Server Components ship 70% less JS to client. Server Actions eliminate API routes for mutations.

Drizzle is 3x faster than Prisma with SQL-like syntax. Streaming + Suspense show content instantly while data loads.

When

Choose App Router for new Next.js apps needing SEO + interactivity, dashboards with heavy data, and teams wanting fewer API endpoints. Avoid if you're migrating a large Pages Router app incrementally (mixing is confusing).

How

1. Scaffold — npx create-next-app@latest --app --typescript --tailwind 2. Drizzle setup — npm i drizzle-orm pg, drizzle-kit generate + migrate 3. Server Components — default in app/, fetch data with await db.query 4. Server Actions — "use server" functions for mutations, revalidatePath after 5. Streaming — wrap slow components in <Suspense fallback={...}> for instant shell 6. Auth — Auth.js v5 with cookies, auth() in RSC, useSession in client 7. Forms — conform + Zod for progressive enhancement, works without JS 8. Deploy — vercel deploy, or fly.io for self-hosted with Postgres

Example

// db/schema.ts — Drizzle ORM import { pgTable, serial, varchar, timestamp } from "drizzle-orm/pg-core"; export const users = pgTable("users", { id: serial("id").primaryKey(), email: varchar("email").notNull().unique(), name: varchar("name").notNull(), createdAt: timestamp("created_at").defaultNow(), }); // app/users/actions.ts — Server Action "use server"; import { db } from "@/db"; import { users } from "@/db/schema"; import { revalidatePath } from "next/cache"; import { z } from "zod"; const schema = z.object({ email: z.string().email(), name: z.string().min(1) }); export async function createUser(formData: FormData) { const data = schema.parse(Object.fromEntries(formData)); await db.insert(users).values(data); revalidatePath("/users"); // instant cache invalidation } // app/users/page.tsx — RSC with streaming import { db } from "@/db"; import { Suspense } from "react"; async function UserList() { const allUsers = await db.query.users.findMany(); return <ul>{allUsers.map((u) => <li key={u.id}>{u.name}</li>)}</ul>; } export default function Page() { return ( <main> <h1>Users</h1> <Suspense fallback={<p>Loading...</p>}> <UserList /> </Suspense> <form action={createUser}> <input name="email" type="email" required /> <input name="name" required /> <button type="submit">Add</button> </form> </main> ); }

Common Problems

  • ⚠'use client' overuse — marks whole tree client, kills RSC benefits, push to leaf components
  • ⚠Server Action without revalidate — data stale after mutation, must call revalidatePath
  • ⚠Drizzle query in client component — leaks DB to browser, move to RSC or API route
  • ⚠Suspense fallback flashes — use loading.tsx convention for route-level streaming

Best Practices

  • ✓Default to Server Components — add 'use client' only for interactivity (onClick, useState)
  • ✓Use Server Actions for all mutations — no API routes needed, type-safe end-to-end
  • ✓Drizzle RQB for queries — chainable, type-safe, compiles to optimal SQL
  • ✓Parallel data fetching — Promise.all in RSC to avoid waterfall loading

FAQ

What is Next.js App Router Architecture and what projects is it for?

Next.js 15 App Router with RSC, Server Actions, Drizzle ORM, and streaming — the modern full-stack architecture. It pairs layered technologies for end-to-end type safety and out-of-the-box deploys, fitting everything from MVPs to production SaaS.

How do I spin up a working demo with Next.js App Router Architecture in 10 minutes?

Two steps: (1) Scaffold — npx create-next-app@latest --app --typescript --tailwind; (2) Drizzle setup — npm i drizzle-orm pg, drizzle-kit generate + migrate. Get the local dev server running, then wire one real data source to verify the end-to-end loop.

What is the most common pitfall with Next.js App Router Architecture and how do I avoid it?

The most common pitfall is: 'use client' overuse — marks whole tree client, kills RSC benefits, push to leaf components. Avoid it by adding the matching checklist to your PR template and running type-check + lint + unit tests in CI — three gates before merge.

What are the best practices for Next.js App Router Architecture?

Best practices for Next.js App Router Architecture include: Default to Server Components — add 'use client' only for interactivity (onClick, useState); Use Server Actions for all mutations — no API routes needed, type-safe end-to-end; Drizzle RQB for queries — chainable, type-safe, compiles to optimal SQL. The first one — "Default to Server Components — add 'use client' only for interactivity (onClick, useState)" — should be set up at project init to avoid costly rework later.

When should I NOT choose Next.js App Router Architecture?

Pick something else in three cases: (1) the team has zero experience with the core framework and the deadline is tight (learning-curve risk); (2) it's a pure content/marketing site (Astro is lighter); (3) you need very high throughput (>10k RPS) or strict-consistency microservices (Go/Rust). Next.js App Router Architecture shines for mid-to-high complexity full-stack and API work — don't over-engineer simple static pages with it.