DEV Community

Cover image for Express vs Fastify — A Practical Performance Comparison for Node.js Developers
Akshay Kurve
Akshay Kurve

Posted on

Express vs Fastify — A Practical Performance Comparison for Node.js Developers

If you've built backend APIs in Node.js, there's a good chance you've used Express.

For years, Express has been the default choice for building web servers in Node.js. But recently, another framework has been getting a lot of attention: Fastify.

Developers often say things like:

"Fastify is way faster than Express."

But what does that actually mean?

And more importantly — should you switch to Fastify?

In this article, we'll break it down in simple terms.

We'll compare:

  • Performance characteristics
  • Developer experience
  • Architecture and design philosophy
  • Real-world benchmarks
  • When you should choose each framework

Let's start with a quick introduction.


What is Express?

Express.js is a minimal and flexible web application framework for Node.js.

Released in 2010, it quickly became the most popular backend framework in the JavaScript ecosystem.

It focuses on simplicity and flexibility.

A basic Express server looks like this:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
Enter fullscreen mode Exit fullscreen mode

That's it.

No complicated setup. Just routes and middleware.

This simplicity is why millions of developers still use Express today.


What is Fastify?

Fastify is a modern web framework for Node.js that focuses heavily on performance and developer experience.

Created in 2016, it was built with one main goal:

Build the fastest possible Node.js web framework without sacrificing developer experience.

A basic Fastify server looks very similar:

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

fastify.get('/', async (request, reply) => {
  return { message: 'Hello World' };
});

fastify.listen({ port: 3000 }, (err) => {
  if (err) throw err;
});
Enter fullscreen mode Exit fullscreen mode

At first glance, it looks almost identical to Express.

But internally, Fastify is architected very differently.


Why Fastify Is Faster

Fastify's performance advantage comes from several deliberate architectural choices.

Let's examine the main ones.


1. Schema-Based Serialization

Fastify uses JSON Schema to optimize response serialization.

Example:

fastify.get('/user', {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          age: { type: 'number' }
        }
      }
    }
  }
}, async (request, reply) => {
  return { name: 'Alex', age: 25 };
});
Enter fullscreen mode Exit fullscreen mode

Why does this matter?

Because Fastify pre-compiles the serialization function at startup using fast-json-stringify, which can be up to 2-3x faster than JSON.stringify().

Express does not do this — it uses native JSON.stringify() every time.


2. Optimized Routing Engine

Fastify uses a highly optimized router called find-my-way.

This router uses a radix tree data structure for route matching, which provides:

  • O(log n) lookup time
  • Efficient parameterized route handling
  • Better performance with many routes

Express uses a simpler linear matching approach that becomes slower as route count increases.

For applications with dozens or hundreds of routes, this difference becomes measurable.


3. Built-In Request Validation

Fastify includes request validation using JSON Schema out of the box.

Example:

fastify.post('/user', {
  schema: {
    body: {
      type: 'object',
      required: ['name', 'email'],
      properties: {
        name: { type: 'string', minLength: 1 },
        email: { type: 'string', format: 'email' }
      }
    }
  }
}, async (request, reply) => {
  // Body is already validated at this point
  return { success: true, user: request.body };
});
Enter fullscreen mode Exit fullscreen mode

Express typically relies on external libraries like:

  • express-validator
  • Joi
  • Yup

While these libraries are excellent, they add:

  • Extra dependencies
  • Additional middleware overhead
  • More configuration complexity

Fastify's built-in validation using Ajv (the fastest JSON validator) is both performant and integrated.


4. Asynchronous Design from the Ground Up

Fastify was designed for modern async/await patterns.

Express was created in 2010, before Promises were standard in Node.js, so async handling was retrofitted later.

Fastify handles async route handlers natively:

// Fastify - native async support
fastify.get('/data', async (request, reply) => {
  const data = await fetchData();
  return data; // automatically serialized
});
Enter fullscreen mode Exit fullscreen mode
// Express - requires manual error handling
app.get('/data', async (req, res, next) => {
  try {
    const data = await fetchData();
    res.json(data);
  } catch (err) {
    next(err); // must explicitly pass to error handler
  }
});
Enter fullscreen mode Exit fullscreen mode

Performance Benchmarks

Benchmarks vary depending on test conditions, but most show a consistent pattern.

Example benchmark results from the official Fastify benchmarks:

Framework Requests/sec Latency (avg) Throughput
Fastify ~76,000 6.53 ms 13.54 MB/sec
Express ~38,000 13.14 ms 6.78 MB/sec

Note: Benchmarks run on simple "Hello World" applications

Fastify typically handles 2x more requests per second than Express with half the latency.

Important caveat:

These benchmarks test very simple APIs with minimal logic.

Real-world applications include:

  • Database queries (Postgres, MongoDB)
  • Authentication/authorization
  • External API calls
  • Caching layers (Redis)
  • Business logic
  • Data transformations

In production, these operations often become the actual bottleneck, not the framework itself.

However, when serving high-throughput APIs or microservices, framework overhead matters significantly.


Memory Usage

