Backend
Intermediate 16 min read Backend

Web Frameworks: Express & Fastify

Express and Fastify are the two dominant Node.js web frameworks. Both map URLs and verbs to handlers, but they differ in philosophy: Express is minimal and imperative with a giant ecosystem, while Fastify is schema-driven, plugin-based and built for performance and validation.

ExpressFastifymiddlewareroutingpluginsvalidation
Introduction

What is this?

Web frameworks abstract raw HTTP handling behind a router and a middleware pipeline. Express wraps Node's http module with route matching, middleware chaining and helper methods, and its plugin ecosystem covers nearly every need. Fastify adds schema-based request validation, a decorator system, encapsulated plugins and benchmark-leading throughput, while keeping a familiar API.

Motivation

Why do you need it?

  • Routing by method and path replaces manual URL parsing, cutting boilerplate and bugs.
  • Middleware chains let you compose cross-cutting concerns — logging, auth, parsing — in order.
  • Request/response helpers (res.json, res.status) and error handling make handlers small and readable.
  • Both frameworks have huge communities, docs and plugins, so production concerns (validation, CORS, OpenAPI) are a package install away.
Think about it

Mental model

Imagine

An airport security line

Think of

Middleware in a web framework

Each passenger (request) walks through stations in order: ID check (auth), baggage scan (body parsing), metal detector (rate limiting) — until they board (the route handler). Any station can stop the line (respond early), and the order matters.

Mechanics

How it works

  • The framework receives the raw request and builds a Request and Response abstraction.
  • A stack of middleware runs in registration order; each middleware can modify the request, respond early, or call next() to continue.
  • The router matches the method and path pattern (including params like /users/:id) to a handler.
  • The handler produces a response via res.send/json/status, which the framework serializes and returns.
  • Errors thrown anywhere bubble to an error-handling middleware that formats a consistent error response.

Request flow through middleware, router and handler in Express/Fastify.

Incoming request
Body parser + CORS
Auth middleware
Router match
Route handler
Response sent
Reference

Key commands

npm install express

Install Express into your project.

added 66 packages in 2s
npm install fastify

Install Fastify.

added 20 packages in 1s
npx express-generator my-app

Scaffold a full Express project with a folder structure.

create : my-app\ncreate : my-app/package.json\n...
fastify.get('/x', handler)

Register a route with Fastify's fluent API (works in any file that has the instance).

node --watch app.js

Run the framework app and restart on file changes during development.

Hands-on

Examples

A REST API with Express and one with Fastify

A REST API with Express and one with Fastify
// Express
const express = require("express")
const app = express()
app.use(express.json())
app.get("/users/:id", (req, res) => {
res.json({ id: req.params.id, source: "express" })
})
app.listen(3000, () => console.log("express on :3000"))
// Fastify
const fastify = require("fastify")({ logger: true })
fastify.get("/users/:id", {
schema: { params: { type: "object", required: ["id"], properties: { id: { type: "string" } } } },
}, async (request, reply) => {
return { id: request.params.id, source: "fastify" }
})
fastify.listen({ port: 3001 })

Both register a GET route for /users/:id. Express uses app.use(express.json()) to parse bodies and res.json to send. Fastify inlines a JSON Schema for the params so requests are validated automatically, and returning an object from an async handler is all it needs to serialize a response — no res.json call required.

Real world

In production

  • Express powers public-facing REST and GraphQL APIs across startups and enterprises, plus BFFs for web frontends.
  • Fastify is a common choice for high-throughput microservices where latency per request matters.
  • Both frameworks serve as the core for CLI-generated projects and serverless adapters (serverless-http, @fastify/aws-lambda).
  • Companies expose OpenAPI docs from Fastify schemas or Express via swagger-jsdoc to keep clients in sync.
Guidelines

Best practices

  • Keep handlers thin: parse/validate in middleware, do work in services, format in handlers.
  • Register JSON body parsing and CORS before any route that needs them.
  • In Express, use express.json() for bodies and a centralized error handler with four arguments.
  • In Fastify, define JSON Schemas on every route so validation and serialization are automatic.
  • Structure Fastify apps as plugins with fastify-plugin for encapsulation and testability.
Watch out

Common mistakes

  • Forgetting next() or returning without responding, which hangs the request until timeout.
  • Registering middleware after routes, so routes never pass through it.
  • In Express, using app.use with a path that over-matches (e.g. app.use('/user') also matches /userx).
  • Trusting req.body before a body parser is registered, leaving it undefined.
Root cause analysis

Common issues

Error
Cannot read properties of undefined (reading 'name') / req.body is undefined
Root cause

The body parser is missing or registered after the route.

Fix

Add app.use(express.json()) before your routes in Express.

Error
FastifyError: Fastify is already listening
Root cause

listen() was called twice, usually via a test helper and the app entry point.

Fix

Guard the server start (if (require.main === module)) so tests can inject their own.

Error
FST_ERR_VALIDATION: params/id is required
Root cause

A request arrived that does not match the route schema.

Fix

Check the request path and that the schema's required fields match the route params.

When things break

Debugging tips

  • Add request logging early — Fastify has { logger: true } built in; Express needs morgan.
  • A handler that never responds usually means a missing next() or an await that rejected and was unhandled.
  • Use NODE_ENV=development and stack traces from the error handler to see the failing middleware chain.
  • Reproduce with a single curl request and -v to see exactly what the server received and returned.
Interviews

Interview checkpoint

Practice

Hands-on lab

Ship the same API on Express and Fastify
35 min · intermediate
In progress
Objectives
  • Scaffold a minimal Express app with JSON body parsing
  • Build a CRUD endpoint for an in-memory store
  • Replicate the API with Fastify including a JSON Schema
  • Compare validation, logging and error responses

Ship the same API on Express and Fastify

35 min · intermediate

0/5 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

What is the correct order for Express middleware that parses JSON bodies?

2

In Fastify, returning an object from an async handler automatically sends it as a JSON response.

3

Which framework validates request payloads against JSON Schema by default?

Quick reference

Cheat sheet

Express
app.use(express.json())Parse JSON bodies
app.get('/users/:id', h)Route with a param
res.json(obj)Send JSON
next(err)Forward to error handler
Fastify
fastify.get('/x', opts, handler)Route with schema
{ logger: true }Enable request logging
reply.code(201).send(obj)Status + payload
fastify.register(plugin)Encapsulated plugin
Go deeper

Official documentation