Fastify vs Express in 2026 Updates: Complete Comparison with Benchmarks

Written by: Bagus Facsi Aginsa
Published at: 10 Jan 2022


Choosing between Fastify and Express in 2026 is one of the most common decisions Node.js developers face. Express has dominated backend JavaScript for over a decade. Fastify has grown into a serious alternative, backed by real performance data and a maturing ecosystem. This article covers everything you need to make an informed decision: real-world benchmarks with database queries, TypeScript support, plugin architectures, HTTP/2, built-in validation, and a plain decision matrix.

Quick Verdict:

  • Use Fastify if you are starting a new project, performance is a constraint, you want first-class TypeScript support, or you want built-in request validation and structured logging out of the box.
  • Use Express if you are maintaining an existing codebase, need maximum compatibility with the npm middleware ecosystem, or your team is already deep in Express.

Quick Comparison Table

Feature Express Fastify
Performance ⚠️ Good ✅ Excellent
TypeScript support ⚠️ Via @types/express ✅ First-class, built-in
HTTP/2 ❌ External library ✅ Native support
WebSocket ❌ External library @fastify/websocket plugin
Built-in validation ❌ Manual / external ✅ JSON Schema, compiled
Built-in logging ❌ No ✅ Pino (async)
Plugin encapsulation ❌ Flat middleware ✅ Scoped plugin system
Learning curve ✅ Very low ✅ Low
Ecosystem maturity ✅ Massive ⚠️ Growing
Active maintenance ✅ Express 5 released ✅ Active

Overview of Each Framework

Express

Express was first released in 2010 and quickly became the dominant Node.js web framework. It is minimal by design — it provides routing and middleware support and leaves everything else to the developer. Express 5 was released in 2024 after a long alpha, bringing native async/await error handling, improved path pattern matching, and dropped legacy compatibility. The framework has [VERIFY: current weekly downloads from npmjs.com] weekly downloads on npm and remains the most installed Node.js framework.

Express is a good fit when:

  • You need access to the widest selection of npm middleware packages.
  • You are maintaining an existing Express codebase.
  • Your team is already productive with it.

Fastify

Fastify was first released in 2016 with an explicit focus on performance and a structured plugin architecture. It is maintained by the same team behind Pino (the logging library) and Avvio (the async boot system). As of 2026, Fastify has [VERIFY: current weekly downloads from npmjs.com] weekly downloads on npm and is used in production at large organizations. Fastify v5 is the current major version.

Fastify is a good fit when:

  • You are starting a new project and care about performance.
  • You want TypeScript support without extra setup.
  • You want request validation without bolting on additional libraries.
  • You need structured logging built in from day one.

Performance Benchmark

The official Fastify benchmarks use a “hello world” test, which shows a large gap but does not reflect real applications. We ran a more realistic test that includes a database query on every request.

Test Setup

  1. CPU: Intel Core Ultra 7 155U, 12 cores, 14 threads
  2. RAM: 16 GB
  3. OS: Ubuntu 24.04 WSL
  4. Node Version: 22.20.0
  5. Express Version: 5.2.1
  6. Fastify Version: 5.8.5
  7. mysql2: 3.22.3
  8. fastify-mysql: 2.1.0
  9. ApacheBench Version: 2.3

The load testing tool is ab (ApacheBench), sending 10,000 GET requests at 100 concurrency to a Node.js server that queries 20 rows from a MySQL database:

ab -n 10000 -c 100 http://localhost:3000/products

Test topology:

ab ------> NodeJS ------> MySQL

Express Code

const express = require("express");
const mysql = require("mysql2/promise");
const app = express();

const pool = mysql.createPool({
  connectionLimit: 100,
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
});

app.get("/products", async (req, res) => {
  const [results] = await pool.query("SELECT * FROM product");
  res.send({ results });
});

app.listen(3000);

Fastify Code

const fastify = require("fastify");
const app = fastify();

app.register(require("fastify-mysql"), {
  connectionLimit: 100,
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASS,
  database: process.env.DB_NAME,
  promise: true,
});

app.get("/products", async (req, res) => {
  const [results] = await app.mysql.query("SELECT * FROM product");
  res.send({ results });
});

app.listen(3000);

The key difference is how MySQL is connected. Fastify uses the fastify-mysql plugin, which registers the connection pool on the app object and integrates with Fastify’s lifecycle. Both implementations use mysql2 under the hood. Each test was run 10 times to reduce error variance.

2022 Baseline: Express 4.17.2 vs Fastify 3.25.3

Run on Node.js 14.17.2 for historical comparison.

Fastify vs Express 2022 Benchmark: Requests Per Second

Fastify 3 averaged 1,598 req/s while Express 4 averaged 1,167 req/s — a 37% advantage.