Fastify is also more memory-efficient.

In typical benchmarks, Fastify uses approximately 15-20% less memory than Express under load.

This matters when:

  • Running containerized microservices (lower resource costs)
  • Deploying serverless functions (faster cold starts)
  • Handling high concurrent connections
  • Operating under tight resource constraints

Express consumes slightly more memory due to:

  • Less optimized middleware stack
  • No schema compilation (more runtime processing)
  • Heavier routing structures

Developer Experience

Performance isn't everything.

Developer experience is crucial for long-term productivity.

Let's compare.


Express Developer Experience

Pros:

  • Extremely simple and approachable
  • Massive ecosystem (thousands of middleware packages)
  • Extensive documentation and tutorials
  • Huge community (Stack Overflow, forums)
  • Very beginner-friendly
  • Flexible and unopinionated

Example middleware:

app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

app.use(express.json()); // body parsing
app.use(cors()); // CORS handling
Enter fullscreen mode Exit fullscreen mode

Middleware in Express is extremely intuitive.

Cons:

  • No enforced structure (can lead to messy codebases)
  • Manual error handling for async routes
  • No built-in validation
  • Performance limitations at scale
  • Easy to create inefficient architectures

Fastify Developer Experience

Pros:

  • Structured architecture (encourages best practices)
  • Built-in validation and serialization
  • Excellent plugin system with encapsulation
  • Great TypeScript support
  • Comprehensive built-in logging
  • Modern async-first design

Cons:

  • Slightly steeper learning curve
  • Smaller ecosystem compared to Express
  • Fewer community tutorials and examples
  • Plugin encapsulation can be confusing initially

Fastify encourages more structured, maintainable backend design through its plugin architecture.


Plugin System

Fastify's plugin system is one of its strongest features.

Example:

// plugins/database.js
async function databasePlugin(fastify, options) {
  const db = await connectToDatabase(options.uri);

  fastify.decorate('db', db);

  fastify.addHook('onClose', async () => {
    await db.close();
  });
}

module.exports = databasePlugin;

// app.js
fastify.register(require('./plugins/database'), {
  uri: 'mongodb://localhost:27017/mydb'
});
Enter fullscreen mode Exit fullscreen mode

Key benefits:

  1. Encapsulation - Plugins are scoped to prevent namespace pollution
  2. Lifecycle hooks - Clean startup/shutdown handling
  3. Dependency management - Plugins can depend on other plugins
  4. Reusability - Easy to share across projects

Express middleware is global by default, which can lead to:

  • Namespace conflicts
  • Harder-to-trace bugs
  • Less modular architecture

Error Handling

Both frameworks provide straightforward error handling mechanisms.

Express:

// Error handling middleware (must have 4 parameters)
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(err.status || 500).json({
    error: err.message
  });
});
Enter fullscreen mode Exit fullscreen mode

Fastify:

// Global error handler
fastify.setErrorHandler((error, request, reply) => {
  fastify.log.error(error);
  reply.status(error.statusCode || 500).send({
    error: error.message
  });
});

