Building a Production REST API with Node.js and Express in 2026
Layered architecture, validation, error handling, auth, rate limiting, observability — the patterns I use to ship Node.js + Express APIs that don't fall over in production.
Why Express, in 2026
I keep getting asked: "Express? Still? Why not Hono / Fastify / Bun?"
Express is still the most common Node.js HTTP framework I see in production codebases, by a wide margin. It's not the fastest, it's not the most modern, but it's stable, mature, and every Node engineer knows it. For a senior who needs to onboard a junior in two days, Express wins.
That said, I'll mention where Fastify or Hono make sense at the end. The patterns in this post apply to all three; Express is just the example.
The architecture I keep coming back to
Five layers, each with one responsibility:
A request flows: route → controller → service → repository → DB → service → controller → response.
The reason for this separation is testability. Services don't know about HTTP, so you can unit-test business logic without booting Express. Repositories don't know about services, so you can swap MongoDB for PostgreSQL without touching business logic.
Validation: at the boundary, not throughout
Bad code I see all the time has validation scattered across the service. Better: validate at the route boundary with Zod, so your service receives already-validated data and stays clean.
I use Zod everywhere now. Joi was the standard for years; Zod is faster, has better TypeScript inference, and the schema syntax is more readable.
Error handling that doesn't suck
The Express default — a single next(err) and a global handler — is fine but easy to misuse. The pattern:
if (!user) throw new NotFoundError("User")The benefit: known errors have clear codes. Unknown errors get logged with context and a generic message goes back to the client. No leaking stack traces in production.
Authentication: JWT with refresh, not session cookies
For a stateless REST API, JWT with a short-lived access token (15 min) and a long-lived refresh token (7 days) stored in an HttpOnly cookie is the pattern I default to.
When NOT to use this: if you only need browser clients on a single domain, a simple HttpOnly session cookie is simpler and more secure (no JWT footguns). JWT shines when you have multiple clients (web + mobile + third-party API consumers).
Rate limiting: at multiple layers
A single layer of rate limiting is brittle. I use three:
Don't roll your own — express-rate-limit with a Redis store handles 99% of cases.
Observability: logs, metrics, traces
The minimum viable observability stack for a Node API:
Where to ship the data: Grafana Cloud (free tier is enough for most projects), Datadog (expensive but excellent), or Honeycomb (best for tracing).
The DB layer
For new projects in 2026 I'm using:
Mongoose is fine but Drizzle's type inference is better than Mongoose's schemas, and for SQL-relational data, Postgres is the right answer.
When to reach for Fastify or Hono instead
What I ship for clients
A typical Node.js + Express API engagement looks like:
If you're starting a new Node.js + Express project and want a senior to architect it, reach out — backend engagements are part of my core service offering.
TL;DR
You might also like
From REST to GraphQL: When the Migration is Actually Worth It
Migrated 4 client APIs from REST to GraphQL and back from 2 of them. Here's when GraphQL pays off and when REST is still the right answer in 2026.
Background Jobs in Node.js 2026: BullMQ, Trigger.dev, or Inngest?
Compared on real client projects: BullMQ vs Trigger.dev vs Inngest for Node.js background jobs. What I pick for what, with cost, DX, and operational trade-offs.
Building Production AI Agents with Claude 4.7 and Tool Use
What I learned shipping AI agents to production: tool design, prompt structure, durable execution, observability, and cost control. Practical patterns from real client work.