Express.js vs Fastify in 2026: Which One Should You Actually Use?

Express is still everywhere. Fastify is faster. But is performance alone a good enough reason to switch?

Express.js vs Fastify Thumbnail

You are starting a new Node.js project.

You search for a framework and quickly end up with two names:

Express.js and Fastify.

Then you see the benchmarks.

Fastify is significantly faster in many synthetic benchmarks.

So the obvious thought is:

“If Fastify is faster, why would anyone still use Express?”

That sounds reasonable.

But choosing a backend framework is rarely that simple.

A framework is not just about requests per second. You also have to think about ecosystem, middleware, validation, TypeScript, migration cost, developer experience, existing code, and what your application actually needs.

And there is another interesting part of this comparison.

Express 5 has finally arrived.

After years of Express 4, version 5 brings several changes that developers maintaining older Express applications should know about.

So let’s break down the real difference between Express and Fastify — and more importantly, when I would choose each one.

Fastify Benchmarking From Fastify.dev

First: What exactly are Express and Fastify?

Both are web frameworks built for Node.js.

At a very basic level, they help you create APIs and web servers without having to deal directly with the lower-level HTTP APIs provided by Node.js.

For example, an Express application can look like this:

const express = require("express");

const app = express();
app.get("/users", (req, res) => {
res.json([
{ id: 1, name: "Neha" },
{ id: 2, name: "Rahul" }
]);
});
app.listen(3000);

Fastify has a very similar starting point:

const fastify = require("fastify")({
logger: true
});

fastify.get("/users", async (request, reply) => {
return [
{ id: 1, name: "Neha" },
{ id: 2, name: "Rahul" }
];
});
fastify.listen({ port: 3000 });

Notice something?

Neither one is particularly difficult.

The real difference starts appearing when the application becomes bigger.

Express vs Fastify: The Big Picture

Express vs Fastify

But let’s look at why these differences matter.

1. Performance: Fastify Has the Advantage

This is probably the biggest reason Fastify gets attention.

Fastify was designed with performance and low overhead as major goals.

Its architecture makes heavy use of schemas for validation and serialization, which can improve performance for API-heavy applications. Fastify’s documentation also recommends JSON Schema for validating routes and serializing responses.

But there is an important warning here.

You should not look at one benchmark and immediately conclude:

Fastify = 3x faster application.

Framework benchmarks usually measure framework overhead under controlled conditions.

Fastify itself points out that its benchmark is a synthetic “hello world” style comparison and recommends benchmarking your own application when performance matters.

Imagine your API does this:

Request

Authentication

Database query

Redis

External API

Business logic

Response

If your database query takes 80 ms, saving a small amount of framework overhead may not suddenly make the endpoint dramatically faster.

So ask yourself:

Is my application actually CPU/framework-overhead bound?

That is “Is the framework itself actually the bottleneck in my application?” If yes then using Fastify can make more sense.

If the answer is no, raw framework benchmarks may not be the most important factor.

2. Validation: This Is Where Fastify Gets Interesting

Suppose your API expects this:

{
"name": "Neha",
"age": 25
}

You probably want to reject something like:

{
"name": 123,
"age": "hello"
}

With Express, developers commonly add another library for request validation.

With Fastify, schema-based validation is a core part of the framework.

For example:

const schema = {
body: {
type: "object",
required: ["name", "age"],
properties: {
name: { type: "string" },
age: { type: "integer" }
}
}
};

fastify.post("/users", { schema }, async (request, reply) => {
return { message: "User created" };
});

Fastify can use JSON Schema for:

  • request body validation
  • query parameters
  • route parameters
  • headers
  • response serialization

Its validation system uses Ajv, and schemas can also help optimize response serialization.

Ajv (Another JSON Schema Validator) is a fast and standard-compliant JavaScript library used to validate data objects against declarative JSON Schema definitions in Node.js and browser environments.

This becomes especially useful when you’re building APIs with lots of structured input and output.

3. Express Has Something Fastify Can’t Easily Replace: Its Ecosystem

Express has been around since 2010.

That means there is an enormous amount of existing knowledge around it.

  • Need authentication? There are packages.
  • Need logging? There are packages.
  • Need CORS? There are packages.
  • Need validation? There are packages.
  • Need help debugging an obscure problem?

There is a very good chance someone has already faced it.

This matters more than it sounds.

Imagine joining a company where the backend has been running for six years.

You don’t get to choose the framework.

You inherit:

Express
├── 200+ routes
├── authentication middleware
├── custom middleware
├── logging
├── validation
├── monitoring
└── lots of business logic

Would you rewrite everything just because Fastify is faster?

Probably not.

Migration has a cost.

And engineering decisions should consider that cost.

4. Middleware vs Plugins

This is another architectural difference.

Express is heavily associated with middleware.

You might write:

app.use(authMiddleware);
app.use(loggingMiddleware);
app.use(express.json());

Then a request flows through these middleware functions.

Fastify has a plugin-oriented architecture with hooks and encapsulation.

Conceptually:

Request

Fastify

Hooks

Plugins

Route

Response

This can make large applications easier to organize when the application is designed around Fastify’s architecture.

But there is also a trade-off.

If you’ve spent years thinking and using Express middleware, Fastify’s plugin and hook model may initially feel different.

5. Error Handling

Express 4 applications commonly used explicit error forwarding for asynchronous operations.

For example:

app.get("/user/:id", async (req, res, next) => {
try {
const user = await getUserById(req.params.id);
res.json(user);
} catch (error) {
next(error);
}
});

Express 5 makes this simpler.

Rejected promises from route handlers and middleware are automatically forwarded to the error-handling middleware.

So you can write:

app.get("/user/:id", async (req, res) => {
const user = await getUserById(req.params.id);
res.json(user);
});

