Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,21 @@ Rewrite the prefix to the specified string. Default: `''`.

A `preHandler` to be applied on all routes. Useful for performing actions before the proxy is executed (e.g. check for authentication).

### `handler`

A function to replace the default HTTP proxy handler. It receives the request, reply, rewritten destination, and reply options.

By default, the handler calls `reply.from(dest, options)`. Use this option to customize the response handling while keeping the proxy URL rewriting behavior.

```javascript
fastify.register(proxy, {
upstream: 'http://api-upstream.com',
async handler (request, reply, dest, options) {
return reply.from(dest, options)
}
})
```

### `proxyPayloads`

When this option is `false`, you will be able to access the body but it will also disable direct pass through of the payload. As a result, it is left up to the implementation to properly parse and proxy the payload correctly.
Expand Down
6 changes: 5 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,10 @@ function generateRewritePrefix (prefix, opts) {
async function fastifyHttpProxy (fastify, opts) {
opts = validateOptions(opts)

const replyHandler = opts.handler ?? function replyHandler (_, reply, dest, options) {
return reply.from(dest, options)
}

const preHandler = opts.preHandler || opts.beforeHandler
const preRewrite = typeof opts.preRewrite === 'function' ? opts.preRewrite : noopPreRewrite
const rewritePrefix = generateRewritePrefix(fastify.prefix, opts)
Expand Down Expand Up @@ -631,7 +635,7 @@ async function fastifyHttpProxy (fastify, opts) {
} /* c8 ignore stop */
return
}
reply.from(dest, options)
return replyHandler(request, reply, dest, options)
}

fastify.decorateReply('fromParameters', fromParameters)
Expand Down
4 changes: 4 additions & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ function validateOptions (options) {
throw new Error('upstream must be specified')
}

if (options.handler !== undefined && typeof options.handler !== 'function') {
throw new Error('handler must be a function')
}

if (options.wsReconnect) {
const wsReconnect = options.wsReconnect

Expand Down
3 changes: 3 additions & 0 deletions test/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ test('validateOptions', (t) => {

assert.throws(() => validateOptions({}), /upstream must be specified/)

assert.throws(() => validateOptions({ ...requiredOptions, handler: '1' }), /handler must be a function/)
assert.doesNotThrow(() => validateOptions({ ...requiredOptions, handler: () => { } }))

assert.throws(() => validateOptions({ ...requiredOptions, wsReconnect: { pingInterval: -1 } }), /wsReconnect.pingInterval must be a non-negative number/)
assert.throws(() => validateOptions({ ...requiredOptions, wsReconnect: { pingInterval: '1' } }), /wsReconnect.pingInterval must be a non-negative number/)
assert.doesNotThrow(() => validateOptions({ ...requiredOptions, wsReconnect: { pingInterval: 1 } }))
Expand Down
58 changes: 58 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,64 @@ async function run () {
}
})

test('custom handler can handle response', async t => {
const proxyServer = Fastify()

proxyServer.register(proxy, {
upstream: `http://localhost:${origin.server.address().port}`,
prefix: '/api',
rewritePrefix: '/api2',
replyOptions: {
contentType: 'text/plain'
},
handler (request, reply, dest, options) {
t.assert.strictEqual(request.url, '/api/a')
t.assert.strictEqual(dest, '/api2/a')
t.assert.strictEqual(options.contentType, 'text/plain')

return reply.send({ dest })
}
})

await proxyServer.listen({ port: 0 })

t.after(() => {
proxyServer.close()
})

const response = await fetch(`http://localhost:${proxyServer.server.address().port}/api/a`)
const body = await response.json()

t.assert.strictEqual(response.status, 200)
t.assert.deepStrictEqual(body, { dest: '/api2/a' })
})

test('custom handler can delegate to reply.from()', async t => {
const proxyServer = Fastify()

proxyServer.register(proxy, {
upstream: `http://localhost:${origin.server.address().port}`,
prefix: '/api',
rewritePrefix: '/api2',
handler (_request, reply, dest, options) {
t.assert.strictEqual(dest, '/api2/a')
return reply.from(dest, options)
}
})

await proxyServer.listen({ port: 0 })

t.after(() => {
proxyServer.close()
})

const response = await fetch(`http://localhost:${proxyServer.server.address().port}/api/a`)
const body = await response.text()

t.assert.strictEqual(response.status, 200)
t.assert.strictEqual(body, 'this is /api2/a')
})

test('rewritePrefix', async t => {
const proxyServer = Fastify()

Expand Down
8 changes: 8 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ type ProxyPreValidationHookHandler = (
done: Parameters<preValidationHookHandler>[2]
) => void

type ProxyHandler = (
request: FastifyRequest,
reply: FastifyReplyWithFromParameters,
dest: string,
options: FastifyReplyFromHooks
) => unknown

interface WebSocketHooks {
onConnect?: (context: { log: Logger }, source: WebSocket, target: WebSocket) => void;
onDisconnect?: (context: { log: Logger }, source: WebSocket) => void;
Expand Down Expand Up @@ -97,6 +104,7 @@ declare namespace fastifyHttpProxy {
preHandler?: ProxyPreHandlerHookHandler;
beforeHandler?: ProxyPreHandlerHookHandler;
preValidation?: ProxyPreValidationHookHandler;
handler?: ProxyHandler;
preRewrite?: ProxyPreRewriteHookHandler;
config?: Object;
replyOptions?: FastifyReplyFromHooks;
Expand Down
8 changes: 8 additions & 0 deletions types/index.tst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fastify, {
type RequestGenericInterface,
} from 'fastify'
import { expect } from 'tstyche'
import { type FastifyReplyFromHooks } from '@fastify/reply-from'
import fastifyHttpProxy from '..'

const app = fastify()
Expand Down Expand Up @@ -59,6 +60,13 @@ app.register(fastifyHttpProxy, {
expect(result.options).type.toBe<unknown>()
expect(result.url).type.toBe<string>()
},
handler: (request, reply, dest, options) => {
expect(request).type.toBe<FastifyRequest>()
expect(reply.raw).type.toBe<RawReplyDefaultExpression>()
expect(dest).type.toBe<string>()
expect(options).type.toBe<FastifyReplyFromHooks>()
return reply.from(dest, options)
},
preRewrite: (url, params, prefix): string => {
expect(url).type.toBe<string>()
expect(params).type.toBe<unknown>()
Expand Down
Loading