Express.js vs Fastify: Comparison for building Node.js applications - Featured Image
App Development5 min read

Express.js vs Fastify: Comparison for building Node.js applications

When you're building backend apps with Node.js, two frameworks really stand out: Express.js and Fastify. Both are solid choices, but they work differently and excel in different areas. Let me break down what makes each one special and help you figure out which one might work better for your next project.

Overview

Express.js: This has been around the block. It's the framework most developers learn first, and for good reason. Express keeps things simple and gives you tons of flexibility. The community is huge, which means you'll find middleware for pretty much anything you need. It's like the reliable old friend of Node.js frameworks.

Fastify: Think of this as the performance-focused younger sibling. Fastify was built from the ground up to be fast and handle lots of traffic without breaking a sweat. It comes with built-in validation using JSON schemas, which is pretty neat. If speed is your main concern, Fastify might be your answer.

Setting up Express and Fastify

Let's see how easy it is to get a basic server running with both frameworks.

Express.js

// express-setup.js
const express = require('express');
const app = express();

app.use(express.json()); // Parse JSON data
app.get('/', (req, res) => {
  res.send('Hello from Express!');
});

app.listen(3000, () => {
  console.log('Express server running on http://localhost:3000');
});

Fastify

// fastify-setup.js
const fastify = require('fastify')();

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

fastify.listen(3000, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Fastify server running on http://localhost:3000');
});

Both setups are pretty straightforward. Notice how Fastify just returns an object for JSON responses - it handles the conversion automatically, which is one of those little touches that makes it faster.

Request validation

Here's where things get interesting. Making sure the data coming into your API is valid is super important. Fastify has this built right in, while Express needs some help from external libraries like Joi.

Express.js (validation with Joi)

// express-validation.js
const express = require('express');
const Joi = require('joi');
const app = express();
app.use(express.json());

const userSchema = Joi.object({
  name: Joi.string().required(),
  age: Joi.number().integer().min(0).required(),
});

app.post('/user', (req, res) => {
  const { error } = userSchema.validate(req.body);
  if (error) return res.status(400).send(error.details[0].message);
  res.send(`User ${req.body.name} validated successfully!`);
});

app.listen(3000, () => console.log('Express validation example running'));

Fastify (built-in JSON schema validation)

// fastify-validation.js
const fastify = require('fastify')();

fastify.post('/user', {
  schema: {
    body: {
      type: 'object',
      required: ['name', 'age'],
      properties: {
        name: { type: 'string' },
        age: { type: 'integer', minimum: 0 },
      },
    },
  },
  handler: async (request, reply) => {
    const { name, age } = request.body;
    return { message: `User ${name} is ${age} years old.` };
  },
});

fastify.listen(3000, (err) => {
  if (err) throw err;
  console.log('Fastify validation example running');
});

Middleware and plugins

Express uses middleware (functions that run between request and response), while Fastify uses a plugin system. Both work well, but they feel different when you're coding.

Express.js middleware example

// express-middleware.js
const express = require('express');
const app = express();

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

app.get('/', (req, res) => {
  res.send('Middleware in Express!');
});

app.listen(3000, () => console.log('Express middleware example running'));

Fastify plugin example

// fastify-plugin.js
const fastify = require('fastify')();

fastify.register(async (instance) => {
  instance.addHook('preHandler', async (request, reply) => {
    console.log(`${request.method} ${request.url}`);
  });
});

fastify.get('/', async (request, reply) => {
  return { message: 'Plugins in Fastify!' };
});

fastify.listen(3000, (err) => {
  if (err) throw err;
  console.log('Fastify plugin example running');
});

Fastify's plugin system is pretty clever. It keeps things organized and can actually boost performance because of how it handles the lifecycle of requests.

Error handling

When things go wrong (and they will), both frameworks let you handle errors in a clean, centralized way.

Express.js error handling

// express-error-handling.js
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  throw new Error('Oops! Something went wrong.');
});

app.use((err, req, res, next) => {
  console.error(err.message);
  res.status(500).send('Internal Server Error');
});

app.listen(3000, () => console.log('Express error handling example running'));

Fastify error handling

// fastify-error-handling.js
const fastify = require('fastify')();

fastify.setErrorHandler((error, request, reply) => {
  console.error(error.message);
  reply.status(500).send({ error: 'Internal Server Error' });
});

fastify.get('/', async (request, reply) => {
  throw new Error('Oops! Something went wrong in Fastify.');
});

fastify.listen(3000, (err) => {
  if (err) throw err;
  console.log('Fastify error handling example running');
});

Handling complex responses and optimization

Here's where Fastify really shines. It can optimize how it sends JSON responses by using schemas, which makes everything faster.

// fastify-optimized-response.js
const fastify = require('fastify')();

fastify.get('/data', {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          id: { type: 'number' },
          name: { type: 'string' },
          active: { type: 'boolean' },
        },
      },
    },
  },
  handler: async (request, reply) => {
    return { id: 1, name: 'John Doe', active: true };
  },
});

fastify.listen(3000, (err) => {
  if (err) throw err;
  console.log('Fastify optimized response example running');
});

TypeScript support

If you're working on bigger projects, TypeScript can save you from a lot of headaches. Both frameworks work with TypeScript, but Fastify's approach feels more natural.

Fastify with TypeScript

import Fastify, { FastifyInstance } from 'fastify';

const server: FastifyInstance = Fastify();

server.get('/ping', async (request, reply) => {
  return { pong: 'it worked!' };
});

server.listen(3000, (err) => {
  if (err) {
    console.error(err);
    process.exit(1);
  }
  console.log('Fastify with TypeScript example running');
});

Summary

So which one should you pick? It depends on what you're building. Go with Express if you want something familiar and flexible - it's great for general apps and prototypes. Choose Fastify if you need speed and built-in validation for high-traffic applications. Both are solid choices.

Posted on: 30/6/2025

hassaankhan

Frontend Developer — UI/UX Enthusiast and building scalable web apps

Posted by





Subscribe to our newsletter

Join 2,000+ subscribers

Stay in the loop with everything you need to know.

We care about your data in our privacy policy

Background shadow leftBackground shadow right

Have something to share?

Write on the platform and dummy copy content

Be Part of Something Big

Shifters, a developer-first community platform, is launching soon with all the features. Don't miss out on day one access. Join the waitlist: