express, fastify, hapi, and koa are the four major server frameworks for Node.js, each solving the problem of handling HTTP requests with different architectural philosophies. express is the unopinionated industry standard with a massive middleware ecosystem. fastify focuses on speed and low overhead, using a schema-based approach for validation and serialization. hapi emphasizes configuration over code, offering robust built-in features for enterprise needs without relying on third-party middleware. koa, created by the Express team, modernizes the stack with async/await and a lightweight context object, leaving middleware choices entirely to the developer.
When building a backend in Node.js, the framework you pick shapes how you handle requests, validate data, and structure your code. express, fastify, hapi, and koa are the main contenders. They all route HTTP traffic, but they solve it in very different ways. Let's look at how they handle the daily work of an API developer.
express relies on a middleware chain. You add functions that run one after another. It is flexible but can get messy if you don't organize files well.
// express: Middleware chain
const express = require('express');
const app = express();
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
app.get('/user', (req, res) => {
res.send('User Data');
});
fastify uses a plugin architecture and hooks. It is designed for speed and encapsulates logic to avoid global state pollution.
// fastify: Plugin and Hooks
const fastify = require('fastify')();
fastify.addHook('onRequest', async (request, reply) => {
console.log('Incoming request');
});
fastify.get('/user', async (request, reply) => {
return { hello: 'world' };
});
hapi avoids middleware entirely. Instead, you configure extensions and routes with detailed options. It forces a structured approach.
// hapi: Configuration and Extensions
const Hapi = require('@hapi/hapi');
const server = Hapi.server({ port: 3000 });
server.ext('onRequest', (request, h) => {
console.log('Request started');
return h.continue;
});
server.route({
method: 'GET',
path: '/user',
handler: (request, h) => 'User Data'
});
koa uses a stack-like middleware system but with async/await. It gives you a clean ctx object instead of separate req and res.
// koa: Async Middleware Stack
const Koa = require('koa');
const app = new Koa();
app.use(async (ctx, next) => {
console.log('Time:', Date.now());
await next();
});
app.use(async (ctx) => {
ctx.body = 'User Data';
});
express has no built-in validation. You must install libraries like joi, zod, or express-validator and wire them up manually.
// express: Manual validation setup
const { body, validationResult } = require('express-validator');
app.post('/user',
body('email').isEmail(),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(400).json({ errors });
res.send('Created');
}
);
fastify has validation built-in using JSON Schema. It also serializes responses quickly based on the schema.
// fastify: Schema-based validation
fastify.post('/user', {
schema: {
body: {
type: 'object',
properties: { email: { type: 'string' } },
required: ['email']
}
}
}, async (request, reply) => {
return { id: 1, email: request.body.email };
});
hapi uses joi (often bundled or integrated) for rigorous validation rules defined in the route config.
// hapi: Joi validation in route options
const Joi = require('joi');
server.route({
method: 'POST',
path: '/user',
options: {
validate: {
payload: Joi.object({ email: Joi.string().required() })
}
},
handler: (request, h) => ({ id: 1, email: request.payload.email })
});
koa like Express, leaves validation to you. You pick your own library and middleware.
// koa: External validation middleware
const validator = require('koa-validator');
app.use(validator());
app.post('/user', async (ctx) => {
ctx.assert(ctx.request.body.email, 400, 'Email required');
ctx.body = { id: 1 };
});
express uses a special middleware function with four arguments. If you forget the next(err), the app hangs.
// express: Error middleware
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
fastify lets you set a global error handler or handle errors inside routes using try/catch.
// fastify: Global error handler
fastify.setErrorHandler((error, request, reply) => {
console.error(error);
reply.status(500).send({ error: 'Internal Server Error' });
});
hapi handles errors via the response toolkit. You return an error object or throw one, and the framework formats it.
// hapi: Error handling in handler
server.route({
method: 'GET',
path: '/error',
handler: (request, h) => {
throw new Error('Something broke');
// Or: return h.response().code(500);
}
});
koa uses a central error handler middleware. Since it uses promises, unhandled rejections are caught here.
// koa: Central error handler
app.use(async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.status = err.status || 500;
ctx.body = err.message;
}
});
express is stable but older. It does not serialize JSON as fast as newer frameworks and has higher overhead per request.
// express: Standard JSON send
res.json({ message: 'Hello' });
// Uses JSON.stringify internally
fastify is built for speed. It uses fast-json-stringify to serialize responses up to 2x faster than standard JSON.
// fastify: Optimized serialization
reply.send({ message: 'Hello' });
// Uses schema-based serialization if defined
hapi prioritizes security and structure over raw speed. It is generally slower than Fastify but consistent.
// hapi: Standard response
return h.response({ message: 'Hello' });
// Robust but heavier processing
koa is lightweight and modern. It is faster than Express because it doesn't bundle legacy features, but it lacks Fastify's serialization tricks.
// koa: Body assignment
ctx.body = { message: 'Hello' };
// Standard JSON.stringify
express has the largest ecosystem. If a tool exists for Node, it likely has an Express middleware.
// express: Huge middleware variety
const cors = require('cors');
const helmet = require('helmet');
app.use(cors());
app.use(helmet());
fastify has a growing plugin ecosystem. Plugins are encapsulated, which prevents conflicts but requires learning the specific API.
// fastify: Encapsulated plugins
fastify.register(require('@fastify/cors'));
fastify.register(require('@fastify/helmet'));
hapi has a curated set of plugins. Quality is high, but variety is lower than Express.
// hapi: Official and community plugins
await server.register(require('@hapi/inert')); // Static assets
await server.register(require('@hapi/vision')); // Templates
koa shares much of the Express middleware ecosystem but requires wrappers for some older callback-based middleware.
// koa: Middleware compatibility
const cors = require('@koa/cors');
app.use(cors());
// Some express middleware needs 'koa-connect' wrapper
| Feature | express | fastify | hapi | koa |
|---|---|---|---|---|
| Style | Middleware | Plugin + Schema | Configuration | Async Middleware |
| Validation | External (Manual) | Built-in (JSON Schema) | Built-in (Joi) | External (Manual) |
| Error Handling | 4-arg Middleware | Handler / Try-Catch | Toolkit / Throw | Try-Catch Middleware |
| Performance | Standard | High (Optimized) | Standard | Lightweight |
| Learning Curve | Low | Medium | High | Medium |
| Best For | General Purpose | High Throughput | Enterprise | Modern Custom Stacks |
express is the reliable workhorse 🐴. It is everywhere, easy to learn, and gets the job done for 90% of projects. Use it when you want to spend time on business logic, not framework configuration.
fastify is the speed demon 🏎️. If you are building microservices that need to handle thousands of requests per second, or if you love schema-driven development, this is the modern choice.
hapi is the enterprise vault 🏦. It is strict and secure by default. Choose it if you are in a regulated industry or need a framework that enforces good patterns across a large team.
koa is the minimalist's canvas 🎨. It gives you the modern async features of Express without the legacy weight. Pick it if you want to build your own architecture from the ground up.
Final Thought: All four frameworks can build production-ready APIs. The decision comes down to whether you value ecosystem size (express), raw performance (fastify), strict governance (hapi), or modern minimalism (koa).
Choose hapi if you are building large-scale enterprise applications where security, input validation, and strict configuration are more important than flexibility. It suits teams that want a 'batteries-included' framework with strong governance and less reliance on the quality of external middleware.
Choose express if you need the widest possible selection of third-party middleware, tutorials, and community support. It is the safest bet for standard REST APIs, prototypes, and teams that value convention and ease of hiring. Stick with it if you don't have strict performance requirements that demand micro-optimization.
Choose fastify if performance is a top priority or if you want built-in validation and serialization without adding extra libraries. It is ideal for high-throughput microservices and teams that prefer schema-driven development to catch errors early in the request lifecycle.
Choose koa if you want a modern, lightweight foundation that uses async/await natively and gives you full control over middleware composition. It is best for developers who find Express callbacks cumbersome and want to build a custom stack without the baggage of legacy Express design decisions.
Lead Maintainer: Eran Hammer
hapi is a simple to use configuration-centric framework with built-in support for input validation, caching, authentication, and other essential facilities for building web and services applications. hapi enables developers to focus on writing reusable application logic in a highly modular and prescriptive approach.
Version 18.x only supports node v8.12.0 and over. For older version of node please use hapi version 16.x.
Development version: 18.1.x (release notes)
For the latest updates, change log, and release information visit hapijs.com and follow @hapijs on twitter. If you have questions, please open an issue in the discussion forum.
Development of the hapi core module generously supported by contributions from individuals and corporations. If you are benefiting from hapi and would like to help keep the project financially sustainable, please visit Eran Hammer's Patreon page or contact him via email.
Past major financial support for the project was provided by:
