back

Building Scalable Next.js Applications — Patterns I Use in Production

November 10, 2025·6 min read·Antima Singh

Next.js has matured into a serious full-stack framework. After shipping several production apps with it — from SaaS invoicing platforms to visa management systems — I have settled on a set of patterns that consistently make codebases easier to reason about and extend.

Folder structure matters more than you think

I co-locate related files aggressively. Route handlers, page components, and server actions for a given feature all live near each other under `app/(feature)/`. Shared logic — services, validations, types — lives in `lib/`. Components consumed across many pages live in `components/`. Nothing exotic, but the discipline pays off as a project grows.

Server Components are the default — Client Components are the exception

Unless I need interactivity (forms, animations, click handlers), everything is a Server Component. Data fetching happens at the layout or page level, results are passed down as props. This keeps the client bundle small and eliminates the waterfall of client-side data loading that plagues so many React apps.

Database access only in Server Components or Route Handlers

I never import Drizzle ORM or raw SQL into a Client Component. All DB access is wrapped in service classes (e.g. `BlogService`, `ProjectService`) that are called from server-side code only. This keeps secrets off the client and makes unit testing trivial.

Type safety end-to-end with Drizzle and Zod

Drizzle's `$inferSelect` and `$inferInsert` types flow from the schema directly into service return values. Zod validates all incoming form data at the API boundary. Together, these eliminate an entire class of runtime bugs.

Route-level caching with revalidation tags

Rather than sprinkling `cache()` calls everywhere, I tag related data (e.g. `'projects'`) and call `revalidateTag` when a mutation happens. Next.js handles the rest. Keeps pages fast without stale data creeping in.

The boring stack wins

Vercel + PostgreSQL (Neon) + Drizzle. None of these choices are clever. All of them have excellent docs, great DX, and generous free tiers. I spend time building features, not fighting infrastructure.