Hermes Wiki
Developer/Languages/TypeScript/BackendFrameworks/Fundamentals/express-fastify-nestjs-architecture

Node.js Backend Framework Architecture: Express, Fastify, NestJS

Concept

Express, Fastify, and NestJS solve the same problem — routing HTTP requests to handlers in Node — at three different layers of abstraction, and the choice between them is really a choice about where you want structure to come from: the framework, or your own conventions.

Express is a thin middleware chain: every request flows through an ordered stack of (req, res, next) functions, each free to mutate the request, short-circuit the response, or call next() to continue. There's no built-in schema validation, no dependency injection, no opinion about how a codebase is organized — that flexibility is the entire value proposition, and also why two Express codebases at different companies can look nothing alike.

Fastify keeps the same request/response shape and low-ceremony feel but rebuilds the internals around a JSON-schema-first router. Routes declare an input/output schema up front, and Fastify precompiles a serializer for that schema at startup instead of calling JSON.stringify generically on every response — that ahead-of-time compilation, plus a faster radix-tree router, is where most of its throughput advantage over Express comes from, not just "leaner middleware."

NestJS is a different kind of thing entirely: an application framework, not an HTTP server. It doesn't implement request handling itself — by default it sits on top of Express (or, configurably, Fastify) as its underlying HTTP engine — and layers modules, controllers, providers, and a dependency-injection container on top, borrowing Angular's decorator-and-DI conventions. You're not choosing NestJS instead of Fastify; you can choose NestJS with Fastify as its engine and get both the throughput and the structure.

Tradeoffs

Framework Benefit Cost
Express Huge ecosystem (~20M weekly downloads), minimal learning curve, maximum flexibility No built-in validation or schema serialization; middleware-chain overhead and generic JSON serialization leave real throughput on the table under sustained load
Fastify ~2-3x Express's throughput on JSON-heavy endpoints via schema-compiled serialization and a faster router; built-in JSON Schema validation Smaller ecosystem than Express; schema-first response typing is a real habit change, and plugins must follow Fastify's encapsulation model rather than Express's global-mutation style
NestJS Enterprise-grade structure out of the box — DI, modules, guards, interceptors, pipes — pays off fast on larger teams and longer-lived codebases Steeper learning curve (decorators, DI, Angular-derived mental model); more ceremony for a small service; still pays whatever engine-level cost its underlying HTTP adapter (Express or Fastify) has

The real axis isn't "which is fastest" — it's throughput-per-engineering-cost at your team's size. A two-person team building a webhook receiver gets little from NestJS's DI container and pays its ceremony tax for nothing; a twenty-engineer team building a long-lived service gets real value from enforced module boundaries that Express's freedom would otherwise let erode into an unstructured pile of routes.

When to use / when not to

  • Reach for Fastify when raw request throughput or CPU headroom actually matters — high-QPS internal APIs, latency-sensitive edge functions, or any service where teams have reported 20-30% server-count reductions swapping in Fastify's schema-compiled responses over Express's generic serialization.
  • Reach for NestJS when the team is 5+ engineers and the service is expected to live for years — the DI container and module boundaries pay for themselves once several people are adding features concurrently and need the compiler/framework, not code review discipline alone, to keep layers from leaking into each other.
  • Reach for plain Express for a small, short-lived, or genuinely simple service (a webhook receiver, a single-purpose edge function) where neither Fastify's schema ceremony nor NestJS's DI machinery earns its cost — and where the ecosystem's sheer size (the most Stack Overflow answers, the most battle-tested middleware) reduces integration risk.
  • Don't pick NestJS purely for "future-proofing" a service that's genuinely expected to stay small — the DI/module ceremony is a cost paid on every PR, not a one-time setup tax, and an over-structured two-route service is as much a maintenance liability as an under-structured twenty-route one.

Common pitfall

Treating "NestJS vs. Fastify" as a single choice when they're not mutually exclusive — NestJS's default HTTP adapter is Express, and teams sometimes ship an under-performing NestJS service assuming they've already gotten Fastify-level throughput, when in fact they never swapped the adapter. If NestJS's structure is wanted and Fastify's throughput matters, the adapter has to be explicitly configured to Fastify; leaving the default in place silently keeps Express's serialization overhead underneath NestJS's DI layer.

Engineering Lens

The three frameworks map cleanly onto a build vs. buy spectrum for backend structure: Express makes you build your own conventions from nothing, Fastify buys you a faster engine but still lets you build the structure, and NestJS buys the structure itself. The strongest answer in a design review isn't naming a favorite — it's being able to say which axis (throughput, or engineering structure at scale) is actually the constraint for this specific service, since the two costs (a slower request path, or an under-structured codebase at team scale) show up on completely different timelines: one shows up in a load test next sprint, the other shows up as an incident six months from now when nobody can safely change the payments module without breaking the notifications module.

Sources

Hermes Wiki