// Custom error responses
fastify.setErrorHandler((error, request, reply) => {
  if (error.validation) {
    reply.status(400).send({
      error: 'Validation Error',
      details: error.validation
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Fastify's error handling integrates better with:

  • Async/await patterns
  • Validation errors
  • Schema serialization

Ecosystem Comparison

Feature Express Fastify
Release year 2010 2016
GitHub stars ~64k ~31k
Weekly npm downloads ~24M ~1.3M
Community size Very large Growing
Performance Good Excellent
Plugin/middleware Massive Growing
Learning curve Very easy Moderate
TypeScript support Via @types First-class
Built-in features Minimal Comprehensive
Corporate backing StrongLoop/IBM OpenJS

When You Should Use Express

Express remains an excellent choice for many scenarios.

Use Express when:

  • You're learning backend development (simpler mental model)
  • You need maximum flexibility and don't want opinions
  • You're building small to medium applications
  • You rely on specific Express-only middleware
  • You want maximum community support and resources
  • You're working with a team already experienced in Express
  • Performance is not a critical concern

Real-world Express use cases:

  • Prototypes and MVPs
  • Internal tools
  • Server-side rendered applications (with template engines)
  • APIs with moderate traffic
  • Learning projects

Many successful production systems run on Express, including applications serving millions of users.


When You Should Use Fastify

Fastify excels in performance-critical and modern architectures.

Use Fastify when:

  • You're building high-performance APIs
  • You're developing microservices architectures
  • You need to handle high request volumes
  • You want built-in validation and serialization
  • You're using TypeScript extensively
  • You value structured, maintainable codebases
  • Performance and resource efficiency matter

Real-world Fastify use cases:

  • REST APIs serving thousands of requests per second
  • GraphQL servers (with mercurius)
  • Microservices in containerized environments
  • Real-time applications
  • Serverless functions (faster cold starts)
  • Resource-constrained environments

Migration Considerations

If you're considering migrating from Express to Fastify:

Challenges:

  • Different middleware patterns (not directly compatible)
  • Route handler signature differences
  • Learning the plugin system
  • Rewriting validation logic
  • Testing and QA overhead

Benefits:

  • Better long-term performance
  • More maintainable architecture
  • Built-in features reduce dependencies
  • Better TypeScript experience
  • Lower resource costs at scale

Recommendation:

Don't migrate existing Express apps unless:

  1. Performance is a proven bottleneck
  2. You're doing a major refactor anyway
  3. You have comprehensive test coverage
  4. The business value justifies the effort

For new projects, Fastify is worth serious consideration.


Real-World Perspective

A common mistake developers make is focusing only on benchmarks.

In reality, most application time is spent in:

  • Database queries (often 70-90% of response time)
  • External API calls (third-party services, microservices)
  • Business logic (data processing, calculations)
  • Authentication/authorization (JWT verification, session lookups)

Example response time breakdown:

Total: 120ms
├─ Framework overhead: 2ms   (Express: ~4ms, Fastify: ~2ms)
├─ Database query: 80ms
├─ External API call: 30ms
└─ Business logic: 8ms
Enter fullscreen mode Exit fullscreen mode

In this realistic scenario, switching from Express (4ms) to Fastify (2ms) saves 2ms out of 120ms — a 1.6% improvement.

However, Fastify can still provide value through:

  • Better resource utilization (handling more concurrent requests)
  • Lower memory footprint (reduced infrastructure costs)
  • Built-in features (faster development)
  • Better structure (long-term maintainability)

For high-scale APIs serving thousands of requests per second, that 2ms per request translates to:

  • Significant resource savings
  • Better user experience
  • Lower cloud costs

Performance Optimization Tips

Regardless of which framework you choose, follow these best practices:

1. Use connection pooling for databases:

// Don't create new connections per request
const pool = new Pool({ max: 20 });
Enter fullscreen mode Exit fullscreen mode

2. Implement caching strategies:

// Cache frequently accessed data
const cache = new NodeCache({ stdTTL: 600 });
Enter fullscreen mode Exit fullscreen mode

3. Use compression middleware:

// Express
app.use(compression());

// Fastify
fastify.register(require('@fastify/compress'));
Enter fullscreen mode Exit fullscreen mode

4. Profile and monitor in production:

  • Use APM tools (New Relic, Datadog)
  • Monitor response times
  • Identify actual bottlenecks
  • Optimize based on data, not assumptions

TypeScript Support

Both frameworks support TypeScript, but with different experiences.

Express with TypeScript:

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

const app = express();

app.get('/user/:id', (req: Request, res: Response) => {
  const id = req.params.id; // type: string
  res.json({ id });
});
Enter fullscreen mode Exit fullscreen mode

Type support is available via @types/express, but:

  • Request/response typing can be verbose
  • Schema validation requires separate setup
  • Type inference is limited

Fastify with TypeScript:

import Fastify from 'fastify';

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

interface UserParams {
  id: string;
}

interface UserBody {
  name: string;
}

fastify.get<{ Params: UserParams }>('/user/:id', async (request, reply) => {
  const { id } = request.params; // fully typed
  return { id };
});
Enter fullscreen mode Exit fullscreen mode

Fastify's TypeScript support is:

  • Built-in (no separate @types packages needed)
  • More comprehensive type inference
  • Better integration with schemas
  • More ergonomic generic types

Community and Ecosystem

Express advantages:

  • 10+ years of Stack Overflow answers
  • Thousands of blog posts and tutorials
  • Mature third-party integrations
  • Extensive middleware library
  • More books and courses

Fastify advantages:

  • Growing official plugin ecosystem
  • Active core team
  • Modern best practices
  • Better documentation structure
  • Active Discord community

Both have strong communities, but Express's maturity means:

  • More solutions to common problems
  • Easier to find developers with experience
  • More enterprise adoption

Final Thoughts

Both frameworks are excellent choices for building Node.js applications.

Express:

✅ Simple and approachable

✅ Battle-tested in production

✅ Massive ecosystem

✅ Beginner-friendly

⚠️ Lower performance

⚠️ Less structured

Fastify:

✅ Significantly faster

✅ Modern architecture

✅ Built-in validation

✅ Great TypeScript support

✅ Resource-efficient

⚠️ Steeper learning curve

⚠️ Smaller community

Decision framework:

Choose Express if:

  • You're learning or teaching
  • Community size matters most
  • You need maximum flexibility
  • Performance isn't critical

Choose Fastify if:

  • Performance is important
  • You're building microservices
  • You value structure and best practices
  • You're using TypeScript heavily

If you're starting a new project where performance matters, Fastify is worth serious consideration.

If you're already productive with Express and your application performs well, there's no urgent need to switch.

The best framework is the one that helps you ship reliable, maintainable software that serves your users effectively.


Additional Resources

Express:

Fastify:

Comparison tools:


💬 Question for you

Have you used Fastify in production? What was your experience compared to Express?

I'd love to hear about:

  • Performance improvements you measured
  • Migration challenges you faced
  • Whether the switch was worth it

Drop a comment below! 👇


Top comments (0)