2026 Results: Express 5.2.1 vs Fastify 5.8.5

Run on Node.js 22.20.0 with identical test parameters.

Fastify vs Express 2026 Benchmark: Requests Per Second

The gap widened because Fastify’s compiled JSON serialization and schema-based validation scale better with Node.js 22’s V8 improvements than Express’s interpreted middleware chain.

TypeScript Support

Express

Express was written in JavaScript. TypeScript definitions are provided by the @types/express community package. The types are functional but you frequently need to cast or use any for custom properties on req:

import express, { Request, Response } from "express";

interface ProductQuery {
  limit: string;
}

const app = express();

app.get("/products", (req: Request<{}, {}, {}, ProductQuery>, res: Response) => {
  const limit = parseInt(req.query.limit);
  res.json({ limit });
});

Adding custom properties to req requires module augmentation, which works but adds boilerplate.

Fastify

Fastify has first-class TypeScript support built in. Request and reply types are generics, so you get full type safety without workarounds:

import Fastify from "fastify";

interface ProductQuerystring {
  limit: number;
}

const app = Fastify();

app.get<{ Querystring: ProductQuerystring }>("/products", async (req, reply) => {
  const limit = req.query.limit; // typed as number
  reply.send({ limit });
});

Fastify’s generic system integrates with its JSON Schema validation, so validated input types flow through to your handler automatically.

HTTP/2 and WebSocket Support

Express

Express does not support HTTP/2 natively. You need the spdy package or Node’s built-in http2 module:

const spdy = require("spdy");
const express = require("express");
const app = express();

spdy.createServer({ /* TLS options */ }, app).listen(3000);

For WebSockets, ws or socket.io are common choices, but they operate outside the Express routing system and do not benefit from Express middleware applied to HTTP routes.

Fastify

Fastify supports HTTP/2 natively with a single option:

const fs = require("fs");
const fastify = require("fastify")({
  http2: true,
  https: {
    key: fs.readFileSync("./key.pem"),
    cert: fs.readFileSync("./cert.pem"),
  },
});

WebSocket support is provided by @fastify/websocket, which integrates directly with Fastify’s routing so hooks and decorators apply to WebSocket routes too:

const fastify = require("fastify")();

fastify.register(require("@fastify/websocket"));

fastify.get("/ws", { websocket: true }, (socket, req) => {
  socket.on("message", (msg) => {
    socket.send("reply: " + msg);
  });
});

Schema-Based Validation

Express

Express has no built-in validation. Common choices are express-validator, joi, or zod:

const { body, validationResult } = require("express-validator");

app.post(
  "/products",
  body("name").isString().notEmpty(),
  body("price").isFloat({ min: 0 }),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // handle valid request
  }
);

This works, but validation runs at the JavaScript layer on every request and adds dependencies to manage.

Fastify

Fastify uses JSON Schema for validation. The schema is compiled to a fast validator function at startup time, not on each request. The schema also drives serialization, so JSON responses are faster too:

const productSchema = {
  body: {
    type: "object",
    required: ["name", "price"],
    properties: {
      name: { type: "string" },
      price: { type: "number", minimum: 0 },
    },
  },
};

fastify.post("/products", { schema: productSchema }, async (req, reply) => {
  // req.body is already validated — invalid requests never reach here
  reply.send({ created: true });
});

Invalid requests are rejected before your handler runs, with a descriptive error message generated automatically.

Plugin Ecosystem

Express Middleware

Express uses a flat, sequential middleware model. Any npm package with the (req, res, next) signature works as middleware:

const cors = require("cors");
const helmet = require("helmet");
const rateLimit = require("express-rate-limit");

app.use(cors());
app.use(helmet());
app.use(rateLimit({ windowMs: 60000, max: 100 }));

The ecosystem is enormous — nearly every popular tool has an Express integration. The trade-off is that middleware is global, and scoping it to specific routes requires extra care.

Fastify Plugin Architecture

Fastify uses an encapsulated plugin system. Plugins registered inside a fastify.register() block are isolated from the rest of the application, which makes it easier to build modular services:

fastify.register(async function adminRoutes(app) {
  app.register(require("@fastify/jwt"), { secret: process.env.JWT_SECRET });

  app.addHook("onRequest", async (req) => {
    await req.jwtVerify();
  });

  app.get("/admin/users", async (req, reply) => {
    reply.send({ users: [] });
  });
});
// The JWT plugin and auth hook only apply inside this block

Common Fastify plugins for everyday tasks:

Task Plugin
CORS @fastify/cors
JWT auth @fastify/jwt
Rate limiting @fastify/rate-limit
Static files @fastify/static
Swagger / OpenAPI docs @fastify/swagger
PostgreSQL @fastify/postgres
Redis @fastify/redis
Multipart uploads @fastify/multipart

