UNPKG

13.2 kBMarkdownView Raw
1<h1 align="center">Fastify</h1>
2
3## Hooks
4
5Hooks 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.
6
7## Request/Response Hooks
8
9By using the hooks you can interact directly inside the lifecycle of Fastify. There are seven different Hooks that you can use *(in order of execution)*:
10- `'onRequest'`
11- `'preParsing'`
12- `'preValidation'`
13- `'preHandler'`
14- `'preSerialization'`
15- `'onError'`
16- `'onSend'`
17- `'onResponse'`
18
19Example:
20```js
21fastify.addHook('onRequest', (request, reply, next) => {
22 // some code
23 next()
24})
25
26fastify.addHook('preParsing', (request, reply, next) => {
27 // some code
28 next()
29})
30
31fastify.addHook('preValidation', (request, reply, next) => {
32 // some code
33 next()
34})
35
36fastify.addHook('preHandler', (request, reply, next) => {
37 // some code
38 next()
39})
40
41fastify.addHook('preSerialization', (request, reply, payload, next) => {
42 // some code
43 next()
44})
45
46fastify.addHook('onError', (request, reply, error, next) => {
47 // some code
48 next()
49})
50
51fastify.addHook('onSend', (request, reply, payload, next) => {
52 // some code
53 next()
54})
55
56fastify.addHook('onResponse', (request, reply, next) => {
57 // some code
58 next()
59})
60```
61Or `async/await`
62```js
63fastify.addHook('onRequest', async (request, reply) => {
64 // some code
65 await asyncMethod()
66 // error occurred
67 if (err) {
68 throw new Error('some errors occurred.')
69 }
70 return
71})
72
73fastify.addHook('preParsing', async (request, reply) => {
74 // some code
75 await asyncMethod()
76 // error occurred
77 if (err) {
78 throw new Error('some errors occurred.')
79 }
80 return
81})
82
83fastify.addHook('preValidation', async (request, reply) => {
84 // some code
85 await asyncMethod()
86 // error occurred
87 if (err) {
88 throw new Error('some errors occurred.')
89 }
90 return
91})
92
93fastify.addHook('preHandler', async (request, reply) => {
94 // some code
95 await asyncMethod()
96 // error occurred
97 if (err) {
98 throw new Error('some errors occurred.')
99 }
100 return
101})
102
103fastify.addHook('preSerialization', async (request, reply, payload) => {
104 // some code
105 await asyncMethod()
106 // error occurred
107 if (err) {
108 throw new Error('some errors occurred.')
109 }
110 return payload
111})
112
113fastify.addHook('onError', async (request, reply, error) => {
114 // useful for custom error logging
115 // you should not use this hook to update the error
116})
117
118fastify.addHook('onSend', async (request, reply, payload) => {
119 // some code
120 await asyncMethod()
121 // error occurred
122 if (err) {
123 throw new Error('some errors occurred.')
124 }
125 return
126})
127
128fastify.addHook('onResponse', async (request, reply) => {
129 // some code
130 await asyncMethod()
131 // error occurred
132 if (err) {
133 throw new Error('some errors occurred.')
134 }
135 return
136})
137```
138
139**Notice:** the `next` callback is not available when using `async`/`await` or returning a `Promise`. If you do invoke a `next` callback in this situation unexpected behavior may occur, e.g. duplicate invocation of handlers.
140
141**Notice:** in the `onRequest` and `preValidation` hooks, `request.body` will always be `null`, because the body parsing happens before the `preHandler` hook.
142
143[Request](https://github.com/fastify/fastify/blob/master/docs/Request.md) and [Reply](https://github.com/fastify/fastify/blob/master/docs/Reply.md) are the core Fastify objects.<br/>
144`next` is the function to continue with the [lifecycle](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md).
145
146It is pretty easy to understand where each hook is executed by looking at the [lifecycle page](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md).<br>
147Hooks are affected by Fastify's encapsulation, and can thus be applied to selected routes. See the [Scopes](#scope) section for more information.
148
149If you get an error during the execution of your hook, just pass it to `next()` and Fastify will automatically close the request and send the appropriate error code to the user.
150
151```js
152fastify.addHook('onRequest', (request, reply, next) => {
153 next(new Error('some error'))
154})
155```
156
157If you want to pass a custom error code to the user, just use `reply.code()`:
158```js
159fastify.addHook('preHandler', (request, reply, next) => {
160 reply.code(400)
161 next(new Error('some error'))
162})
163```
164
165*The error will be handled by [`Reply`](https://github.com/fastify/fastify/blob/master/docs/Reply.md#errors).*
166
167#### The `onError` Hook
168
169This hook is useful if you need to do some custom error logging or add some specific header in case of error.<br/>
170It is not intended for changing the error, and calling `reply.send` will throw an exception.<br/>
171This hook will be executed only after the `customErrorHandler` has been executed, and only if the `customErrorHandler` sends back an error to the user *(Note that the default `customErrorHandler` always send back the error to the user)*.<br/>
172**Notice:** unlike the other hooks, pass an error to the `next` function is not supported.
173
174```js
175fastify.addHook('onError', (request, reply, error, next) => {
176 // apm stands for Application Performance Monitoring
177 apm.sendError(error)
178 next()
179})
180
181// Or async
182fastify.addHook('onError', async (request, reply, error) => {
183 // apm stands for Application Performance Monitoring
184 apm.sendError(error)
185})
186```
187
188#### The `preSerialization` Hook
189
190If you are using the `preSerialization` hook, you can change (or replace) the payload before it is serialized. For example:
191
192```js
193fastify.addHook('preSerialization', (request, reply, payload, next) => {
194 var err = null;
195 var newPayload = {wrapped: payload }
196 next(err, newPayload)
197})
198
199// Or async
200fastify.addHook('preSerialization', async (request, reply, payload) => {
201 return {wrapped: payload }
202})
203```
204
205Note: the hook is NOT called if the payload is a `string`, a `Buffer`, a `stream`, or `null`.
206
207#### The `onSend` Hook
208
209If you are using the `onSend` hook, you can change the payload. For example:
210
211```js
212fastify.addHook('onSend', (request, reply, payload, next) => {
213 var err = null;
214 var newPayload = payload.replace('some-text', 'some-new-text')
215 next(err, newPayload)
216})
217
218// Or async
219fastify.addHook('onSend', async (request, reply, payload) => {
220 var newPayload = payload.replace('some-text', 'some-new-text')
221 return newPayload
222})
223```
224
225You can also clear the payload to send a response with an empty body by replacing the payload with `null`:
226
227```js
228fastify.addHook('onSend', (request, reply, payload, next) => {
229 reply.code(304)
230 const newPayload = null
231 next(null, newPayload)
232})
233```
234
235> 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`.
236
237Note: If you change the payload, you may only change it to a `string`, a `Buffer`, a `stream`, or `null`.
238
239#### The `onResponse` Hook
240The `onResponse` hook is executed when a response has been sent, so you will not be able to send more data to the client, however you can use this hook to send some data to an external service or elaborate some statistics.
241
242### Respond to a request from a hook
243If needed, you can respond to a request before you reach the route handler. An example could be an authentication hook. If you are using `onRequest` or `preHandler` use `reply.send`; if you are using a middleware, `res.end`.
244
245```js
246fastify.addHook('onRequest', (request, reply, next) => {
247 reply.send('early response')
248})
249
250// Works with async functions too
251fastify.addHook('preHandler', async (request, reply) => {
252 reply.send({ hello: 'world' })
253})
254```
255
256If 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).
257
258```js
259fastify.addHook('onRequest', (request, reply, next) => {
260 const stream = fs.createReadStream('some-file', 'utf8')
261 reply.send(stream)
262})
263```
264
265## Application Hooks
266
267You are able to hook into the application-lifecycle as well. It's important to note that these hooks aren't fully encapsulated. The `this` inside the hooks are encapsulated but the handlers can respond to an event outside the encapsulation boundaries.
268
269- `'onClose'`
270- `'onRoute'`
271- `'onRegister'`
272
273<a name="on-close"></a>
274**'onClose'**<br>
275Triggered when `fastify.close()` is invoked to stop the server. It is useful when [plugins](https://github.com/fastify/fastify/blob/master/docs/Plugins.md) need a "shutdown" event, such as a connection to a database.<br>
276The first argument is the Fastify instance, the second one the `done` callback.
277```js
278fastify.addHook('onClose', (instance, done) => {
279 // some code
280 done()
281})
282```
283<a name="on-route"></a>
284**'onRoute'**<br>
285Triggered 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 do not get passed a callback.
286```js
287fastify.addHook('onRoute', (routeOptions) => {
288 // some code
289 routeOptions.method
290 routeOptions.schema
291 routeOptions.url
292 routeOptions.bodyLimit
293 routeOptions.logLevel
294 routeOptions.prefix
295})
296```
297<a name="on-register"></a>
298**'onRegister'**<br>
299Triggered when a new plugin function is registered, and a new encapsulation context is created, the hook will be executed **before** the plugin code.<br/>
300This 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.<br/>
301**Note:** This hook will not be called if a plugin is wrapped inside [`fastify-plugin`](https://github.com/fastify/fastify-plugin).
302```js
303fastify.decorate('data', [])
304
305fastify.register(async (instance, opts) => {
306 instance.data.push('hello')
307 console.log(instance.data) // ['hello']
308
309 instance.register(async (instance, opts) => {
310 instance.data.push('world')
311 console.log(instance.data) // ['hello', 'world']
312 })
313})
314
315fastify.register(async (instance, opts) => {
316 console.log(instance.data) // []
317})
318
319fastify.addHook('onRegister', (instance) => {
320 // create a new array from the old one
321 // but without keeping the reference
322 // allowing the user to have encapsulated
323 // instances of the `data` property
324 instance.data = instance.data.slice()
325})
326```
327
328<a name="scope"></a>
329### Scope
330Except for [Application Hooks](#application-hooks), all hooks are encapsulated. This means that you can decide where your hooks should run by using `register` as explained in the [plugins guide](https://github.com/fastify/fastify/blob/master/docs/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.
331
332```js
333fastify.addHook('onRequest', function (request, reply, next) {
334 const self = this // Fastify context
335 next()
336})
337```
338Note: using an arrow function will break the binding of this to the Fastify instance.
339
340<a name="route-hooks"></a>
341## Route level hooks
342You can declare one or more custom `onRequest`, `preParsing`, `preValidation`, `preHandler` and `preSerialization` hook(s) that will be unique for the route.
343If you do so, those hooks always be executed as last hook in their category. <br/>
344This can be useful if you need to run the authentication, and the `preParsing` or `preValidation` hooks are exactly what you need for doing that.
345Multiple route-level hooks can also be specified as an array.
346
347Let's make an example:
348
349```js
350fastify.addHook('onRequest', (request, reply, done) => {
351 // your code
352 done()
353})
354
355fastify.addHook('preParsing', (request, reply, done) => {
356 // your code
357 done()
358})
359
360fastify.addHook('preValidation', (request, reply, done) => {
361 // your code
362 done()
363})
364
365fastify.addHook('preHandler', (request, reply, done) => {
366 // your code
367 done()
368})
369
370fastify.addHook('preSerialization', (request, reply, done) => {
371 // your code
372 done()
373})
374
375fastify.route({
376 method: 'GET',
377 url: '/',
378 schema: { ... },
379 onRequest: function (request, reply, done) {
380 // this hook will always be executed after the shared `onRequest` hooks
381 done()
382 },
383 preParsing: function (request, reply, done) {
384 // this hook will always be executed after the shared `preParsing` hooks
385 done()
386 },
387 preValidation: function (request, reply, done) {
388 // this hook will always be executed after the shared `preValidation` hooks
389 done()
390 },
391 preHandler: function (request, reply, done) {
392 // this hook will always be executed after the shared `preHandler` hooks
393 done()
394 },
395 // // Example with an array. All hooks support this syntax.
396 //
397 // preHandler: [function (request, reply, done) {
398 // // this hook will always be executed after the shared `preHandler` hooks
399 // done()
400 // }],
401 preSerialization: (request, reply, payload, next) => {
402 // manipulate the payload
403 next(null, payload)
404 },
405 handler: function (request, reply) {
406 reply.send({ hello: 'world' })
407 }
408})
409```
410
411**Note**: both options also accept an array of functions.