· nestjs / fastify / nodejs

NestJS vs Fastify — frameworks vs micro-frameworks

NestJS for large TypeScript teams that want structure; Fastify for microservices and edge where cold start and throughput matter. The numbers behind each call.

By

1,888 words · 10 min read

If you are building a monolith with a large TypeScript team, pick NestJS. If you are building low-latency microservices or deploying to serverless edge, pick Fastify. The two frameworks solve different problems, and neither is a sensible default for the other’s use case.

Who this is for

Node.js backend developers choosing a primary framework for a new project — or evaluating whether to migrate an existing Express app. If you are already deep in NestJS with established patterns, the migration calculus is different; skip to verdict.

What we tested

Numbers in this article come from two independent benchmark runs:

  • Fastify official benchmarks (January 2026): single-thread “hello world,” measuring pure framework overhead.
  • drcodes.com (Node.js 20.11 LTS, Ubuntu 22.04, standard hello-world endpoints): throughput, p99 latency, memory, and cold start across Fastify, Express, and NestJS with the default Express adapter.

For multi-core and adapter comparisons: pkgpulse.com (March 2026, NestJS + Fastify adapter vs standalone Fastify).
For production realism: leapcell.io (DB-call scenario, NestJS vs Express equivalent workload).


Quick verdict

DimensionNestJS (Express adapter)Fastify
Throughput (raw)28,163 req/s76,835 req/s
Throughput (with DB calls)~8,500 QPS~9,200 QPS (Express baseline)
p99 latency58ms18ms
Memory per worker89MB45MB
Cold start340ms125ms
DXStructured (DI, decorators, modules)Minimal — bring your own
Learning curveSteep without DI backgroundShallow — Express-familiar
EcosystemRich (Swagger, CQRS, auth, TypeORM)Focused (official plugins, Ajv)
Enterprise supportYes — Trilon commercial contractsOpenJS Foundation

The raw throughput gap is real. The production gap with DB calls narrows to 8%. p99 latency and cold start survive into real workloads — those numbers matter for edge deployments.


What NestJS gets right

Dependency injection that works at scale

NestJS ships Angular-style DI: providers, modules, and constructor injection via TypeScript decorators. In practice, this means you can swap a UserRepository for a mock in tests without monkey-patching globals.

@Injectable()
export class UsersService {
  constructor(private readonly usersRepo: UsersRepository) {}

  findOne(id: string) {
    return this.usersRepo.findById(id);
  }
}

Testing is clean:

const module = await Test.createTestingModule({
  providers: [
    UsersService,
    { provide: UsersRepository, useClass: MockUsersRepository },
  ],
}).compile();

Teams coming from Spring Boot or C# find the module system immediately recognizable. That pattern scales to 50 engineers without a meeting to agree on conventions — the framework enforces the conventions.

Pipes, guards, and interceptors

NestJS’s request lifecycle is explicit: guards for auth, pipes for validation, interceptors for logging and transformation. Each is typed, testable in isolation, and declared where it attaches.

@UseGuards(JwtAuthGuard)
@UsePipes(new ValidationPipe({ transform: true }))
@Controller('users')
export class UsersController {}

There is no configuration-by-convention magic here. Where it attaches is where it runs. Debugging request processing means reading decorators, not scanning middleware arrays.

Swagger auto-generation

@nestjs/swagger reads your DTOs and controller decorators and emits an OpenAPI spec. No YAML files, no drift between implementation and spec. For teams that ship an API as a product — internal platform teams, B2B SaaS — this alone can justify the framework overhead.

@ApiProperty({ example: '[email protected]', format: 'email' })
email: string;

That annotation propagates into the generated spec. No extra build step.

CQRS and event sourcing out of the box

@nestjs/cqrs ships command handlers, query handlers, and an event bus. You can model a complex domain without wiring these patterns yourself. Not every team needs this. The teams that do appreciate not building it from scratch.

Community criticism worth knowing

Cold start is the real serverless problem. 340ms vs Fastify’s 125ms is a 2.7× difference. Under sustained cold-start pressure on Lambda, that is felt in p50 latency for the first request after an idle period. NestJS teams solve this with provisioned concurrency (adds cost) or by switching to the Fastify adapter (see verdict).

class-validator has open security reports. If you use ValidationPipe with the default class-validator setup, check CVEs before shipping. zod or Ajv-based validation sidesteps this entirely.

TypeORM N+1 problems. TypeORM is the default ORM recommendation in the NestJS docs. Its eager relation loading generates N+1 queries when you are not careful. Use Prisma or Drizzle instead — both integrate cleanly with NestJS, and Prisma’s query API makes it structurally harder to add accidental N+1s. For a full side-by-side, see best TypeScript ORM for 2026.


What Fastify gets right

The benchmark is real — partially

Fastify’s official benchmark: 46,664 req/s vs Express’s 9,433 req/s — about 5× in a synthetic, single-thread hello-world. The drcodes.com multi-run puts Fastify at 76,835 req/s vs NestJS+Express adapter at 28,163 req/s — a 2.7× gap.

In a realistic application with database calls (leapcell.io benchmark), NestJS hits ~8,500 QPS vs a bare Express baseline at ~9,200 QPS — an 8% difference. The raw gap disappears. Two numbers survive into real workloads: p99 latency (18ms vs 58ms) and cold start (125ms vs 340ms). For serverless functions and latency-sensitive paths, those are real.

Ajv schema validation as a first-class feature

Fastify validates and serializes via Ajv by default. You declare the schema on the route:

const schema = {
  body: {
    type: 'object',
    properties: {
      email: { type: 'string', format: 'email' },
    },
    required: ['email'],
  },
};

fastify.post('/users', { schema }, async (request, reply) => {
  // request.body is validated — bad input never reaches here
});

