Technology Comparisons

Fastify vs Express

Express has been the default Node.js framework for over a decade. Fastify is the modern alternative — better TypeScript support, better performance, fewer footguns. I use Fastify for all my backends now.

Quick Comparison

Fastify Express
First release 2016 2010
TypeScript Built-in type definitions @types/express (community)
Performance ~2-3x faster than Express Baseline
Plugin system Encapsulated, async-aware Middleware chain
Validation Built-in (JSON Schema / Ajv) Bring your own
Serialization Automatic fast-json-stringify JSON.stringify
Logging Built-in (pino) Bring your own
Lifecycle hooks onRequest, preHandler, onSend, etc. Middleware only
Async/await Native error handling Native in Express 5, required wrappers in v4

Performance

Fastify is 2-3x faster than Express in benchmarks for JSON serialization and routing. The difference comes from schema-based serialization (fast-json-stringify), a radix tree router (find-my-way), and less per-request overhead.

Does this matter for a typical SaaS? Honestly, for most applications your database queries take 10-100x longer than the framework overhead. You won't feel the difference serving 100 requests per second.

Where it matters: high-throughput APIs or thousands of concurrent requests. Lower P99 latency. But I didn't choose Fastify for benchmarks — I chose it for the developer experience.

TypeScript

This is the real reason I switched. Fastify was designed with TypeScript in mind. Route handlers have typed request and reply objects. Plugins chain types properly. Schema validation can infer TypeScript types.

Express's TypeScript support comes from community-maintained @types/express. req.body is any by default, middleware type chaining is awkward, and you'll write more type assertions.

Less casting, more compiler-caught errors. That's the difference.

Plugin System vs Middleware

Express uses a linear middleware chain. req, res, next. Simple, everyone knows it. But middleware execution order matters, there's no encapsulation, and async error handling requires wrappers.

Fastify's plugins are encapsulated — a plugin's decorators and hooks don't leak to sibling plugins. Scoped middleware, nested contexts, async initialization. The lifecycle has distinct hooks (onRequest, preHandler, preSerialization, onSend, onResponse) instead of one catch-all concept.

In a large SaaS backend, Fastify's encapsulation helps you organize code without unintended side effects between routes. Express's flat chain requires more discipline.

Validation

Fastify has built-in request validation. Define a schema, Fastify validates incoming requests before your handler runs:

app.post("/users", {
  schema: {
    body: {
      type: "object",
      required: ["email"],
      properties: {
        email: { type: "string", format: "email" }
      }
    }
  }
}, handler)

Express has no built-in validation. You add a library and wire it up. Flexible, but every project does it differently.

Logging

Fastify includes pino — a fast, structured JSON logger. Every request gets logged with timing information automatically. Configure it once, done.

Express has no built-in logging. Most projects cobble together morgan for request logging and winston or pino for application logging.

Structured logging is essential for debugging production SaaS. Fastify gives you this out of the box.

Async Error Handling

Express 5 finally handles async errors natively — rejected promises in route handlers get forwarded to the error handler automatically. For years with Express 4, you needed wrapper functions or express-async-errors to avoid unhandled promise rejections crashing your server.

Fastify has handled async/await natively from the start. Throw in an async handler, Fastify catches it, sends an error response, logs it.

Express 5 closes this gap, but if you're on Express 4 (still common in many codebases), the footgun remains.

Ecosystem

Express has a much larger ecosystem. More middleware, more tutorials, more Stack Overflow answers. Fastify's ecosystem is smaller but covers the common needs — CORS, static files, multipart uploads, rate limiting, JWT auth.

For anything else, you can wrap Express middleware to work with Fastify using @fastify/express. The gap narrows every year.

When to Choose Fastify

  • You want first-class TypeScript support
  • You value built-in validation, logging, and serialization
  • You want native async/await error handling
  • You prefer encapsulated plugins over flat middleware
  • You're starting a new project

When to Choose Express

  • Your team knows Express deeply and switching has a cost
  • You need a specific Express-only middleware
  • You're maintaining an existing Express application

What Stacknaut Uses

I chose Fastify for the backend and public API services.

If you've decided on Fastify, the next step is setting up the plugin architecture, wiring Zod validation into preHandler hooks, configuring pino with structured logging, organizing routes into encapsulated plugins, and building auth and billing middleware. Each of these is straightforward individually, but assembling them into a coherent production backend takes a day or more.

Stacknaut includes the complete Fastify setup — two separate services (internal backend + public API), validation patterns, structured logging, error handling, and the plugin architecture already organized for a SaaS application. Auth, Stripe webhooks, and transactional emails are wired up and working.

See what's included or check out the auth and billing documentation.

00b2d54d

© 2026 Stacknaut