If getUserById() throws or rejects, Express 5 can forward that error automatically.

That is a small change in code.

But across a large application, small improvements like this can make development much cleaner.

Express 5: What Actually Changed?

Express 5 was released in October 2024, so by 2026 it isn’t really a “new” framework anymore. But many developers still maintain Express 4 applications, which makes the migration changes important.

And there are some breaking changes worth knowing.

1. Optional Route Parameters Changed

Older Express code could look like:

app.get("/:file.:ext?", handler);

Express 5 changes this syntax to:

app.get("/:file{.:ext}", handler);

The ? syntax for optional parameters is no longer supported in the old form.

Why make such a change?

The newer path-matching syntax makes optional sections more explicit.

This is one of those changes that can look tiny until you upgrade an application with hundreds of routes.

2. Wildcard Routes Changed

You might have previously written:

app.get("/*", handler);

In Express 5, wildcards need a name:

app.get("/*splat", handler);

If you also want to match the root / path, you can use:

app.get("/{*splat}", handler);

This is another migration detail that can break existing routing code.

3. Regular Expression Route Patterns Changed

Express 5 no longer supports some of the old string patterns that relied on regular-expression characters.

For example, instead of trying to put alternatives directly inside the route pattern, you may need to provide multiple paths:

app.get(
["/discussion/:slug", "/page/:slug"],
handler
);

The goal is to make route matching more explicit and predictable.

4. req.body Can Now Be undefined

This one is easy to miss.

In Express 4, developers could often assume:

req.body

was initialized to an empty object.

In Express 5, if the body wasn’t parsed, req.body can be undefined.

So code like:

if (!req.body) {
return res.status(400).send("Request body required");
}

can become important depending on your endpoint.

This is a small change, but small changes are exactly what can cause unexpected bugs during migration.

5. express.urlencoded() Changed

The default value of extended is now false.

So instead of relying on a default:

app.use(express.urlencoded());

you can explicitly configure it when your application needs the previous behavior:

app.use(
express.urlencoded({
extended: true
})
);

This is another migration detail worth checking in older applications.

6. Body Parser Changes

Express 5 also cleaned up some older body-parser behavior.

You can use:

app.use(express.json());
app.use(
express.urlencoded({
extended: false
})
);

instead of relying on older combinations of body-parser middleware.

Express 5 also added Brotli decompression support for incoming request bodies and introduced a configurable URL-encoded body depth.

7. Some Old Response APIs Were Removed

Suppose an old Express application contains:

res.send({
message: "Success"
}, 200);

Express 5 expects:

res
.status(200)
.send({
message: "Success"
});

Similarly, the old:

res.redirect("back");

is no longer supported.

The migration guide recommends explicitly reading the referrer and providing a fallback.

These aren’t difficult changes.

But imagine finding thousands of these calls in an old codebase.

That’s where migration becomes an engineering project rather than a simple package upgrade.

So… Should You Choose Express or Fastify?

Now we can finally answer the question.

I wouldn’t choose based only on:

“Which one is faster?”

I’d start with the application.

Choose Express when:

  • You’re learning Node.js backend development.
  • Your team already knows Express.
  • You’re working on an existing Express application.
  • You depend heavily on Express middleware.
  • You want the largest possible ecosystem.
  • Performance isn’t your primary bottleneck.
  • You want a simple and flexible framework.

Consider Fastify when:

  • You’re building a new API-heavy application.
  • Throughput and framework overhead matter.
  • You want schema-driven validation.
  • You want schema-based serialization.
  • You’re building microservices.
  • You like a plugin-oriented architecture.
  • You are comfortable adopting a newer ecosystem.

And there is nothing wrong with choosing Express for a large application.

“Large application” does not automatically mean “use Fastify.”

The architecture around the framework often matters much more.

A Simple Decision Flow

Here’s how I would think about the decision:

Start


Is this an existing app?
/ \
Yes No


Already using Need very high
Express? throughput?
/ \ / \
Yes No Yes No


Keep Evaluate Fastify Evaluate
Express migration both

And there is one question I would ask before making the final decision:

What problem are you actually trying to solve?

If your current Express application is slow, don’t immediately blame Express.

Profile the application first.

Maybe the actual problem is:

Slow API

Database query

Missing index

or:

Slow API

External API

3-second response time

or:

Slow API

Expensive business logic

CPU bottleneck

Switching frameworks won’t magically fix these problems.

My Take

If I were starting a small Node.js project today, I wouldn’t reject Express just because Fastify has better benchmark numbers.

Express has an enormous ecosystem, a simple programming model, and decades of accumulated knowledge around it.

At the same time, I wouldn’t ignore Fastify either.

If I were building a new service where throughput, schema validation, serialization, and low framework overhead were important, Fastify would be very high on my list.

And if I inherited an Express 4 application?

I would not rewrite it just because Fastify is faster.

I’d first understand the application, measure its actual bottlenecks, check Express 5 compatibility, run the migration tests, and then decide whether a framework migration provides enough business value to justify the cost.

That is probably the bigger lesson here.

The fastest framework is not necessarily the best framework for your application.

The best framework is the one that solves your actual problem with the least unnecessary complexity.

So I’m curious:

If you were starting a new Node.js backend today, would you pick Express or Fastify — and what would be the deciding factor for you: performance, ecosystem, simplicity, or something else?

From Tech By Neha Gupta

  • 👏 Enjoyed the article? Don’t forget to leave a clap.
  • 💬 Have thoughts or questions? Share them in the comments.

Before you go

  • Please take a moment to like the post and follow the writer!
  • Did you know that over 400,000 developers share what they’re building, learning, and discovering across our platforms every month? Learn how you can contribute here