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');
});
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;
});
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 };
});
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 };
});
Express typically relies on external libraries like:
express-validatorJoiYup
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
});
// 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
}
});
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
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'
});
Key benefits:
- Encapsulation - Plugins are scoped to prevent namespace pollution
- Lifecycle hooks - Clean startup/shutdown handling
- Dependency management - Plugins can depend on other plugins
- 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
});
});
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
});
}
});
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:
- Performance is a proven bottleneck
- You're doing a major refactor anyway
- You have comprehensive test coverage
- 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
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 });
2. Implement caching strategies:
// Cache frequently accessed data
const cache = new NodeCache({ stdTTL: 600 });
3. Use compression middleware:
// Express
app.use(compression());
// Fastify
fastify.register(require('@fastify/compress'));
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 });
});
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 };
});
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)