PermissiveBSD-3-ClauseTested by Geekflare

hapi review

Configuration-driven Node.js server framework with built-in auth, validation, caching and plugins

Data refreshedMethodologyHow we tested
Director of Geekflare

Rity Anand is a director of Geekflare. She has extensive experience managing business technology and a special interest in emerging technologies.

Verdict

hapi suits teams that want auth, route validation, caching and a plugin system in one framework, configured per route, with official modules for JWT, cookies, third-party login and WebSockets. The catch is that it is kept going by a small volunteer committee on a modest community budget, and OpenAPI docs come only from the community hapi-swagger plugin.

Best for

Teams that want auth and validation configured per route in one framework

Not for

Teams that want OpenAPI from an official plugin or a large plugin ecosystem

Pros

  • Auth schemes and strategies are part of the core
  • Route validation for params, queries, payloads and headers with joi
  • Ships its own TypeScript types
  • Regular patch and security releases through 2026

Cons

  • OpenAPI docs only through the community hapi-swagger plugin
  • Run by volunteers on a small community budget
  • Node.js minimums differ between hapi, its docs and joi
Ask Sparkian

Is hapi right for your project?

Describe what you need and AI will check the fit against this review.

Or try:

hapi features and capabilities

Built-in auth framework

Auth schemes and strategies are registered once and chosen per route, with official modules for JWT, cookies, basic auth and third-party login through bell.

Route validation

Each route can validate headers, params, query, payload and cookies with joi, and set a failAction for what happens when validation fails.

Plugins and server composition

Features are packaged as plugins with their own routes and settings, and glue builds a server from a manifest, which keeps larger apps split into parts.

Caching built in

Server methods and routes can cache results through catbox with memory, Redis or Memcached adapters.

WebSockets through nes

The official nes plugin adds a WebSocket adapter for hapi routes, so real-time features stay inside the same server and plugin model.

TypeScript firstWritten in TypeScript or ships its own types, with typed routes or handlers
YesShips types with typed route refs
Request validationValidates bodies, params or queries against a schema, in the core or an official package
YesRoute validation with joi from the hapijs org
OpenAPI docsGenerates an OpenAPI spec or Swagger UI from routes
Partlyhapi-swagger is a community plugin
Beyond Node.jsOfficially runs on Bun, Deno or edge runtimes such as Cloudflare Workers
NoNode.js only
WebSocketsBuilt in or through an official plugin
YesOfficial @hapi/nes plugin
Project structureModules, dependency injection or a fixed layout, not only routing
PartlyPlugins and glue manifests; no fixed layout
Database layerAn official ORM, query builder or data layer
No
AuthenticationAn official auth module, plugin or strategy system
YesCore auth strategies plus official jwt, cookie and bell

At a glance

Type
Configuration-driven server framework
Stack
JavaScript / Node.js 16+

Free vs paid editions

What you get when you self-host, and what needs a paid plan.

hapi is free and has no paid edition or official hosting. A volunteer committee maintains it, with funding through Open Collective.

Running hapi

hapi 21 declares Node.js 14.15 or later, but the current joi release needs Node.js 20, so plan for Node.js 20 or later.

New cloud server to ready

9 s

Memory in use at idle

81 MB of 4 GB

Reads per second

10,111

Writes per second

5,421

Startup

0.48 s

Version tested

21.4.12 · Oct 31, 2026

  • The smallest install on disk, 36 packages and 5 MB with joi. It still took 9 seconds, longer than larger installs, so treat install times here as rough.
  • By default hapi hides validation details and only says the payload is invalid. Passing the error through failAction shows joi's message, which names only the first invalid field.
  • Reads had a median latency of 4.7 ms and 8.1 ms at the 99th percentile, and malformed JSON got a JSON error.
app.js, after npm i @hapi/hapi joi
import Hapi from "@hapi/hapi";
import Joi from "joi";

// 100 items kept in memory, so the test measures the framework, not a database.
const items = new Map();
for (let i = 1; i <= 100; i++) items.set(i, { id: i, name: `Item ${i}`, price: i * 1.5, tags: ["sample"] });
let nextId = 101;
const server = Hapi.server({ port: Number(process.env.PORT ?? 3000), host: "127.0.0.1" });

server.route({ method: "GET", path: "/health", handler: () => ({ status: "ok" }) });

server.route({
  method: "GET",
  path: "/items/{id}",
  handler: (req, h) => {
    const item = items.get(Number(req.params.id));
    if (!item) return h.response({ error: "Not found" }).code(404);
    return item;
  },
});

server.route({
  method: "POST",
  path: "/items",
  options: {
    validate: {
      payload: Joi.object({ name: Joi.string().min(1).max(100).required(), price: Joi.number().min(0).required(), tags: Joi.array().items(Joi.string()).max(10) }),
      // hapi hides validation details by default; return them like the other apps do.
      failAction: (req, h, err) => { throw err; },
    },
  },
  handler: (req, h) => {
    const item = { id: nextId++, ...req.payload };
    items.set(item.id, item);
    return h.response(item).code(201);
  },
});

await server.start();
OutputPassed21.4.12 · 400, with failAction set to pass on joi's message
Request: POST /items with {"name": "", "price": -1}
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "\"name\" is not allowed to be empty",
  "validation": {
    "source": "payload",
    "keys": [
      "name"
    ]
  }
}

What you can do with hapi

APIs with several login methods

Serve one API to browsers with cookie sessions and to services with JWTs, choosing the strategy per route.

Validated internal services

Define each route's inputs with joi so bad requests are rejected before handler code runs.

Larger apps split into plugins

Let separate teams own plugins with their own routes and settings, composed into one server with glue.