Fastify vs Express — Which is Faster?

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

Fastify is the new hotness in Node.js frameworks, promising 3x better throughput than Express. But is it worth switching? Let's compare both side by side.

Performance Comparison

Fastify is genuinely faster:

  • Requests/sec: Fastify ~30,000 vs Express ~10,000
  • Latency: Fastify ~1ms avg vs Express ~3ms avg
  • Memory: Similar consumption
// Express hello world
import express from "express";
const app = express();
app.get("/", (req, res) => {
  res.json({ hello: "world" });
});
app.listen(3000);

// Fastify hello world
import Fastify from "fastify";
const fastify = Fastify();
fastify.get("/", async (request, reply) => {
  return { hello: "world" };
});
fastify.listen({ port: 3000 });

Feature Comparison

FeatureExpressFastify
Built-in routingBasicRadix tree (faster)
Schema validationVia middlewareBuilt-in JSON Schema
Type supportPartialExcellent
Plugin systemMiddlewareEncapsulation-aware
Learning curveEasyModerate
EcosystemHugeGrowing
PerformanceGoodExcellent

Fastify Setup

import Fastify from "fastify";
import cors from "@fastify/cors";
import jwt from "@fastify/jwt";

const fastify = Fastify({ logger: true });

// Plugins
await fastify.register(cors);
await fastify.register(jwt, { secret: "secret" });

// Routes with schema validation
fastify.post<{ Body: { name: string; email: string } }>(
  "/users",
  {
    schema: {
      body: {
        type: "object",
        required: ["name", "email"],
        properties: {
          name: { type: "string" },
          email: { type: "string", format: "email" },
        },
      },
      response: {
        201: {
          type: "object",
          properties: {
            id: { type: "number" },
            name: { type: "string" },
            email: { type: "string" },
          },
        },
      },
    },
  },
  async (request, reply) => {
    const { name, email } = request.body;
    reply.code(201);
    return { id: 1, name, email };
  }
);

await fastify.listen({ port: 3000 });

Plugins System

// Custom Fastify plugin
import { FastifyPlugin } from "fastify";

const myPlugin: FastifyPlugin = async (fastify) => {
  fastify.decorate("utils", {
    hello: () => "Hello from plugin",
  });

  fastify.get("/hello", async () => {
    return { message: fastify.utils.hello() };
  });
};

fastify.register(myPlugin);

// Express middleware (for comparison)
app.use((req, res, next) => {
  req.app.locals.utils = {
    hello: () => "Hello from middleware",
  };
  next();
});

Error Handling in Fastify

// Fastify error handler
fastify.setErrorHandler((error, request, reply) => {
  fastify.log.error(error);
  reply.code(error.statusCode || 500);
  return { error: error.message };
});

// Custom error class
class ApiError extends Error {
  constructor(
    public statusCode: number,
    message: string
  ) {
    super(message);
  }
}

fastify.get("/users/:id", async (request, reply) => {
  const { id } = request.params as { id: string };
  const user = await getUser(parseInt(id));
  if (!user) {
    throw new ApiError(404, "User not found");
  }
  return user;
});

Hooks System

// Request lifecycle hooks
fastify.addHook("onRequest", async (request, reply) => {
  console.log("Request:", request.method, request.url);
});

fastify.addHook("preHandler", async (request, reply) => {
  // Authentication
  if (!request.headers.authorization) {
    throw new Error("Unauthorized");
  }
});

fastify.addHook("onSend", async (request, reply, payload) => {
  // Modify response
  return payload;
});

fastify.addHook("onResponse", async (request, reply) => {
  console.log("Response:", reply.statusCode);
});

When to Use Fastify

Use Fastify if:

  • High throughput is critical
  • You want built-in schema validation
  • Building microservices
  • Starting a new project

Use Express if:

  • Huge ecosystem matters
  • Team expertise exists
  • Maximum community support needed
  • Migration cost too high

When to Use Express

Use Express if:

  • Team knows it well
  • Large ecosystem of middleware
  • Existing codebase
  • Rapid prototyping

Use Express if:

  • Performance is critical
  • You need modern design patterns

Real-World Benchmark

// Load test with k6
import http from "k6/http";
import { check } from "k6";

export const options = {
  vus: 100, // 100 virtual users
  duration: "30s",
};

export default function () {
  const res = http.get("http://localhost:3000/");
  check(res, {
    "status is 200": (r) => r.status === 200,
    "response time < 100ms": (r) => r.timings.duration < 100,
  });
}

Results on modern hardware:

  • Express: ~10,000 req/s
  • Fastify: ~30,000 req/s

Migration Path

// Express code
app.post("/users", express.json(), (req, res) => {
  res.json({ id: 1, ...req.body });
});

// Equivalent Fastify code
fastify.post("/users", async (request, reply) => {
  return { id: 1, ...request.body };
});

FAQ

Q: Should I migrate my Express app to Fastify? A: Only if performance is bottlenecking. For most apps, Express is fine. If you're starting new, consider Fastify.

Q: Is Fastify mature for production? A: Yes, absolutely. Used by major companies at scale.

Q: Can I use Express middleware in Fastify? A: Partially, via @fastify/express. But native Fastify plugins are better.


Fastify is genuinely faster than Express. The question isn't speed but whether that speed matters for your use case. For new high-performance APIs, Fastify is the right choice. For existing Express apps, the migration cost rarely justifies the gains.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro