h2o2

Proxy handler plugin for hapi.js that forwards requests to an upstream service with fine-grained control.

Library
npm
v10.0.4
164stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity0
Maintenance20
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
75/100Good
Architecture78
Code Quality90
Innovation55
Learning Curve75

@hapi/h2o2 adds a proxy handler to hapi.js servers, letting routes forward incoming requests to an upstream host and stream the response straight back to the client. It plugs into hapi’s handler and response-toolkit decoration system, so a route only needs handler: { proxy: { host, port, protocol } } (or the imperative h.proxy() toolkit method) to become a fully configurable reverse proxy — no separate proxy server or extra process required.

Under the hood it validates handler options against a Joi-style schema, maps every incoming request to an upstream target via mapUri, and hands the request to a pluggable HTTP client (Wreck by default). It supports header/cookie pass-through, X-Forwarded-* headers, TLS agent and cipher overrides, redirect following, upstream cache-control-driven TTLs, and custom onRequest/onResponse hooks for rewriting or intercepting responses before they reach the client.

What You Get

  • Drop-in proxy handler - register the plugin once and use handler: { proxy: {...} } on any route to forward requests upstream.
  • Imperative h.proxy() toolkit method - proxy programmatically from inside a custom handler instead of via route config.
  • Configurable upstream targeting - point at a fixed host/port/protocol, an absolute uri, or a dynamic mapUri(request) function.
  • Response post-processing hooks - onRequest and onResponse let you rewrite the upstream request or intercept/transform the response before it reaches the client.
  • TypeScript typings included - ships a hand-maintained .d.ts covering all handler options and toolkit decorations.

Common Use Cases

  • API gateway routing - a hapi.js gateway forwards /api/* requests to internal microservices while keeping auth and rate-limiting in the gateway layer.
  • Legacy service migration - teams incrementally move endpoints off an old backend by proxying unmigrated routes through h2o2 while new routes are served natively.
  • Asset/CDN passthrough - a hapi server proxies static asset requests to an origin store or CDN, adding caching headers via the upstream TTL support.
  • Response rewriting - an onResponse hook rewrites upstream JSON error bodies into a consistent API error format before returning them to clients.

Under The Hood

Architecture The entire plugin lives in a single module, lib/index.js, which registers itself via server.decorate('handler', 'proxy', ...) and server.decorate('toolkit', 'proxy', ...), and exposes an internal _agents map (via server.expose) that caches per-upstream http/https agents keyed by request.info.uri. It leans on @hapi/hoek for option merging/cloning/assertion and @hapi/validate for a Joi-style schema enforcing mutually-exclusive option groups (xor('host', 'mapUri', 'uri')). The handler resolves a mapUri function from either a literal uri, a host/port/protocol triple, or a user-supplied function, builds the outbound request options (headers, agent, payload, timeout), issues the request through a pluggable httpClient (defaulting to Wreck), and pipes the result back through hapi’s response toolkit with TTL and status/header handling. Because every consumer’s pass-through, x-forward, and TTL behavior routes through this one mapping/agent-selection path, changing it would ripple through all proxy routes in a server.

Tech Stack Plain JavaScript (Node >=14) with a hand-maintained .d.ts for TypeScript consumers. Runtime dependencies are all within the hapi family: @hapi/boom for error objects, @hapi/hoek for utility merge/clone/assert helpers, @hapi/validate for schema validation, and @hapi/wreck as the default HTTP client used to issue proxied requests. The dev/test stack uses @hapi/lab as the test runner with @hapi/code for assertions, a real @hapi/hapi server plus @hapi/inert for integration-style tests, and @hapi/teamwork for async test coordination. There’s no bundler or build step — it ships as plain CommonJS from lib/ (the files field in package.json limits the published package to that directory), and CI runs through a centralized reusable workflow (hapijs/.github ci-plugin.yml) pinned to a minimum Node 14 and hapi 20.

Code Quality test/index.js is a large Lab-based suite (~79 top-level it blocks, some nested) covering maxSockets/agent selection, TLS options, redirect handling, header/cookie pass-through, xforward, mapUri templating, error propagation through onResponse, and streaming edge cases, run with the -t 100 flag requiring full coverage to pass. A parallel test/index.ts and test/esm.js verify the TypeScript typings and ESM interop separately. Error handling favors explicit Hoek.assert/Validate.assert calls with descriptive messages over silent failures, and upstream errors are surfaced to onResponse or re-thrown. Naming follows consistent hapi conventions (an internals namespace object, camelCase throughout), and linting is delegated to the shared @hapi/eslint-plugin config referenced in package.json.

API Design The public surface is intentionally small: register the plugin, then either declare handler: { proxy: {...} } on a route or call the equivalent h.proxy(options) toolkit method imperatively — both accept the exact same option object, so there’s nothing new to learn between the declarative and imperative forms. API.md documents every option inline with its default value, keeping the option surface easy to scan even though it’s fairly large (TLS, redirects, caching, and forwarding are all first-class options rather than requiring a custom onResponse). The proxying pattern itself is a well-established one (comparable to Express’s http-proxy-middleware), so it isn’t a novel technical contribution, but it is executed with unusually low boilerplate for hapi users.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search