You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
696 lines
20 KiB
696 lines
20 KiB
<h1 align="center">Fastify</h1>
|
|
|
|
## Hooks
|
|
|
|
Hooks are registered with the `fastify.addHook` method and allow you to listen
|
|
to specific events in the application or request/response lifecycle. You have to
|
|
register a hook before the event is triggered, otherwise, the event is lost.
|
|
|
|
By using hooks you can interact directly with the lifecycle of Fastify. There
|
|
are Request/Reply hooks and application hooks:
|
|
|
|
- [Request/Reply Hooks](#requestreply-hooks)
|
|
- [onRequest](#onrequest)
|
|
- [preParsing](#preparsing)
|
|
- [preValidation](#prevalidation)
|
|
- [preHandler](#prehandler)
|
|
- [preSerialization](#preserialization)
|
|
- [onError](#onerror)
|
|
- [onSend](#onsend)
|
|
- [onResponse](#onresponse)
|
|
- [onTimeout](#ontimeout)
|
|
- [Manage Errors from a hook](#manage-errors-from-a-hook)
|
|
- [Respond to a request from a hook](#respond-to-a-request-from-a-hook)
|
|
- [Application Hooks](#application-hooks)
|
|
- [onReady](#onready)
|
|
- [onClose](#onclose)
|
|
- [onRoute](#onroute)
|
|
- [onRegister](#onregister)
|
|
- [Scope](#scope)
|
|
- [Route level hooks](#route-level-hooks)
|
|
- [Diagnostics Channel Hooks](#diagnostics-channel-hooks)
|
|
|
|
**Notice:** the `done` callback is not available when using `async`/`await` or
|
|
returning a `Promise`. If you do invoke a `done` callback in this situation
|
|
unexpected behavior may occur, e.g. duplicate invocation of handlers.
|
|
|
|
## Request/Reply Hooks
|
|
|
|
[Request](./Request.md) and [Reply](./Reply.md) are the core Fastify objects.
|
|
|
|
`done` is the function to continue with the [lifecycle](./Lifecycle.md).
|
|
|
|
It is easy to understand where each hook is executed by looking at the
|
|
[lifecycle page](./Lifecycle.md).
|
|
|
|
Hooks are affected by Fastify's encapsulation, and can thus be applied to
|
|
selected routes. See the [Scopes](#scope) section for more information.
|
|
|
|
There are eight different hooks that you can use in Request/Reply *(in order of
|
|
execution)*:
|
|
|
|
### onRequest
|
|
```js
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
// Some code
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
// Some code
|
|
await asyncMethod()
|
|
})
|
|
```
|
|
|
|
**Notice:** in the [onRequest](#onrequest) hook, `request.body` will always be
|
|
`null`, because the body parsing happens before the
|
|
[preValidation](#prevalidation) hook.
|
|
|
|
### preParsing
|
|
|
|
If you are using the `preParsing` hook, you can transform the request payload
|
|
stream before it is parsed. It receives the request and reply objects as other
|
|
hooks, and a stream with the current request payload.
|
|
|
|
If it returns a value (via `return` or via the callback function), it must
|
|
return a stream.
|
|
|
|
For instance, you can uncompress the request body:
|
|
|
|
```js
|
|
fastify.addHook('preParsing', (request, reply, payload, done) => {
|
|
// Some code
|
|
done(null, newPayload)
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('preParsing', async (request, reply, payload) => {
|
|
// Some code
|
|
await asyncMethod()
|
|
return newPayload
|
|
})
|
|
```
|
|
|
|
**Notice:** in the [preParsing](#preparsing) hook, `request.body` will always be
|
|
`null`, because the body parsing happens before the
|
|
[preValidation](#prevalidation) hook.
|
|
|
|
**Notice:** you should also add a `receivedEncodedLength` property to the
|
|
returned stream. This property is used to correctly match the request payload
|
|
with the `Content-Length` header value. Ideally, this property should be updated
|
|
on each received chunk.
|
|
|
|
**Notice**: The old syntaxes `function(request, reply, done)` and `async
|
|
function(request, reply)` for the parser are still supported but they are
|
|
deprecated.
|
|
|
|
### preValidation
|
|
|
|
If you are using the `preValidation` hook, you can change the payload before it
|
|
is validated. For example:
|
|
|
|
```js
|
|
fastify.addHook('preValidation', (request, reply, done) => {
|
|
request.body = { ...request.body, importantKey: 'randomString' }
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('preValidation', async (request, reply) => {
|
|
const importantKey = await generateRandomString()
|
|
request.body = { ...request.body, importantKey }
|
|
})
|
|
```
|
|
|
|
### preHandler
|
|
```js
|
|
fastify.addHook('preHandler', (request, reply, done) => {
|
|
// some code
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
// Some code
|
|
await asyncMethod()
|
|
})
|
|
```
|
|
### preSerialization
|
|
|
|
If you are using the `preSerialization` hook, you can change (or replace) the
|
|
payload before it is serialized. For example:
|
|
|
|
```js
|
|
fastify.addHook('preSerialization', (request, reply, payload, done) => {
|
|
const err = null
|
|
const newPayload = { wrapped: payload }
|
|
done(err, newPayload)
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('preSerialization', async (request, reply, payload) => {
|
|
return { wrapped: payload }
|
|
})
|
|
```
|
|
|
|
Note: the hook is NOT called if the payload is a `string`, a `Buffer`, a
|
|
`stream`, or `null`.
|
|
|
|
### onError
|
|
```js
|
|
fastify.addHook('onError', (request, reply, error, done) => {
|
|
// Some code
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('onError', async (request, reply, error) => {
|
|
// Useful for custom error logging
|
|
// You should not use this hook to update the error
|
|
})
|
|
```
|
|
This hook is useful if you need to do some custom error logging or add some
|
|
specific header in case of error.
|
|
|
|
It is not intended for changing the error, and calling `reply.send` will throw
|
|
an exception.
|
|
|
|
This hook will be executed only after the `customErrorHandler` has been
|
|
executed, and only if the `customErrorHandler` sends an error back to the user
|
|
*(Note that the default `customErrorHandler` always sends the error back to the
|
|
user)*.
|
|
|
|
**Notice:** unlike the other hooks, pass an error to the `done` function is not
|
|
supported.
|
|
|
|
### onSend
|
|
If you are using the `onSend` hook, you can change the payload. For example:
|
|
|
|
```js
|
|
fastify.addHook('onSend', (request, reply, payload, done) => {
|
|
const err = null;
|
|
const newPayload = payload.replace('some-text', 'some-new-text')
|
|
done(err, newPayload)
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('onSend', async (request, reply, payload) => {
|
|
const newPayload = payload.replace('some-text', 'some-new-text')
|
|
return newPayload
|
|
})
|
|
```
|
|
|
|
You can also clear the payload to send a response with an empty body by
|
|
replacing the payload with `null`:
|
|
|
|
```js
|
|
fastify.addHook('onSend', (request, reply, payload, done) => {
|
|
reply.code(304)
|
|
const newPayload = null
|
|
done(null, newPayload)
|
|
})
|
|
```
|
|
|
|
> You can also send an empty body by replacing the payload with the empty string
|
|
> `''`, but be aware that this will cause the `Content-Length` header to be set
|
|
> to `0`, whereas the `Content-Length` header will not be set if the payload is
|
|
> `null`.
|
|
|
|
Note: If you change the payload, you may only change it to a `string`, a
|
|
`Buffer`, a `stream`, or `null`.
|
|
|
|
|
|
### onResponse
|
|
```js
|
|
fastify.addHook('onResponse', (request, reply, done) => {
|
|
// Some code
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('onResponse', async (request, reply) => {
|
|
// Some code
|
|
await asyncMethod()
|
|
})
|
|
```
|
|
|
|
The `onResponse` hook is executed when a response has been sent, so you will not
|
|
be able to send more data to the client. It can however be useful for sending
|
|
data to external services, for example, to gather statistics.
|
|
|
|
### onTimeout
|
|
|
|
```js
|
|
fastify.addHook('onTimeout', (request, reply, done) => {
|
|
// Some code
|
|
done()
|
|
})
|
|
```
|
|
Or `async/await`:
|
|
```js
|
|
fastify.addHook('onTimeout', async (request, reply) => {
|
|
// Some code
|
|
await asyncMethod()
|
|
})
|
|
```
|
|
`onTimeout` is useful if you need to monitor the request timed out in your
|
|
service (if the `connectionTimeout` property is set on the Fastify instance).
|
|
The `onTimeout` hook is executed when a request is timed out and the HTTP socket
|
|
has been hanged up. Therefore, you will not be able to send data to the client.
|
|
|
|
|
|
### Manage Errors from a hook
|
|
If you get an error during the execution of your hook, just pass it to `done()`
|
|
and Fastify will automatically close the request and send the appropriate error
|
|
code to the user.
|
|
|
|
```js
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
done(new Error('Some error'))
|
|
})
|
|
```
|
|
|
|
If you want to pass a custom error code to the user, just use `reply.code()`:
|
|
```js
|
|
fastify.addHook('preHandler', (request, reply, done) => {
|
|
reply.code(400)
|
|
done(new Error('Some error'))
|
|
})
|
|
```
|
|
*The error will be handled by [`Reply`](./Reply.md#errors).*
|
|
|
|
Or if you're using `async/await` you can just throw an error:
|
|
```js
|
|
fastify.addHook('onResponse', async (request, reply) => {
|
|
throw new Error('Some error')
|
|
})
|
|
```
|
|
|
|
### Respond to a request from a hook
|
|
|
|
If needed, you can respond to a request before you reach the route handler, for
|
|
example when implementing an authentication hook. Replying from a hook implies
|
|
that the hook chain is __stopped__ and the rest of the hooks and handlers are
|
|
not executed. If the hook is using the callback approach, i.e. it is not an
|
|
`async` function or it returns a `Promise`, it is as simple as calling
|
|
`reply.send()` and avoiding calling the callback. If the hook is `async`,
|
|
`reply.send()` __must__ be called _before_ the function returns or the promise
|
|
resolves, otherwise, the request will proceed. When `reply.send()` is called
|
|
outside of the promise chain, it is important to `return reply` otherwise the
|
|
request will be executed twice.
|
|
|
|
It is important to __not mix callbacks and `async`/`Promise`__, otherwise the
|
|
hook chain will be executed twice.
|
|
|
|
If you are using `onRequest` or `preHandler` use `reply.send`.
|
|
|
|
```js
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
reply.send('Early response')
|
|
})
|
|
|
|
// Works with async functions too
|
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
await something()
|
|
reply.send({ hello: 'world' })
|
|
return reply // optional in this case, but it is a good practice
|
|
})
|
|
```
|
|
|
|
If you want to respond with a stream, you should avoid using an `async` function
|
|
for the hook. If you must use an `async` function, your code will need to follow
|
|
the pattern in
|
|
[test/hooks-async.js](https://github.com/fastify/fastify/blob/94ea67ef2d8dce8a955d510cd9081aabd036fa85/test/hooks-async.js#L269-L275).
|
|
|
|
```js
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
const stream = fs.createReadStream('some-file', 'utf8')
|
|
reply.send(stream)
|
|
})
|
|
```
|
|
|
|
If you are sending a response without `await` on it, make sure to always `return
|
|
reply`:
|
|
|
|
```js
|
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
setImmediate(() => { reply.send('hello') })
|
|
|
|
// This is needed to signal the handler to wait for a response
|
|
// to be sent outside of the promise chain
|
|
return reply
|
|
})
|
|
|
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
// the @fastify/static plugin will send a file asynchronously,
|
|
// so we should return reply
|
|
reply.sendFile('myfile')
|
|
return reply
|
|
})
|
|
```
|
|
|
|
## Application Hooks
|
|
|
|
You can hook into the application-lifecycle as well.
|
|
|
|
- [onReady](#onready)
|
|
- [onClose](#onclose)
|
|
- [onRoute](#onroute)
|
|
- [onRegister](#onregister)
|
|
|
|
### onReady
|
|
Triggered before the server starts listening for requests and when `.ready()` is
|
|
invoked. It cannot change the routes or add new hooks. Registered hook functions
|
|
are executed serially. Only after all `onReady` hook functions have completed
|
|
will the server start listening for requests. Hook functions accept one
|
|
argument: a callback, `done`, to be invoked after the hook function is complete.
|
|
Hook functions are invoked with `this` bound to the associated Fastify instance.
|
|
|
|
```js
|
|
// callback style
|
|
fastify.addHook('onReady', function (done) {
|
|
// Some code
|
|
const err = null;
|
|
done(err)
|
|
})
|
|
|
|
// or async/await style
|
|
fastify.addHook('onReady', async function () {
|
|
// Some async code
|
|
await loadCacheFromDatabase()
|
|
})
|
|
```
|
|
|
|
### onClose
|
|
<a id="on-close"></a>
|
|
|
|
Triggered when `fastify.close()` is invoked to stop the server. It is useful
|
|
when [plugins](./Plugins.md) need a "shutdown" event, for example, to close an
|
|
open connection to a database.
|
|
|
|
The hook function takes the Fastify instance as a first argument,
|
|
and a `done` callback for synchronous hook functions.
|
|
```js
|
|
// callback style
|
|
fastify.addHook('onClose', (instance, done) => {
|
|
// Some code
|
|
done()
|
|
})
|
|
|
|
// or async/await style
|
|
fastify.addHook('onClose', async (instance) => {
|
|
// Some async code
|
|
await closeDatabaseConnections()
|
|
})
|
|
```
|
|
|
|
### onRoute
|
|
<a id="on-route"></a>
|
|
|
|
Triggered when a new route is registered. Listeners are passed a `routeOptions`
|
|
object as the sole parameter. The interface is synchronous, and, as such, the
|
|
listeners are not passed a callback. This hook is encapsulated.
|
|
```js
|
|
fastify.addHook('onRoute', (routeOptions) => {
|
|
//Some code
|
|
routeOptions.method
|
|
routeOptions.schema
|
|
routeOptions.url // the complete URL of the route, it will include the prefix if any
|
|
routeOptions.path // `url` alias
|
|
routeOptions.routePath // the URL of the route without the prefix
|
|
routeOptions.bodyLimit
|
|
routeOptions.logLevel
|
|
routeOptions.logSerializers
|
|
routeOptions.prefix
|
|
})
|
|
```
|
|
|
|
If you are authoring a plugin and you need to customize application routes, like
|
|
modifying the options or adding new route hooks, this is the right place.
|
|
|
|
```js
|
|
fastify.addHook('onRoute', (routeOptions) => {
|
|
function onPreSerialization(request, reply, payload, done) {
|
|
// Your code
|
|
done(null, payload)
|
|
}
|
|
// preSerialization can be an array or undefined
|
|
routeOptions.preSerialization = [...(routeOptions.preSerialization || []), onPreSerialization]
|
|
})
|
|
```
|
|
|
|
### onRegister
|
|
<a id="on-register"></a>
|
|
|
|
Triggered when a new plugin is registered and a new encapsulation context is
|
|
created. The hook will be executed **before** the registered code.
|
|
|
|
This hook can be useful if you are developing a plugin that needs to know when a
|
|
plugin context is formed, and you want to operate in that specific context, thus
|
|
this hook is encapsulated.
|
|
|
|
**Note:** This hook will not be called if a plugin is wrapped inside
|
|
[`fastify-plugin`](https://github.com/fastify/fastify-plugin).
|
|
```js
|
|
fastify.decorate('data', [])
|
|
|
|
fastify.register(async (instance, opts) => {
|
|
instance.data.push('hello')
|
|
console.log(instance.data) // ['hello']
|
|
|
|
instance.register(async (instance, opts) => {
|
|
instance.data.push('world')
|
|
console.log(instance.data) // ['hello', 'world']
|
|
}, { prefix: '/hola' })
|
|
}, { prefix: '/ciao' })
|
|
|
|
fastify.register(async (instance, opts) => {
|
|
console.log(instance.data) // []
|
|
}, { prefix: '/hello' })
|
|
|
|
fastify.addHook('onRegister', (instance, opts) => {
|
|
// Create a new array from the old one
|
|
// but without keeping the reference
|
|
// allowing the user to have encapsulated
|
|
// instances of the `data` property
|
|
instance.data = instance.data.slice()
|
|
|
|
// the options of the new registered instance
|
|
console.log(opts.prefix)
|
|
})
|
|
```
|
|
|
|
## Scope
|
|
<a id="scope"></a>
|
|
|
|
Except for [onClose](#onclose), all hooks are encapsulated. This means that you
|
|
can decide where your hooks should run by using `register` as explained in the
|
|
[plugins guide](../Guides/Plugins-Guide.md). If you pass a function, that
|
|
function is bound to the right Fastify context and from there you have full
|
|
access to the Fastify API.
|
|
|
|
```js
|
|
fastify.addHook('onRequest', function (request, reply, done) {
|
|
const self = this // Fastify context
|
|
done()
|
|
})
|
|
```
|
|
|
|
Note that the Fastify context in each hook is the same as the plugin where the
|
|
route was registered, for example:
|
|
|
|
```js
|
|
fastify.addHook('onRequest', async function (req, reply) {
|
|
if (req.raw.url === '/nested') {
|
|
assert.strictEqual(this.foo, 'bar')
|
|
} else {
|
|
assert.strictEqual(this.foo, undefined)
|
|
}
|
|
})
|
|
|
|
fastify.get('/', async function (req, reply) {
|
|
assert.strictEqual(this.foo, undefined)
|
|
return { hello: 'world' }
|
|
})
|
|
|
|
fastify.register(async function plugin (fastify, opts) {
|
|
fastify.decorate('foo', 'bar')
|
|
|
|
fastify.get('/nested', async function (req, reply) {
|
|
assert.strictEqual(this.foo, 'bar')
|
|
return { hello: 'world' }
|
|
})
|
|
})
|
|
```
|
|
|
|
Warn: if you declare the function with an [arrow
|
|
function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions),
|
|
the `this` will not be Fastify, but the one of the current scope.
|
|
|
|
|
|
## Route level hooks
|
|
<a id="route-hooks"></a>
|
|
|
|
You can declare one or more custom lifecycle hooks ([onRequest](#onrequest),
|
|
[onResponse](#onresponse), [preParsing](#preparsing),
|
|
[preValidation](#prevalidation), [preHandler](#prehandler),
|
|
[preSerialization](#preserialization), [onSend](#onsend),
|
|
[onTimeout](#ontimeout), and [onError](#onerror)) hook(s) that will be
|
|
**unique** for the route. If you do so, those hooks are always executed as the
|
|
last hook in their category.
|
|
|
|
This can be useful if you need to implement authentication, where the
|
|
[preParsing](#preparsing) or [preValidation](#prevalidation) hooks are exactly
|
|
what you need. Multiple route-level hooks can also be specified as an array.
|
|
|
|
```js
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('onResponse', (request, reply, done) => {
|
|
// your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('preParsing', (request, reply, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('preValidation', (request, reply, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('preHandler', (request, reply, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('preSerialization', (request, reply, payload, done) => {
|
|
// Your code
|
|
done(null, payload)
|
|
})
|
|
|
|
fastify.addHook('onSend', (request, reply, payload, done) => {
|
|
// Your code
|
|
done(null, payload)
|
|
})
|
|
|
|
fastify.addHook('onTimeout', (request, reply, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('onError', (request, reply, error, done) => {
|
|
// Your code
|
|
done()
|
|
})
|
|
|
|
fastify.route({
|
|
method: 'GET',
|
|
url: '/',
|
|
schema: { ... },
|
|
onRequest: function (request, reply, done) {
|
|
// This hook will always be executed after the shared `onRequest` hooks
|
|
done()
|
|
},
|
|
onResponse: function (request, reply, done) {
|
|
// this hook will always be executed after the shared `onResponse` hooks
|
|
done()
|
|
},
|
|
preParsing: function (request, reply, done) {
|
|
// This hook will always be executed after the shared `preParsing` hooks
|
|
done()
|
|
},
|
|
preValidation: function (request, reply, done) {
|
|
// This hook will always be executed after the shared `preValidation` hooks
|
|
done()
|
|
},
|
|
preHandler: function (request, reply, done) {
|
|
// This hook will always be executed after the shared `preHandler` hooks
|
|
done()
|
|
},
|
|
// // Example with an array. All hooks support this syntax.
|
|
//
|
|
// preHandler: [function (request, reply, done) {
|
|
// // This hook will always be executed after the shared `preHandler` hooks
|
|
// done()
|
|
// }],
|
|
preSerialization: (request, reply, payload, done) => {
|
|
// This hook will always be executed after the shared `preSerialization` hooks
|
|
done(null, payload)
|
|
},
|
|
onSend: (request, reply, payload, done) => {
|
|
// This hook will always be executed after the shared `onSend` hooks
|
|
done(null, payload)
|
|
},
|
|
onTimeout: (request, reply, done) => {
|
|
// This hook will always be executed after the shared `onTimeout` hooks
|
|
done()
|
|
},
|
|
onError: (request, reply, error, done) => {
|
|
// This hook will always be executed after the shared `onError` hooks
|
|
done()
|
|
},
|
|
handler: function (request, reply) {
|
|
reply.send({ hello: 'world' })
|
|
}
|
|
})
|
|
```
|
|
|
|
**Note**: both options also accept an array of functions.
|
|
|
|
## Diagnostics Channel Hooks
|
|
|
|
> **Note:** The `diagnostics_channel` is currently experimental on Node.js, so
|
|
> its API is subject to change even in semver-patch releases of Node.js. For
|
|
> versions of Node.js supported by Fastify where `diagnostics_channel` is
|
|
> unavailable, the hook will use the
|
|
> [polyfill](https://www.npmjs.com/package/diagnostics_channel) if it is
|
|
> available. Otherwise this feature will not be present.
|
|
|
|
Currently, one
|
|
[`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) publish
|
|
event, `'fastify.initialization'`, happens at initialization time. The Fastify
|
|
instance is passed into the hook as a property of the object passed in. At this
|
|
point, the instance can be interacted with to add hooks, plugins, routes or any
|
|
other sort of modification.
|
|
|
|
For example, a tracing package might do something like the following (which is,
|
|
of course, a simplification). This would be in a file loaded in the
|
|
initialization of the tracking package, in the typical "require instrumentation
|
|
tools first" fashion.
|
|
|
|
```js
|
|
const tracer = /* retrieved from elsehwere in the package */
|
|
const dc = require('diagnostics_channel')
|
|
const channel = dc.channel('fastify.initialization')
|
|
const spans = new WeakMap()
|
|
|
|
channel.subscribe(function ({ fastify }) {
|
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
const span = tracer.startSpan('fastify.request')
|
|
spans.set(request, span)
|
|
done()
|
|
})
|
|
|
|
fastify.addHook('onResponse', (request, reply, done) => {
|
|
const span = spans.get(request)
|
|
span.finish()
|
|
done()
|
|
})
|
|
})
|
|
```
|