@fastify/http-proxy

repository·main·Indexed 18 days ago

https://github.com/fastify/fastify-http-proxy

A Fastify plugin for forwarding incoming HTTP requests to an upstream server, built on top of @fastify/reply-from. It supports API gateway implementation, CORS avoidance, URL rewriting via prefix and rewritePrefix, and partial WebSocket proxying with connection monitoring and recovery mechanisms. Compatible with Fastify 5.x (plugin version 11.6.0) and Fastify 4.x (plugin version 9.x).

Tokens
4.7K
Snippets
16
Records
23
Agent score
63%

What's inside @fastify/http-proxy

  1. How proxy connection monitoring and recovery works

    main

    The proxy maintains connection resilience using a ping/pong mechanism to monitor the target service.

    • Detection: If a pong response from the target does not arrive within the expected timeframe, the proxy considers the connection failed, closes it, and initiates a reconnection attempt. This handles cases where the target is unresponsive (e.g., due to an event loop block) even if the TCP connection is technically still open.
    • Recovery: The proxy detects connection loss regardless of whether the target service crashes gracefully or abruptly, and automatically attempts to reconnect.
    • Client Stability: The connection between the client and the proxy remains stable even when the connection between the proxy and the target is being recovered.

    This mechanism is useful for integrating with services that may experience intermittent freezes or restarts.

  2. Handle data loss during reconnection using hooks

    main

    When a reconnection occurs, there is a risk of data loss for messages sent during the disconnection window. The proxy provides hooks that allow you to implement custom logic to ensure message integrity.

    Depending on your target service, you can use these hooks to:

    • GraphQL subscriptions: Resend the subscription starting from the last received message.
    • Message brokers: Resend messages starting from the last successfully processed message.
    • General use: Resend messages from the last successful ping/pong to ensure the target receives all data, noting that this may result in duplicate messages (at-least-once delivery).
  3. Run the Reconnection Example

    main

    To test the reconnection and resilience features, you can run the provided example which simulates an unstable target service (slow starts, event loop blocks, and crashes). You must run three separate components in order: the unstable target, the proxy, and finally the client.

    1. Start the unstable target
    2. Start the proxy
    3. Start the client
    # Run the unstable target
    cd examples/reconnection/unstable-target
    npm run unstable
    
    # Run the proxy
    cd examples/reconnection/proxy
    npm run start
    
    # Run the client
    cd examples/reconnection/client
    npm run start
  4. Enable WebSocket proxying

    main

    To proxy WebSockets, set the websocket: true option in the plugin configuration. When enabled, the plugin handles the upgrade event and manages the connection between the client and the upstream WebSocket server.

    If websocket: true is used, you can also provide wsHooks to intercept WebSocket lifecycle events.

    fastify.register(proxy, {
      upstream: 'ws://localhost:8080',
      prefix: '/ws',
      websocket: true,
      wsHooks: {
        onConnect: (context, source, target) => { /* ... */ },
        onDisconnect: (context, source) => { /* ... */ },
        onIncomingMessage: (context, source, target, { data, binary }) => { /* ... */ },
        onOutgoingMessage: (context, source, target, { data, binary }) => { /* ... */ },
        onPong: (context, source, target) => { /* ... */ }
      }
    })
  5. Handle non-JSON payloads with `proxyPayloads`

    main

    By default, the plugin streams non-JSON payloads directly to the destination. If you set proxyPayloads: false, you can access the body, but direct pass-through is disabled. In this case, you must manually parse and proxy the payload.

    Example for application/xml:

    fastify.addContentTypeParser('application/xml', (req, done) => {
      const parsedBody = parsingCode(req);
      done(null, parsedBody);
    });
  6. Configure WebSocket proxying

    main

    The plugin has partial support for forwarding WebSockets. To enable it, set websocket: true.

    Key options:

    • wsUpstream: The target WebSocket URL (supports https:// and wss://). If not specified, it uses the upstream value.
    • wsServerOptions: Options passed to new ws.Server().
    • wsClientOptions: Options passed to the WebSocket constructor. Supports rewriteRequestHeaders(headers, request) to modify headers before opening the connection.
    • wsReconnect (Experimental): Enables automatic reconnection for broken connections.
  7. Configure the proxy prefix and URL rewriting

    main

    Use prefix to mount the plugin on a specific path. All requests starting with this prefix will be proxied. Parametric paths are supported using a colon (e.g., /:id). By default, the prefix is removed from the URL when forwarding.

    Use rewritePrefix to change the prefix to a different string during the proxy process. The default value is '' (empty string).

    // /api/abc will be proxied to http://api-upstream.com/api2/xyz
    fastify.register(proxy, {
      upstream: `http://api-upstream.com`,
      prefix: '/api',
      rewritePrefix: '/api2/',
      preRewrite (url, params, prefix) {
        return url.replace('abc', 'xyz');
      }
    })
  8. Register the @fastify/http-proxy plugin

    main

    To use the proxy, register the @fastify/http-proxy plugin with your Fastify instance. You must provide an upstream URL which specifies the target server where requests will be forwarded. You can also define specific routes, httpMethods, and preHandler hooks.

    By default, the plugin registers routes for ['/', '/*'] and handles standard HTTP methods. It uses @fastify/reply-from internally to manage the proxying logic.

    const Fastify = require('fastify')
    const proxy = require('@fastify/http-proxy')
    
    const fastify = Fastify()
    
    fastify.register(proxy, { 
      upstream: 'http://localhost:8080', 
      prefix: '/proxy' 
    })
    
    fastify.listen({ port: 3000 })
  9. Basic usage of @fastify/http-proxy

    main

    Register the plugin with an upstream URL to forward requests. You can optionally specify a prefix to determine which incoming requests are intercepted and a http2 boolean to enable/disable HTTP2 support.

    const Fastify = require('fastify');
    const server = Fastify();
    
    server.register(require('@fastify/http-proxy'), {
      upstream: 'http://my-api.example.com',
      prefix: '/api', // optional
      http2: false, // optional
    });
    
    server.listen({ port: 3000 });
  10. Track request-id across upstreams

    main

    To maintain request traceability, you can pipe a request-id to the upstream server by using the replyOptions.rewriteRequestHeaders function. This allows you to inject custom headers (like a UUID generated by hyperid) into the proxied request.

    const Fastify = require('fastify');
    const proxy = require('@fastify/http-proxy');
    const hyperid = require('hyperid');
    
    const server = Fastify();
    const uuid = hyperid();
    
    server.register(proxy, {
      upstream: 'http://localhost:4001',
      replyOptions: {
        rewriteRequestHeaders: (originalReq, headers) => ({
          ...headers,
          'request-id': uuid(),
        }),
      },
    });
    
    server.listen({ port: 3000 });