For a deeper look at Fastify plugins in practice, see 3 Highly Recommended Fastify Plugins You Must Use.

Migration Guide: Express → Fastify

When Migration Makes Sense

Migration is worth the effort when:

  • Response latency is a measurable problem in production.
  • You are adding TypeScript to the project and want end-to-end type safety.
  • You want to consolidate validation and structured logging under a single framework rather than managing five separate libraries.

Incremental Migration with @fastify/express

The @fastify/express plugin lets you mount existing Express middleware inside a Fastify application, so you can migrate route by route:

const fastify = require("fastify")();
const expressPlugin = require("@fastify/express");
const legacyApp = require("./legacy-express-app");

fastify.register(expressPlugin).then(() => {
  fastify.use(legacyApp); // existing Express app continues to work
  fastify.get("/new-route", async (req, reply) => {
    reply.send({ migrated: true });
  });
});

Common Gotchas

  • req and reply differences: Fastify uses reply instead of res. Methods like res.json() do not exist — use reply.send() instead.
  • Middleware vs hooks: Express app.use() middleware becomes Fastify hooks (addHook). Fastify hooks are more granular: onRequest, preValidation, preHandler, onSend, and more.
  • Async error handling: In Express 4, errors inside async handlers require an explicit next(err) call. In Fastify, any thrown error in an async handler is automatically sent as an error response.

Equivalent Endpoints Side by Side

Express:

app.post("/products", async (req, res) => {
  try {
    const product = await db.create(req.body);
    res.status(201).json(product);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

Fastify:

fastify.post("/products", async (req, reply) => {
  const product = await db.create(req.body); // thrown errors handled automatically
  reply.code(201).send(product);
});

Decision Matrix: Which Should You Choose?

Choose Express if:

  • You are maintaining an existing Express application.
  • Your team already knows Express well and learning Fastify is not worth it right now.
  • You rely on Express-specific middleware that has no Fastify equivalent.
  • You need the widest possible third-party integration support.
  • Stability and familiarity matter more than performance for your use case.

Choose Fastify if:

  • You are starting a new project and performance is a concern.
  • You want first-class TypeScript support with minimal setup.
  • You want built-in request validation tied to route schemas.
  • You want structured, async logging (Pino) configured from the start.
  • You are building a microservice or API where response latency directly affects cost.

FAQ

Is Fastify faster than Express?

Yes. In our real-world benchmark with MySQL queries, Fastify was 27% faster than Express. On simpler routes without database access the gap is wider. The performance difference comes from Fastify’s compiled JSON serialization, schema-based validation, and Pino’s async logging rather than interpreted code running on each request.

Can I use Express middleware in Fastify?

Yes, using the @fastify/express compatibility plugin. Install it with npm install @fastify/express and use fastify.use() to mount Express middleware. This is useful for incremental migration, though purpose-built Fastify plugins will generally perform better.

Is Express still maintained in 2026?

Yes. Express 5 was released in 2024 after an extended alpha period. The framework is actively maintained, though the pace of new features is slower than Fastify. Its install base is enormous and the ecosystem continues to grow.

Should I migrate from Express to Fastify?

It depends. If your Express app is working well and performance is not a measurable bottleneck, migration is probably not worth the risk and effort. If you are seeing latency problems, adding TypeScript, or building a new service alongside your existing one, Fastify is worth evaluating seriously.

Which has better TypeScript support?

Fastify. It uses generics-based typing that flows from route schemas through to request handlers. Express TypeScript support via @types/express works but requires more manual type annotations and module augmentation for custom request properties.

Which is easier to learn?

Express has a lower initial learning curve because its mental model is simpler — middleware is just a function. Fastify adds concepts like plugin encapsulation and lifecycle hooks, but most Express developers are productive with Fastify within a few hours and appreciate the structure as applications grow.

Does Fastify support WebSockets?

Yes. The @fastify/websocket plugin integrates WebSocket connections directly with Fastify’s routing system. You can apply hooks and decorators to WebSocket routes just like HTTP routes.

Is Fastify production-ready?

Yes. Fastify has been production-ready since v1.0 and is used in production at large organizations. It has a stable API, semantic versioning, and an active core team. Fastify v5 is the current major release as of 2026.

Conclusion

Express and Fastify are both mature, production-ready Node.js frameworks. Express has the larger ecosystem and remains the safest choice for teams with existing codebases and established Express workflows. Fastify has a measurable performance advantage — 27% faster in real-world database-backed tests — along with better native TypeScript support and a more structured plugin architecture that pays off as applications grow.

If you are starting something new in 2026, give Fastify serious consideration. The performance gain is real, the TypeScript experience is noticeably better, and built-in validation removes an entire category of runtime errors. If you are not ready to commit fully, use @fastify/express to migrate one route at a time.