fastify vs express vs hapi vs koa vs sails
Selecting a Node.js Web Framework for Backend-for-Frontend and API Services
fastifyexpresshapikoasails

Selecting a Node.js Web Framework for Backend-for-Frontend and API Services

express, fastify, hapi, koa, and sails are all server-side frameworks for Node.js used to build web applications and APIs. express is the minimal and widely adopted standard. fastify focuses on high performance and developer experience with built-in validation. hapi emphasizes configuration over code and is suited for enterprise needs. koa is a lightweight foundation built by the Express team that leverages async/await. sails is an opinionated MVC framework that auto-generates REST APIs.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
fastify7,307,66136,3482.78 MB1462 months agoMIT
express069,07075.4 kB2146 months agoMIT
hapi014,786-617 years agoBSD-3-Clause
koa035,70965 kB3212 days agoMIT
sails022,8073.27 MB5895 days agoMIT

Node.js Web Frameworks: Express vs Fastify vs Hapi vs Koa vs Sails

When building a Backend-for-Frontend (BFF) or a standalone API in Node.js, the framework you choose shapes your development speed, runtime performance, and long-term maintenance. express, fastify, hapi, koa, and sails all solve the same core problem β€” handling HTTP requests β€” but they approach it with different philosophies. Let's compare how they handle routing, middleware, validation, and architecture.

πŸ›£οΈ Routing and Request Handling

The way you define routes sets the tone for your entire codebase. Some frameworks prefer code, while others prefer configuration.

express uses a straightforward, code-based approach where you attach methods directly to the app instance.

// express: Direct method attachment
const express = require('express');
const app = express();

app.get('/users', (req, res) => {
  res.json({ users: [] });
});

fastify looks similar to Express but wraps the request and reply objects for better performance and encapsulation.

// fastify: Encapsulated request/reply
const fastify = require('fastify')();

fastify.get('/users', async (request, reply) => {
  return { users: [] };
});

hapi separates route definitions from the server instance using a configuration object.

// hapi: Configuration-based routes
const Hapi = require('@hapi/hapi');
const server = Hapi.server({ port: 3000 });

server.route({
  method: 'GET',
  path: '/users',
  handler: (request, h) => {
    return { users: [] };
  }
});

koa does not have a built-in router. You must install @koa/router separately, and it uses a middleware stack approach.

// koa: Middleware stack with external router
const Koa = require('koa');
const Router = require('@koa/router');
const app = new Koa();
const router = new Router();

router.get('/users', (ctx) => {
  ctx.body = { users: [] };
});

app.use(router.routes());

sails relies heavily on convention. Routes are often auto-generated based on your controllers and models, or defined in a config file.

// sails: config/routes.js mapping
module.exports.routes = {
  'GET /users': 'UserController.find'
};

// UserController.js
module.exports = {
  find: async function (req, res) {
    return res.json({ users: [] });
  }
};

πŸ”Œ Middleware and Plugin Architecture

Middleware determines how you handle cross-cutting concerns like logging, authentication, and parsing.

express uses a linear stack. Middleware functions receive req, res, and next.

// express: Linear middleware stack
app.use((req, res, next) => {
  console.log('Time:', Date.now());
  next();
});

fastify uses a hook-based system (onRequest, preHandler) which is faster and avoids the next() callback pattern.

// fastify: Lifecycle hooks
fastify.addHook('onRequest', async (request, reply) => {
  console.log('Request received');
});

hapi uses extension points that are similar to hooks but strictly configured within the server lifecycle.

// hapi: Server extensions
server.ext('onRequest', (request, h) => {
  console.log('Request received');
  return h.continue;
});

koa uses a composed middleware stack where you call next() as a promise, enabling clean async/await flows.

// koa: Composed async middleware
app.use(async (ctx, next) => {
  await next();
  console.log('Response sent');
});

sails uses a policy system for middleware-like logic, often applied globally or per-controller.

// sails: Policies (config/policies.js)
module.exports.policies = {
  UserController: {
    find: ['isAuthenticated']
  }
};

βœ… Validation and Schema

Validating input is critical for security. Some frameworks require external libraries, while others have it built-in.

express has no built-in validation. You typically add joi, zod, or express-validator.

// express: External validation middleware
const { body, validationResult } = require('express-validator');

app.post('/users', 
  body('email').isEmail(), 
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) return res.status(400).json(errors);
  }
);

fastify has schema-based validation built-in using JSON Schema.

// fastify: Built-in JSON Schema validation
fastify.post('/users', {
  schema: {
    body: {
      type: 'object',
      properties: {
        email: { type: 'string', format: 'email' }
      }
    }
  }
}, async (request, reply) => {
  // Body is already validated
});

hapi uses joi (now hapi/joi or joi standalone) integrated deeply into the route validation options.