Ajv JIT-compiles schemas on startup. Validation is fast and explicit. The schema is plain data — shareable with a client SDK generator, serializable to JSON Schema, testable in isolation from the HTTP layer.

Plugin system with scoped ownership

Fastify plugins wrap scope. A plugin that registers a decorator inside fastify.register(plugin) does not leak into sibling plugins. This forces composable structure without a framework imposing modules.

async function authPlugin(fastify, options) {
  fastify.decorate('verifyToken', async (token) => {
    // verify logic
  });
}

fastify.register(authPlugin);

The Val Town migration from Express to Fastify cited the plugin ecosystem as a key reason: every rate-limiting and observability need had a high-quality, first-party official plugin. Their primary stated drivers were enforcing the OpenAPI spec in code (not a dead YAML file) and native async/await compatibility — the scoped plugin model made everything composable without conflicts.

Memory footprint

45MB per worker vs NestJS’s 89MB. At scale across many containerized microservice instances, that is real infrastructure cost. In environments with tight per-container memory limits (Lambda’s 128MB tier, for example), the difference between fitting and not fitting is practical, not theoretical.


Real-world case studies

Val Town (named company, primary source): migrated their public API from Express to Fastify in July 2024. Their stated reasons were ecosystem quality — every observability tool had an official first-party plugin — and native async/await throughout. Raw throughput was not the deciding factor; their words were “web framework overhead wasn’t a problem with Express.” The @fastify/express compatibility layer let them migrate route-by-route without downtime.

Anonymous team (johal.in, 2026): migrated 47 production Fastify microservices to NestJS in Q1 2026. Reported 40.2% faster API development velocity. The gain was in DX and enforced structure, not runtime performance. Company is unnamed — treat as directional, not definitive.

Enterprise NestJS users: Autodesk runs NestJS at 1B+ daily requests with 99.99% uptime; Adidas runs it across 30+ global regions for e-commerce microservices; Roche uses it for medical data pipelines (TypeScript safety cited as a compliance requirement). NestJS’s commercial enterprise support via Trilon is the differentiator for regulated industries with SLA contracts.


Migration from Express

To Fastify: Install @fastify/express and migrate routes one at a time. Val Town’s migration was complete — all but two routes — before they published the blog post. Main friction points: Express middleware API differences, and Express’s next(err) pattern needs to become thrown errors or reply.send(error). Budget two days to understand Fastify’s lifecycle hooks before tackling complex middleware. For a focused Fastify-versus-Express benchmark and migration guide, see Fastify vs Express.

To NestJS: The @nestjs/platform-express adapter lets you point NestJS at an existing Express app structure, but that defeats the DI benefits. A real NestJS migration rewrites controllers and services into modules. For a 200-route Express API, budget two to four weeks for a small team — not a weekend. The upfront cost is real; the payback is lower onboarding time for every subsequent engineer.


Verdict per use case

Monolith with a team of 5 or more: NestJS. The module system, DI, and testing utilities pay back their setup cost by the time you hit 20 routes. The enforced structure prevents the sprawl that eventually kills Express monoliths.

Microservice or serverless edge function: Fastify. 125ms cold start vs 340ms is meaningful on Lambda under load. 2.7× throughput advantage holds in framework-overhead-heavy workloads — many small fast requests, minimal DB. The plugin ecosystem covers observability and auth without pulling in a full framework.

Mid-path — you want NestJS structure but cold starts are a problem: NestJS with the Fastify adapter. Swap the platform in main.ts:

import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';

const app = await NestFactory.create<NestFastifyApplication>(
  AppModule,
  new FastifyAdapter(),
);
await app.listen({ port: 3000, host: '0.0.0.0' });

pkgpulse.com (March 2026) puts NestJS + Fastify adapter at ~180K req/s in multi-core optimized configs vs ~230K for standalone Fastify. You give up about 22% of Fastify’s throughput ceiling; you keep NestJS’s entire module and DI system. For most teams the trade is worth it.

Greenfield solo or small team without Spring/Angular background: Fastify with @fastify/autoload. There is no DI to learn. You pick an ORM, autoload your plugins and routes, and ship. NestJS’s module system is a genuine cognitive burden if you don’t have the background that makes it feel familiar — plan for a week of confusion before it clicks.


Deploying either

Railway handles both NestJS and Fastify with native Node.js detection. Push code; Railway builds and deploys without YAML. Their deployment guides for both frameworks cover environment variables, health checks, and persistent storage.


Caveats

  • The throughput gap narrows to 8% with DB calls. If your API is DB-bound, make the choice on DX and team background, not benchmarks.
  • The NestJS 340ms cold start is measured with the default Express adapter. Switching to the Fastify adapter reduces it; the exact reduction depends on how many modules you load.
  • The anonymous team case study (40.2% dev velocity gain) is self-reported, company-unnamed, and not controlled. Treat it as directional evidence.
  • The enterprise adoption figures for Autodesk, Adidas, and Roche are cited from leapcell.io, which does not provide primary citations for those claims. Treat as directional — these companies are publicly known NestJS users, but the specific metrics (1B+ daily requests, 30+ regions) are unverified at source.
  • This article contains affiliate links (Railway, Prisma). See full disclosure.

References

  1. Fastify official benchmarks — January 2026
  2. drcodes.com Node.js 20.11 benchmark
  3. leapcell.io realistic workload benchmark
  4. pkgpulse.com NestJS+Fastify adapter benchmark — March 2026
  5. Val Town Fastify migration — July 2024
  6. Anonymous NestJS migration — 2026
  7. OpenJS Foundation Fastify growth — September 2024
  8. NestJS GitHub — data: May 2026
  9. Fastify GitHub — data: May 2026