node.jsfastifyhttp-proxy-middleware

Using an async preHandler function on @fastify/http-proxy creates a FastifyError: Reply was already sent


I have the current code for proxying.

const fastify = require('fastify')({ logger: true })
const fastifyHttpProxy = require('@fastify/http-proxy')

fastify.register(fastifyHttpProxy, {
  upstream: ...,
  prefix: '/',
  http2: false,
  rewritePrefix: ...,
  preValidation: (request, reply, done) => {
    // Some validation code
    done()
  },
  preHandler: (request, reply, next) => {
    // Some handler
    request.raw.url = `${request.raw.url}/some-path`
    next()
  }
})

fastify.listen({ port: 3000 }, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`Server listening on ${address}`)
})

But when I add an async operation to my preHandler function, it returns this error.

err":{"type":"FastifyError","message":"Reply was already sent, did you forget to \"return reply\" in \"//app1/index.html\" (GET)?","stack":"FastifyError: Reply was already sent, did you forget to \"return reply\" in \"//app1/index.html\" (GET)?\n    at Reply.send 

To reproduce, just add a simple Promise.resolve.

const fastify = require('fastify')({ logger: true })
const fastifyHttpProxy = require('@fastify/http-proxy')

fastify.register(fastifyHttpProxy, {
  upstream: ...,
  prefix: '/',
  http2: false,
  rewritePrefix: ...,
  preValidation: (request, reply, done) => {
    // Some validation code
    done()
  },
  preHandler: async (request, reply, next) => {
    // Some handler, e.g. adding path
    await Promise.resolve(1)
    request.raw.url = `${request.raw.url}/some-path`
    next()
  }
})

fastify.listen({ port: 3000 }, (err, address) => {
  if (err) {
    fastify.log.error(err)
    process.exit(1)
  }
  fastify.log.info(`Server listening on ${address}`)
})

What is causing this and how can I perform async operations in preHandler (I want to use it to do some authentication)?


Solution

  • You need to remove the next callback in the preHandler function to make it works:

      preHandler: async (request, reply) => {
        // Some handler, e.g. adding path
        await Promise.resolve(1)
        request.raw.url = `${request.raw.url}/some-path`
      }
    

    The API is documented here: https://fastify.dev/docs/latest/Reference/Hooks/#prehandler

    meanwhile the "do not mix async/await and callback" is explained at: https://fastify.dev/docs/latest/Reference/Hooks/#respond-to-a-request-from-a-hook

    It is important to not mix callbacks and async/Promise, otherwise the hook chain will be executed twice.

    Plus: I wrote the only Fastify book out there, checkout my profile if you need it