// hapi: Integrated Joi validation
server.route({
  method: 'POST',
  path: '/users',
  options: {
    validate: {
      payload: {
        email: Joi.string().email().required()
      }
    }
  },
  handler: (request, h) => { /*...*/ }
});

koa like Express, requires external middleware for validation.

// koa: External validation middleware
const validate = require('koa-json-validator');

router.post('/users', validate({
  properties: {
    email: { type: 'string' }
  }
}), (ctx) => { /*...*/ });

sails includes validation rules directly in your model definitions.

// sails: Model-based validation (api/models/User.js)
module.exports = {
  attributes: {
    email: {
      type: 'string',
      isEmail: true,
      required: true
    }
  }
};

⚑ Performance and Concurrency

Runtime speed matters when your API scales. fastify is generally recognized as the fastest in this group due to its low-overhead architecture. express is solid but carries legacy overhead. hapi is robust but heavier due to its validation and configuration layers. koa is lightweight but depends on what middleware you add. sails is the heaviest as it loads a full MVC stack including ORM and WebSocket support by default.

For high-throughput microservices, fastify often handles 2x-3x more requests per second than express in benchmark scenarios. koa sits in the middle, offering good performance if you keep the middleware stack lean.

πŸ› οΈ Maintenance and Ecosystem Status

express is in maintenance mode. It is stable and unlikely to change, which is good for longevity but means new features are rare. The ecosystem is massive.

fastify is actively developed with frequent releases. It has a growing plugin ecosystem and strong TypeScript support.

hapi is stable and enterprise-focused. It has a smaller community than Express but is well-maintained by its core team.

koa is stable with infrequent updates. It is a foundation rather than a full framework, so you rely on third-party middleware quality.

sails has a slower release cadence. While still functional, it is less common in new greenfield projects compared to modern alternatives like NestJS or Fastify.

πŸ“Š Summary Table

Featureexpressfastifyhapikoasails
PhilosophyMinimal, UnopinionatedHigh Performance, Schema-basedConfiguration, EnterpriseMinimal, Async/AwaitMVC, Convention over Config
RoutingCode-basedCode-basedConfig-basedExternal ModuleAuto-generated / Config
ValidationExternal MiddlewareBuilt-in (JSON Schema)Built-in (Joi)External MiddlewareModel-based
Async ModelCallbacks / PromisesPromises / AsyncPromises / AsyncAsync / AwaitAsync / Await
Learning CurveLowLow/MediumMediumMediumHigh
Best ForGeneral Purpose, LegacyMicroservices, High LoadEnterprise, Complex AppsCustom ArchitecturesRapid CRUD Prototyping

πŸ’‘ The Big Picture

express remains the safe choice for general-purpose APIs where library support is critical. It is the default for a reason β€” it works everywhere.

fastify is the modern upgrade. If you are starting a new service today, especially a microservice, its built-in validation and speed make it a strong candidate.

hapi shines in large organizations where configuration rules and strict validation prevent errors before they happen.

koa is for architects who want to build their own framework on top of a solid async foundation without the baggage of older callback patterns.

sails is a specialized tool for teams that want a Rails-like experience in Node.js, complete with auto-generated APIs and WebSocket support, but it comes with more weight.

Final Thought: For most frontend teams building BFF layers, fastify offers the best balance of speed and developer experience, while express remains the reliable workhorse for integrating with existing tools. Choose based on how much structure you want out of the box versus how much you want to build yourself.

How to Choose: fastify vs express vs hapi vs koa vs sails

  • fastify:

    Choose fastify if performance is a priority or you want built-in schema validation to reduce boilerplate. It is ideal for microservices and high-throughput APIs where low latency matters.

  • express:

    Choose express if you need maximum compatibility with existing middleware, extensive community support, and a simple learning curve. It is the safest bet for legacy projects or when you need a specific plugin that only exists in the Express ecosystem.

  • hapi:

    Choose hapi if you prefer configuration over code and need robust input validation and security policies out of the box. It fits well in large enterprise teams that value strict structure and stability.

  • koa:

    Choose koa if you want a minimal core without callback hell and prefer building your own architecture from the ground up using modern async/await features. It is best for developers who want full control over middleware stacking.

  • sails:

    Choose sails if you want a full MVC framework that automatically generates REST APIs from your data models. It is suitable for rapid prototyping of standard CRUD applications but may feel heavy for custom microservices.

README for fastify

CI Package Manager
CI Web
site neostandard javascript style CII Best Practices

NPM
version NPM
downloads Security Responsible
Disclosure Discord Contribute with Gitpod Open Collective backers and sponsors


An efficient server implies a lower cost of the infrastructure, better responsiveness under load, and happy users. How can you efficiently handle the resources of your server, knowing that you are serving the highest number of requests possible, without sacrificing security validations and handy development?

