Express handles 30,000 requests per second. Fastify handles 65,000+. Both use Node.js. The difference? Fastify was designed for performance from day one — JSON serialization, schema-based validation, and a plugin architecture that doesn't sacrifice speed.
If you're building APIs on Node.js and performance matters, Express is leaving money on the table.
Fastify 5 — What You Get
- 2x Express throughput — 65,000+ req/s on a single core
- JSON Schema validation — request/response validated automatically
- Structured logging — Pino logger built in (30x faster than Winston)
- Plugin system — encapsulated, composable, no global middleware soup
- TypeScript-first — full type inference for routes, plugins, decorators
- Auto-generated Swagger — from your JSON schemas
Quick Start
npm init fastify@latest my-api
cd my-api && npm install && npm run dev
Basic Route With Validation
import Fastify from "fastify";
const app = Fastify({ logger: true });
app.post("/users", {
schema: {
body: {
type: "object",
required: ["name", "email"],
properties: {
name: { type: "string", minLength: 1 },
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0 },
},
},
response: {
201: {
type: "object",
properties: {
id: { type: "string" },
name: { type: "string" },
email: { type: "string" },
},
},
},
},
handler: async (request, reply) => {
const { name, email, age } = request.body;
const user = await db.users.create({ name, email, age });
reply.code(201).send(user);
},
});
app.listen({ port: 3000 });
The response schema doesn't just validate — it strips extra fields and speeds up JSON serialization by knowing the shape ahead of time.
Plugin System — Encapsulated, Not Global
// plugins/database.ts
import fp from "fastify-plugin";
export default fp(async (fastify) => {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
fastify.decorate("db", pool);
fastify.addHook("onClose", async () => {
await pool.end();
});
});
// routes/users.ts
export default async function userRoutes(fastify) {
fastify.get("/users", async (request) => {
const { rows } = await fastify.db.query("SELECT * FROM users LIMIT 50");
return rows;
});
fastify.get("/users/:id", async (request) => {
const { rows } = await fastify.db.query("SELECT * FROM users WHERE id = $1", [request.params.id]);
if (!rows[0]) throw fastify.httpErrors.notFound("User not found");
return rows[0];
});
}
// app.ts
app.register(import("./plugins/database"));
app.register(import("./routes/users"), { prefix: "/api" });
Auto-Generated Swagger
import swagger from "@fastify/swagger";
import swaggerUi from "@fastify/swagger-ui";
app.register(swagger, {
openapi: {
info: { title: "My API", version: "1.0.0" },
},
});
app.register(swaggerUi, { routePrefix: "/docs" });
// Your JSON schemas automatically become OpenAPI documentation
// Visit /docs for interactive API explorer
Hooks — Lifecycle Control
app.addHook("onRequest", async (request, reply) => {
// Runs before routing — auth, rate limiting
const token = request.headers.authorization;
if (!token) throw app.httpErrors.unauthorized();
request.user = await verifyToken(token);
});
app.addHook("onSend", async (request, reply, payload) => {
// Runs before sending response — add headers, transform
reply.header("X-Request-Id", request.id);
return payload;
});
app.addHook("onError", async (request, reply, error) => {
// Centralized error handling
request.log.error(error);
});
Fastify 5 vs Express vs Hono
| Feature | Fastify 5 | Express 4 | Hono |
|---|---|---|---|
| Req/s (Node.js) | 65,000+ | 30,000 | 45,000* |
| Validation | JSON Schema built-in | Manual | Zod/custom |
| Logging | Pino (built-in) | Morgan (add-on) | Built-in |
| TypeScript | First-class | @types needed | Native |
| Swagger gen | From schemas | Manual | Manual |
| Plugin isolation | Yes | No (global middleware) | Partial |
*Hono on Node; on Bun it's faster.
When to Choose Fastify
Choose Fastify when:
- Building production APIs on Node.js where performance matters
- You want auto-generated docs from validation schemas
- Structured logging is a requirement
- You prefer plugins over global middleware chains
Skip Fastify when:
- Simple prototype/MVP (Express is faster to start)
- You're deploying to edge/serverless (Hono or Bun-native is better)
- Your team knows Express and switching cost isn't justified
The Bottom Line
Fastify proves that Node.js APIs don't have to be slow. Built-in validation, logging, and plugin isolation give you production-grade features without the middleware spaghetti.
Start here: fastify.dev
Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors
Top comments (0)