fastify-reply-from

Fastify plugin that forwards the current HTTP request to another server, with pluggable HTTP/1.1, HTTP/2, and undici transports.

Library
npm
v12.6.5
167stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
82/100Excellent
Development Activity80
Maintenance84
Community84
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
79/100Good
Architecture85
Code Quality92
Innovation84
Learning Curve55

@fastify/reply-from decorates Fastify’s reply object with a from() method so route handlers can forward the current request to an upstream server and stream the response straight back to the client. It functions as a lightweight reverse-proxy building block for Fastify applications, without requiring a separate proxy layer like nginx or HAProxy in front of the app.

Under the hood it can transport requests over undici (the default, for maximum throughput), Node’s native http/https module, or HTTP/2, and it automatically switches to undici’s BalancedPool for round-robin load balancing when given multiple base origins. It ships automatic retry logic for idempotent methods on 503 responses, pluggable header rewriting hooks, and typed errors for every failure mode, making it a common building block for API gateways, backend-for-frontend services, and incremental service migrations built on Fastify.

What You Get

  • A reply.from(source, opts) decorator for forwarding the current request to an upstream server
  • Pluggable transport layer: undici (default), Node’s http/https module, or HTTP/2
  • Automatic retry-on-503 logic for idempotent methods, with a customizable retryDelay handler
  • Header rewriting hooks (rewriteHeaders, rewriteRequestHeaders) plus response/error interception (onResponse, onError)
  • Load-balanced multi-origin forwarding via undici’s BalancedPool when base is given an array of origins
  • Unix domain socket forwarding via unix+http:// and unix+https:// URL schemes
  • Hand-maintained TypeScript type definitions with compile-time type tests (tstyche)

Common Use Cases

  • Building a lightweight API gateway that forwards requests to internal microservices
  • Implementing a backend-for-frontend that load-balances across multiple backend origins
  • Proxying a subset of routes to a new backend during an incremental service migration
  • Bridging HTTP/1.1 clients to an HTTP/2 upstream service, or vice versa

Under The Hood

Architecture index.js decorates the Fastify reply with from(), which delegates to buildRequest() in lib/request.js. That function selects one of three transport implementations — handleHttp2Req, handleUndici, or handleHttp1Req — based on plugin options, all exposed through a uniform request(opts, callback) interface that abstracts over Node’s http/https, undici’s Pool/BalancedPool, and raw http2 sessions. Retry behavior is layered on top via createRequestRetry(), which wraps whichever transport was selected with a getDefaultDelay/retryDelay closure that reschedules failed idempotent requests. URL building and header manipulation are factored into lib/utils.js (buildURL, stripHttp1ConnectionHeaders, filterPseudoHeaders, getConnectionHeaders), and typed FST_REPLY_FROM_* errors are centralized in lib/errors.js on top of @fastify/error. The whole plugin is registered through fastify-plugin as a single encapsulated context with an onClose hook that tears down agents, pools, and HTTP/2 clients — so the transport-selection function in buildRequest is the one abstraction every retry and error-mapping path depends on, since they all assume the same (err, res) callback shape regardless of which transport is active.

Tech Stack Plain CommonJS JavaScript with no build step, targeting Fastify 5.x as a peer via fastify-plugin ^6. Runtime dependencies are undici ^7 (default HTTP client, also used for BalancedPool and ProxyAgent), @fastify/error ^4 for typed errors, toad-cache ^3 (LruMap) for URL-parse caching, fast-querystring ^1 and fast-content-type-parse ^3 for performance-sensitive parsing, and end-of-stream ^1 for HTTP/2 stream cleanup. The test stack uses Node’s built-in test runner under c8 ^12 with a --100 coverage gate, tstyche ^7 for compile-time type tests against the hand-written type definitions, neostandard/eslint ^9 for linting, and nock ^14 plus proxy ^4 for HTTP mocking.

Code Quality The test suite spans over 100 files, each targeting one specific behavior or regression — including named CVE regression tests such as fix-GHSA-2q7r-29rg-6m5h, fix-GHSA-v2v2-hph8-q5xp, and fix-GHSA-v574-6498-x57v — run through Node’s native test runner with c8 enforcing full statement, branch, function, and line coverage. Errors are explicit, typed FST_REPLY_FROM_* classes built with @fastify/error rather than generic throws, and transport-level error codes (ECONNRESET, UND_ERR_SOCKET, UND_ERR_CONNECT_TIMEOUT) are mapped explicitly instead of being swallowed. Naming is consistently camelCase, coverage-ignore comments are used sparingly and only for platform-specific unreachable branches such as Windows unix-socket paths, and both eslint and tstyche run as part of CI.

API Design reply.from(source, opts) is a single decorator with sensible defaults — calling it with no arguments forwards the current request path verbatim to whichever base was configured at registration. Every extension point (rewriteHeaders, rewriteRequestHeaders, onResponse, onError, getUpstream, queryString, retryDelay) is a plain function that can be omitted entirely, so a minimal integration is one fastify.register() call plus one reply.from() call in a route handler. The RetryDelay callback exposes a getDefaultDelay escape hatch so custom retry logic never has to fully reimplement the built-in behavior. Some options interact non-obviously — passing an undici instance, a proxy, or an array base each silently selects a different internal code path — and there is no async/await-native variant of reply.from(), though that mirrors Fastify’s own decorator conventions. Type definitions are comprehensive and kept in sync by hand via module augmentation of FastifyReply.

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