Enter Fastify. Fastify is a web framework highly focused on providing the best developer experience with the least overhead and a powerful plugin architecture. It is inspired by Hapi and Express and as far as we know, it is one of the fastest web frameworks in town.

The main branch refers to the Fastify v5 release. Check out the 4.x branch for v4.

Table of Contents

Quick start

Create a folder and make it your current working directory:

mkdir my-app
cd my-app

Generate a fastify project with npm init:

npm init fastify

Install dependencies:

npm i

To start the app in dev mode:

npm run dev

For production mode:

npm start

Under the hood npm init downloads and runs Fastify Create, which in turn uses the generate functionality of Fastify CLI.

Install

To install Fastify in an existing project as a dependency:

npm i fastify

Example

// Require the framework and instantiate it

// ESM
import Fastify from 'fastify'

const fastify = Fastify({
  logger: true
})
// CommonJs
const fastify = require('fastify')({
  logger: true
})

// Declare a route
fastify.get('/', (request, reply) => {
  reply.send({ hello: 'world' })
})

// Run the server!
fastify.listen({ port: 3000 }, (err, address) => {
  if (err) throw err
  // Server is now listening on ${address}
})

With async-await:

// ESM
import Fastify from 'fastify'

const fastify = Fastify({
  logger: true
})
// CommonJs
const fastify = require('fastify')({
  logger: true
})

fastify.get('/', async (request, reply) => {
  reply.type('application/json').code(200)
  return { hello: 'world' }
})

fastify.listen({ port: 3000 }, (err, address) => {
  if (err) throw err
  // Server is now listening on ${address}
})

Do you want to know more? Head to the Getting Started. If you learn best by reading code, explore the official demo.

Note

.listen binds to the local host, localhost, interface by default (127.0.0.1 or ::1, depending on the operating system configuration). If you are running Fastify in a container (Docker, GCP, etc.), you may need to bind to 0.0.0.0. Be careful when listening on all interfaces; it comes with inherent security risks. See the documentation for more information.

Core features

  • Highly performant: as far as we know, Fastify is one of the fastest web frameworks in town, depending on the code complexity we can serve up to 76+ thousand requests per second.
  • Extensible: Fastify is fully extensible via its hooks, plugins, and decorators.
  • Schema-based: even if it is not mandatory we recommend using JSON Schema to validate your routes and serialize your outputs. Internally Fastify compiles the schema in a highly performant function.
  • Logging: logs are extremely important but are costly; we chose the best logger to almost remove this cost, Pino!
  • Developer friendly: the framework is built to be very expressive and help developers in their daily use without sacrificing performance and security.

Benchmarks

Machine: EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C/8T, SSD.

Method: autocannon -c 100 -d 40 -p 10 localhost:3000 * 2, taking the second average

FrameworkVersionRouter?Requests/sec
Express4.17.3βœ“14,200
hapi20.2.1βœ“42,284
Restify8.6.1βœ“50,363
Koa2.13.0βœ—54,272
Fastify4.0.0βœ“77,193
-
http.Server16.14.2βœ—74,513

These benchmarks taken using https://github.com/fastify/benchmarks. This is a synthetic "hello world" benchmark that aims to evaluate the framework overhead. The overhead that each framework has on your application depends on your application. You should always benchmark if performance matters to you.

Documentation

Ecosystem

  • Core - Core plugins maintained by the Fastify team.
  • Community - Community-supported plugins.
  • Live Examples - Multirepo with a broad set of real working examples.
  • Discord - Join our discord server and chat with the maintainers.

Support

Please visit Fastify help to view prior support issues and to ask new support questions.

Version 3 of Fastify and lower are EOL and will not receive any security or bug fixes.

Fastify's partner, HeroDevs, provides commercial security fixes for all unsupported versions at https://herodevs.com/support/fastify-nes. Fastify's supported version matrix is available in the Long Term Support documentation.

Contributing

Whether reporting bugs, discussing improvements and new ideas, or writing code, we welcome contributions from anyone and everyone. Please read the CONTRIBUTING guidelines before submitting pull requests.

Team

Fastify is the result of the work of a great community. Team members are listed in alphabetical order.

Lead Maintainers:

Fastify Core team

Fastify Plugins team

Emeritus Contributors

Great contributors to a specific area of the Fastify ecosystem will be invited to join this group by Lead Maintainers when they decide to step down from the active contributor's group.

Hosted by

We are an At-Large Project in the OpenJS Foundation.

Sponsors

Support this project by becoming a SPONSOR! Fastify has an Open Collective page where we accept and manage financial contributions.

Acknowledgments

This project is kindly sponsored by:

Past Sponsors:

This list includes all companies that support one or more team members in maintaining this project.

License

Licensed under MIT.

For your convenience, here is a list of all the licenses of our production dependencies:

  • MIT
  • ISC
  • BSD-3-Clause
  • BSD-2-Clause