fastify 4.6.0 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/docs/Guides/Ecosystem.md +20 -9
  2. package/docs/Guides/Plugins-Guide.md +44 -0
  3. package/docs/Guides/Testing.md +50 -17
  4. package/docs/Reference/Errors.md +205 -60
  5. package/docs/Reference/Hooks.md +27 -0
  6. package/docs/Reference/Reply.md +5 -5
  7. package/docs/Reference/Request.md +19 -6
  8. package/docs/Reference/Routes.md +56 -2
  9. package/docs/Reference/Server.md +3 -0
  10. package/docs/Reference/Type-Providers.md +3 -3
  11. package/fastify.d.ts +6 -0
  12. package/fastify.js +58 -25
  13. package/lib/contentTypeParser.js +10 -9
  14. package/lib/context.js +27 -3
  15. package/lib/error-handler.js +7 -3
  16. package/lib/errors.js +39 -28
  17. package/lib/handleRequest.js +15 -13
  18. package/lib/reply.js +42 -34
  19. package/lib/request.js +36 -18
  20. package/lib/route.js +22 -11
  21. package/lib/schema-controller.js +7 -1
  22. package/lib/server.js +2 -2
  23. package/lib/symbols.js +2 -0
  24. package/lib/validation.js +4 -3
  25. package/lib/warnings.js +2 -0
  26. package/package.json +26 -26
  27. package/test/404s.test.js +19 -1
  28. package/test/constrained-routes.test.js +114 -0
  29. package/test/context-config.test.js +2 -1
  30. package/test/handler-context.test.js +12 -30
  31. package/test/internals/contentTypeParser.test.js +3 -3
  32. package/test/internals/handleRequest.test.js +4 -3
  33. package/test/internals/reply-serialize.test.js +9 -9
  34. package/test/internals/reply.test.js +10 -9
  35. package/test/internals/request-validate.test.js +97 -9
  36. package/test/internals/request.test.js +57 -3
  37. package/test/internals/validation.test.js +15 -0
  38. package/test/plugin.test.js +1 -1
  39. package/test/reply-code.test.js +59 -0
  40. package/test/route.test.js +29 -0
  41. package/test/router-options.test.js +89 -68
  42. package/test/schema-feature.test.js +25 -0
  43. package/test/schema-validation.test.js +19 -0
  44. package/test/search.test.js +169 -30
  45. package/test/server.test.js +54 -0
  46. package/test/types/fastify.test-d.ts +5 -1
  47. package/types/errors.d.ts +53 -0
  48. package/types/request.d.ts +9 -3
@@ -80,6 +80,8 @@ section.
80
80
  [Next](https://github.com/zeit/next.js/).
81
81
  - [`@fastify/oauth2`](https://github.com/fastify/fastify-oauth2) Wrap around
82
82
  [`simple-oauth2`](https://github.com/lelylan/simple-oauth2).
83
+ - [`@fastify/one-line-logger`](https://github.com/fastify/one-line-logger) Formats
84
+ Fastify's logs into a nice one-line message.
83
85
  - [`@fastify/postgres`](https://github.com/fastify/fastify-postgres) Fastify
84
86
  PostgreSQL connection plugin, with this you can share the same PostgreSQL
85
87
  connection pool in every part of your server.
@@ -119,10 +121,10 @@ section.
119
121
  - [`@fastify/type-provider-json-schema-to-ts`](https://github.com/fastify/fastify-type-provider-json-schema-to-ts)
120
122
  Fastify
121
123
  [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
122
- for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
124
+ for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
123
125
  - [`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
124
126
  Fastify
125
- [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
127
+ [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
126
128
  for [Typebox](https://github.com/sinclairzx81/typebox).
127
129
  - [`@fastify/under-pressure`](https://github.com/fastify/under-pressure) Measure
128
130
  process load with automatic handling of _"Service Unavailable"_ plugin for
@@ -167,7 +169,7 @@ section.
167
169
  Sentry errors handler that just works! Install, add your DSN and you're good
168
170
  to go!
169
171
  - [`@mateonunez/fastify-lyra`](https://github.com/mateonunez/fastify-lyra)
170
- A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
172
+ A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
171
173
  on Fastify
172
174
  - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
173
175
  A plugin to close the server gracefully
@@ -191,7 +193,7 @@ section.
191
193
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware
192
194
  for CLS-based request ID generation. An out-of-the-box solution for adding
193
195
  request IDs into your logs.
194
- - [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
196
+ - [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
195
197
  using Fastify without the need of consuming a port on Electron apps.
196
198
  - [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for
197
199
  waterline. Decorates Fastify with waterline models.
@@ -446,7 +448,7 @@ section.
446
448
  OrientDB connection plugin, with which you can share the OrientDB connection
447
449
  across every part of your server.
448
450
  - [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
449
- OSM plugin to run overpass queries by OpenStreetMap.
451
+ OSM plugin to run overpass queries by OpenStreetMap.
450
452
  - [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo)
451
453
  Fastify plugin for memoize responses by expressive settings.
452
454
  - [`fastify-piscina`](https://github.com/piscinajs/fastify-piscina) A worker
@@ -466,11 +468,14 @@ section.
466
468
  Fastify and protobufjs, together at last. Uses protobufjs by default.
467
469
  - [`fastify-qrcode`](https://github.com/chonla/fastify-qrcode) This plugin
468
470
  utilizes [qrcode](https://github.com/soldair/node-qrcode) to generate QR Code.
469
- - [`fastify-qs`](https://github.com/webdevium/fastify-qs) A plugin for Fastify
471
+ - [`fastify-qs`](https://github.com/vanodevium/fastify-qs) A plugin for Fastify
470
472
  that adds support for parsing URL query parameters with
471
473
  [qs](https://github.com/ljharb/qs).
472
474
  - [`fastify-racing`](https://github.com/metcoder95/fastify-racing) Fastify's
473
475
  plugin that adds support to handle an aborted request asynchronous.
476
+ - [`fastify-ravendb`](https://github.com/nearform/fastify-ravendb) RavenDB
477
+ connection plugin. It exposes the same `DocumentStore` (or multiple ones)
478
+ across the whole Fastify application.
474
479
  - [`fastify-raw-body`](https://github.com/Eomm/fastify-raw-body) Add the
475
480
  `request.rawBody` field.
476
481
  - [`fastify-rbac`](https://gitlab.com/m03geek/fastify-rbac) Fastify role-based
@@ -496,7 +501,9 @@ section.
496
501
  - [`fastify-rob-config`](https://github.com/jeromemacias/fastify-rob-config)
497
502
  Fastify Rob-Config integration.
498
503
  - [`fastify-route-group`](https://github.com/TakNePoidet/fastify-route-group)
499
- Convenient grouping and inheritance of routes
504
+ Convenient grouping and inheritance of routes.
505
+ - [`fastify-s3-buckets`](https://github.com/kibertoad/fastify-s3-buckets)
506
+ Ensure the existence of defined S3 buckets on the application startup.
500
507
  - [`fastify-schema-constraint`](https://github.com/Eomm/fastify-schema-constraint)
501
508
  Choose the JSON schema to use based on request parameters.
502
509
  - [`fastify-schema-to-typescript`](https://github.com/thomasthiebaud/fastify-schema-to-typescript)
@@ -512,11 +519,15 @@ section.
512
519
  `fastify-caching`.
513
520
  - [`fastify-slonik`](https://github.com/Unbuttun/fastify-slonik) Fastify Slonik
514
521
  plugin, with this you can use slonik in every part of your server.
522
+ - [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
523
+ to delay the response from the server.
515
524
  - [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io) a
516
525
  Socket.io plugin for Fastify.
517
526
  - [`fastify-split-validator`](https://github.com/MetCoder95/fastify-split-validator)
518
527
  Small plugin to allow you use multiple validators in one route based on each
519
528
  HTTP part of the request.
529
+ - [`fastify-sqlite`](https://github.com/Eomm/fastify-sqlite) connects your
530
+ application to a sqlite3 database.
520
531
  - [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent
521
532
  Events with `reply.sse( … )` to Fastify.
522
533
  - [`fastify-sse-v2`](https://github.com/nodefactoryio/fastify-sse-v2) to provide
@@ -541,8 +552,8 @@ section.
541
552
  - [`fastify-twitch-ebs-tools`](https://github.com/lukemnet/fastify-twitch-ebs-tools)
542
553
  Useful functions for Twitch Extension Backend Services (EBS).
543
554
  - [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
544
- Fastify
545
- [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
555
+ Fastify
556
+ [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
546
557
  for [zod](https://github.com/colinhacks/zod).
547
558
  - [`fastify-typeorm-plugin`](https://github.com/inthepocket/fastify-typeorm-plugin)
548
559
  Fastify plugin to work with TypeORM.
@@ -308,6 +308,50 @@ fastify.get('/plugin2', (request, reply) => {
308
308
  ```
309
309
  Now your hook will run just for the first route!
310
310
 
311
+ An alternative approach is to make use of the [onRoute hook](../Reference/Hooks.md#onroute)
312
+ to customize application routes dynamically from inside the plugin. Every time
313
+ a new route is registered, you can read and modify the route options. For example,
314
+ based on a [route config option](../Reference/Routes.md#routes-options):
315
+
316
+ ```js
317
+ fastify.register((instance, opts, done) => {
318
+ instance.decorate('util', (request, key, value) => { request[key] = value })
319
+
320
+ function handler(request, reply, done) {
321
+ instance.util(request, 'timestamp', new Date())
322
+ done()
323
+ }
324
+
325
+ instance.addHook('onRoute', (routeOptions) => {
326
+ if (routeOptions.config && routeOptions.config.useUtil === true) {
327
+ // set or add our handler to the route preHandler hook
328
+ if (!routeOptions.preHandler) {
329
+ routeOptions.preHandler = [handler]
330
+ return
331
+ }
332
+ if (Array.isArray(routeOptions.preHandler)) {
333
+ routeOptions.preHandler.push(handler)
334
+ return
335
+ }
336
+ routeOptions.preHandler = [routeOptions.preHandler, handler]
337
+ }
338
+ })
339
+
340
+ fastify.get('/plugin1', {config: {useUtil: true}}, (request, reply) => {
341
+ reply.send(request)
342
+ })
343
+
344
+ fastify.get('/plugin2', (request, reply) => {
345
+ reply.send(request)
346
+ })
347
+
348
+ done()
349
+ })
350
+ ```
351
+
352
+ This variant becomes extremely useful if you plan to distribute your plugin, as
353
+ described in the next section.
354
+
311
355
  As you probably noticed by now, `request` and `reply` are not the standard
312
356
  Nodejs *request* and *response* objects, but Fastify's objects.
313
357
 
@@ -243,33 +243,66 @@ after initializing routes and plugins with `fastify.ready()`.
243
243
 
244
244
  Uses **app.js** from the previous example.
245
245
 
246
- **test-listen.js** (testing with
247
- [`Request`](https://www.npmjs.com/package/request))
246
+ **test-listen.js** (testing with [`undici`](https://www.npmjs.com/package/undici))
248
247
  ```js
249
248
  const tap = require('tap')
250
- const request = require('request')
249
+ const { Client } = require('undici')
251
250
  const buildFastify = require('./app')
252
251
 
253
- tap.test('GET `/` route', t => {
254
- t.plan(5)
252
+ tap.test('should work with undici', async t => {
253
+ t.plan(2)
255
254
 
256
255
  const fastify = buildFastify()
257
256
 
258
- t.teardown(() => fastify.close())
257
+ await fastify.listen()
259
258
 
260
- fastify.listen({ port: 0 }, (err) => {
261
- t.error(err)
259
+ const client = new Client(
260
+ 'http://localhost:' + fastify.server.address().port, {
261
+ keepAliveTimeout: 10,
262
+ keepAliveMaxTimeout: 10
263
+ }
264
+ )
262
265
 
263
- request({
264
- method: 'GET',
265
- url: 'http://localhost:' + fastify.server.address().port
266
- }, (err, response, body) => {
267
- t.error(err)
268
- t.equal(response.statusCode, 200)
269
- t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
270
- t.same(JSON.parse(body), { hello: 'world' })
271
- })
266
+ t.teardown(() => {
267
+ fastify.close()
268
+ client.close()
272
269
  })
270
+
271
+ const response = await client.request({ method: 'GET', path: '/' })
272
+
273
+ t.equal(await response.body.text(), '{"hello":"world"}')
274
+ t.equal(response.statusCode, 200)
275
+ })
276
+ ```
277
+
278
+ Alternatively, starting with Node.js 18,
279
+ [`fetch`](https://nodejs.org/docs/latest-v18.x/api/globals.html#fetch)
280
+ may be used without requiring any extra dependencies:
281
+
282
+ **test-listen.js**
283
+ ```js
284
+ const tap = require('tap')
285
+ const buildFastify = require('./app')
286
+
287
+ tap.test('should work with fetch', async t => {
288
+ t.plan(3)
289
+
290
+ const fastify = buildFastify()
291
+
292
+ t.teardown(() => fastify.close())
293
+
294
+ await fastify.listen()
295
+
296
+ const response = await fetch(
297
+ 'http://localhost:' + fastify.server.address().port
298
+ )
299
+
300
+ t.equal(response.status, 200)
301
+ t.equal(
302
+ response.headers.get('content-type'),
303
+ 'application/json; charset=utf-8'
304
+ )
305
+ t.has(await response.json(), { hello: 'world' })
273
306
  })
274
307
  ```
275
308
 
@@ -83,15 +83,52 @@ Some things to consider in your custom error handler:
83
83
  ### Fastify Error Codes
84
84
  <a id="fastify-error-codes"></a>
85
85
 
86
- #### FST_ERR_BAD_URL
87
- <a id="FST_ERR_BAD_URL"></a>
88
-
89
- The router received an invalid url.
90
-
91
- <a name="FST_ERR_DUPLICATED_ROUTE"></a>
92
- #### FST_ERR_DUPLICATED_ROUTE
93
-
94
- The HTTP method already has a registered controller for that URL
86
+ You can access `errorCodes` for mapping:
87
+ ```js
88
+ // ESM
89
+ import { errorCodes } from 'fastify'
90
+
91
+ // CommonJs
92
+ const errorCodes = require('fastify').errorCodes
93
+ ```
94
+
95
+ For example:
96
+ ```js
97
+ const Fastify = require('./fastify')
98
+
99
+ // Instantiate the framework
100
+ const fastify = Fastify({
101
+ logger: true
102
+ })
103
+
104
+ // Declare a route
105
+ fastify.get('/', function (request, reply) {
106
+ reply.code('bad status code').send({ hello: 'world' })
107
+ })
108
+
109
+ fastify.setErrorHandler(function (error, request, reply) {
110
+ if (error instanceof Fastify.errorCodes.FST_ERR_BAD_STATUS_CODE) {
111
+ // Log error
112
+ this.log.error(error)
113
+ // Send error response
114
+ reply.status(500).send({ ok: false })
115
+ }
116
+ })
117
+
118
+ // Run the server!
119
+ fastify.listen({ port: 3000 }, function (err, address) {
120
+ if (err) {
121
+ fastify.log.error(err)
122
+ process.exit(1)
123
+ }
124
+ // Server is now listening on ${address}
125
+ })
126
+ ```
127
+
128
+ #### FST_ERR_NOT_FOUND
129
+ <a id="FST_ERR_NOT_FOUND"></a>
130
+
131
+ 404 Not Found.
95
132
 
96
133
  <a name="FST_ERR_CTP_ALREADY_PRESENT"></a>
97
134
  #### FST_ERR_CTP_ALREADY_PRESENT
@@ -99,130 +136,238 @@ The HTTP method already has a registered controller for that URL
99
136
 
100
137
  The parser for this content type was already registered.
101
138
 
102
- #### FST_ERR_CTP_BODY_TOO_LARGE
103
- <a id="FST_ERR_CTP_BODY_TOO_LARGE"></a>
104
-
105
- The request body is larger than the provided limit.
139
+ #### FST_ERR_CTP_INVALID_TYPE
140
+ <a id="FST_ERR_CTP_INVALID_TYPE"></a>
106
141
 
107
- This setting can be defined in the Fastify server instance:
108
- [`bodyLimit`](./Server.md#bodylimit)
142
+ The `Content-Type` should be a string.
109
143
 
110
144
  #### FST_ERR_CTP_EMPTY_TYPE
111
145
  <a id="FST_ERR_CTP_EMPTY_TYPE"></a>
112
146
 
113
147
  The content type cannot be an empty string.
114
148
 
115
- #### FST_ERR_CTP_INVALID_CONTENT_LENGTH
116
- <a id="FST_ERR_CTP_INVALID_CONTENT_LENGTH"></a>
117
-
118
- Request body size did not match Content-Length.
119
-
120
149
  #### FST_ERR_CTP_INVALID_HANDLER
121
150
  <a id="FST_ERR_CTP_INVALID_HANDLER"></a>
122
151
 
123
152
  An invalid handler was passed for the content type.
124
153
 
154
+ #### FST_ERR_CTP_INVALID_PARSE_TYPE
155
+ <a id="FST_ERR_CTP_INVALID_PARSE_TYPE"></a>
156
+
157
+ The provided parse type is not supported. Accepted values are `string` or
158
+ `buffer`.
159
+
160
+ #### FST_ERR_CTP_BODY_TOO_LARGE
161
+ <a id="FST_ERR_CTP_BODY_TOO_LARGE"></a>
162
+
163
+ The request body is larger than the provided limit.
164
+
165
+ This setting can be defined in the Fastify server instance:
166
+ [`bodyLimit`](./Server.md#bodylimit)
167
+
125
168
  #### FST_ERR_CTP_INVALID_MEDIA_TYPE
126
169
  <a id="FST_ERR_CTP_INVALID_MEDIA_TYPE"></a>
127
170
 
128
171
  The received media type is not supported (i.e. there is no suitable
129
172
  `Content-Type` parser for it).
130
173
 
131
- #### FST_ERR_CTP_INVALID_PARSE_TYPE
132
- <a id="FST_ERR_CTP_INVALID_PARSE_TYPE"></a>
174
+ #### FST_ERR_CTP_INVALID_CONTENT_LENGTH
175
+ <a id="FST_ERR_CTP_INVALID_CONTENT_LENGTH"></a>
133
176
 
134
- The provided parse type is not supported. Accepted values are `string` or
135
- `buffer`.
177
+ Request body size did not match `Content-Length`.
136
178
 
137
- #### FST_ERR_CTP_INVALID_TYPE
138
- <a id="FST_ERR_CTP_INVALID_TYPE"></a>
179
+ #### FST_ERR_CTP_EMPTY_JSON_BODY
180
+ <a id="FST_ERR_CTP_EMPTY_JSON_BODY"></a>
139
181
 
140
- The `Content-Type` should be a string.
182
+ Body cannot be empty when content-type is set to `application/json`.
141
183
 
142
184
  #### FST_ERR_DEC_ALREADY_PRESENT
143
185
  <a id="FST_ERR_DEC_ALREADY_PRESENT"></a>
144
186
 
145
187
  A decorator with the same name is already registered.
146
188
 
189
+ #### FST_ERR_DEC_DEPENDENCY_INVALID_TYPE
190
+ <a id="FST_ERR_DEC_DEPENDENCY_INVALID_TYPE"></a>
191
+
192
+ The dependencies of decorator must be of type `Array`.
193
+
147
194
  #### FST_ERR_DEC_MISSING_DEPENDENCY
148
195
  <a id="FST_ERR_DEC_MISSING_DEPENDENCY"></a>
149
196
 
150
197
  The decorator cannot be registered due to a missing dependency.
151
198
 
152
- #### FST_ERR_HOOK_INVALID_HANDLER
153
- <a id="FST_ERR_HOOK_INVALID_HANDLER"></a>
199
+ #### FST_ERR_DEC_AFTER_START
200
+ <a id="FST_ERR_DEC_AFTER_START"></a>
154
201
 
155
- The hook callback must be a function.
202
+ The decorator cannot be added after start.
156
203
 
157
204
  #### FST_ERR_HOOK_INVALID_TYPE
158
205
  <a id="FST_ERR_HOOK_INVALID_TYPE"></a>
159
206
 
160
207
  The hook name must be a string.
161
208
 
209
+ #### FST_ERR_HOOK_INVALID_HANDLER
210
+ <a id="FST_ERR_HOOK_INVALID_HANDLER"></a>
211
+
212
+ The hook callback must be a function.
213
+
214
+ #### FST_ERR_MISSING_MIDDLEWARE
215
+ <a id="FST_ERR_MISSING_MIDDLEWARE"></a>
216
+
217
+ You must register a plugin for handling middlewares,
218
+ visit [`Middleware`](./Middleware.md) for more info.
219
+
220
+ <a name="FST_ERR_HOOK_TIMEOUT"></a>
221
+ #### FST_ERR_HOOK_TIMEOUT
222
+
223
+ A callback for a hook timed out
224
+
162
225
  #### FST_ERR_LOG_INVALID_DESTINATION
163
226
  <a id="FST_ERR_LOG_INVALID_DESTINATION"></a>
164
227
 
165
228
  The logger accepts either a `'stream'` or a `'file'` as the destination.
166
229
 
167
- #### FST_ERR_PROMISE_NOT_FULFILLED
168
- <a id="FST_ERR_PROMISE_NOT_FULFILLED"></a>
230
+ #### FST_ERR_REP_INVALID_PAYLOAD_TYPE
231
+ <a id="FST_ERR_REP_INVALID_PAYLOAD_TYPE"></a>
169
232
 
170
- A promise may not be fulfilled with 'undefined' when statusCode is not 204.
233
+ Reply payload can be either a `string` or a `Buffer`.
171
234
 
172
235
  #### FST_ERR_REP_ALREADY_SENT
173
236
  <a id="FST_ERR_REP_ALREADY_SENT"></a>
174
237
 
175
238
  A response was already sent.
176
239
 
177
- #### FST_ERR_REP_INVALID_PAYLOAD_TYPE
178
- <a id="FST_ERR_REP_INVALID_PAYLOAD_TYPE"></a>
240
+ #### FST_ERR_REP_SENT_VALUE
241
+ <a id="FST_ERR_REP_SENT_VALUE"></a>
179
242
 
180
- Reply payload can be either a `string` or a `Buffer`.
243
+ The only possible value for `reply.sent` is `true`.
181
244
 
182
- #### FST_ERR_SCH_ALREADY_PRESENT
183
- <a id="FST_ERR_SCH_ALREADY_PRESENT"></a>
245
+ #### FST_ERR_SEND_INSIDE_ONERR
246
+ <a id="FST_ERR_SEND_INSIDE_ONERR"></a>
184
247
 
185
- A schema with the same `$id` already exists.
248
+ You cannot use `send` inside the `onError` hook.
249
+
250
+ #### FST_ERR_SEND_UNDEFINED_ERR
251
+ <a id="FST_ERR_SEND_UNDEFINED_ERR"></a>
252
+
253
+ Undefined error has occurred.
254
+
255
+ #### FST_ERR_BAD_STATUS_CODE
256
+ <a id="FST_ERR_BAD_STATUS_CODE"></a>
257
+
258
+ Called `reply` with an invalid status code.
259
+
260
+ #### FST_ERR_BAD_TRAILER_NAME
261
+ <a id="FST_ERR_BAD_TRAILER_NAME"></a>
262
+
263
+ Called `reply.trailer` with an invalid header name.
264
+
265
+ #### FST_ERR_BAD_TRAILER_VALUE
266
+ <a id="FST_ERR_BAD_TRAILER_VALUE"></a>
267
+
268
+ Called `reply.trailer` with an invalid type. Expected a function.
269
+
270
+ #### FST_ERR_MISSING_SERIALIZATION_FN
271
+ <a id="FST_ERR_MISSING_SERIALIZATION_FN"></a>
272
+
273
+ Missing serialization function.
274
+
275
+ #### FST_ERR_REQ_INVALID_VALIDATION_INVOCATION
276
+ <a id="FST_ERR_REQ_INVALID_VALIDATION_INVOCATION"></a>
277
+
278
+ Invalid validation invocation. Missing validation function for
279
+ HTTP part nor schema provided.
186
280
 
187
281
  #### FST_ERR_SCH_MISSING_ID
188
282
  <a id="FST_ERR_SCH_MISSING_ID"></a>
189
283
 
190
284
  The schema provided does not have `$id` property.
191
285
 
192
- #### FST_ERR_SCH_SERIALIZATION_BUILD
193
- <a id="FST_ERR_SCH_SERIALIZATION_BUILD"></a>
286
+ #### FST_ERR_SCH_ALREADY_PRESENT
287
+ <a id="FST_ERR_SCH_ALREADY_PRESENT"></a>
194
288
 
195
- The JSON schema provided for serialization of a route response is not valid.
289
+ A schema with the same `$id` already exists.
290
+
291
+ #### FST_ERR_SCH_DUPLICATE
292
+ <a id="FST_ERR_SCH_DUPLICATE"></a>
293
+
294
+ Schema with the same `$id` already present!
196
295
 
197
296
  #### FST_ERR_SCH_VALIDATION_BUILD
198
297
  <a id="FST_ERR_SCH_VALIDATION_BUILD"></a>
199
298
 
200
299
  The JSON schema provided for validation to a route is not valid.
201
300
 
202
- #### FST_ERR_SEND_INSIDE_ONERR
203
- <a id="FST_ERR_SEND_INSIDE_ONERR"></a>
301
+ #### FST_ERR_SCH_SERIALIZATION_BUILD
302
+ <a id="FST_ERR_SCH_SERIALIZATION_BUILD"></a>
204
303
 
205
- You cannot use `send` inside the `onError` hook.
304
+ The JSON schema provided for serialization of a route response is not valid.
206
305
 
207
- #### FST_ERR_SEND_UNDEFINED_ERR
208
- <a id="FST_ERR_SEND_UNDEFINED_ERR"></a>
306
+ #### FST_ERR_HTTP2_INVALID_VERSION
307
+ <a id="FST_ERR_HTTP2_INVALID_VERSION"></a>
209
308
 
210
- Undefined error has occurred.
309
+ HTTP2 is available only from node >= 8.8.1.
211
310
 
212
- <a name="FST_ERR_PLUGIN_NOT_VALID"></a>
213
- #### FST_ERR_PLUGIN_NOT_VALID
311
+ #### FST_ERR_INIT_OPTS_INVALID
312
+ <a id="FST_ERR_INIT_OPTS_INVALID"></a>
214
313
 
215
- Plugin must be a function or a promise.
314
+ Invalid initialization options.
216
315
 
217
- <a name="FST_ERR_PLUGIN_TIMEOUT"></a>
218
- #### FST_ERR_PLUGIN_TIMEOUT
316
+ #### FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE
317
+ <a id="FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE"></a>
219
318
 
220
- Plugin did not start in time. Default timeout (in millis): `10000`
319
+ Cannot set forceCloseConnections to `idle` as your HTTP server
320
+ does not support `closeIdleConnections` method.
221
321
 
222
- <a name="FST_ERR_HOOK_TIMEOUT"></a>
223
- #### FST_ERR_HOOK_TIMEOUT
322
+ <a name="FST_ERR_DUPLICATED_ROUTE"></a>
323
+ #### FST_ERR_DUPLICATED_ROUTE
224
324
 
225
- A callback for a hook timed out
325
+ The HTTP method already has a registered controller for that URL
326
+
327
+ #### FST_ERR_BAD_URL
328
+ <a id="FST_ERR_BAD_URL"></a>
329
+
330
+ The router received an invalid url.
331
+
332
+ ### FST_ERR_ASYNC_CONSTRAINT
333
+ <a id="FST_ERR_ASYNC_CONSTRAINT"></a>
334
+
335
+ The router received error when using asynchronous constraints.
336
+
337
+ #### FST_ERR_DEFAULT_ROUTE_INVALID_TYPE
338
+ <a id="FST_ERR_DEFAULT_ROUTE_INVALID_TYPE"></a>
339
+
340
+ The `defaultRoute` type should be a function.
341
+
342
+ #### FST_ERR_INVALID_URL
343
+ <a id="FST_ERR_INVALID_URL"></a>
344
+
345
+ URL must be a string.
346
+
347
+ #### FST_ERR_REOPENED_CLOSE_SERVER
348
+ <a id="FST_ERR_REOPENED_CLOSE_SERVER"></a>
349
+
350
+ Fastify has already been closed and cannot be reopened.
351
+
352
+ #### FST_ERR_REOPENED_SERVER
353
+ <a id="FST_ERR_REOPENED_SERVER"></a>
354
+
355
+ Fastify is already listening.
356
+
357
+ #### FST_ERR_PLUGIN_VERSION_MISMATCH
358
+ <a id="FST_ERR_PLUGIN_VERSION_MISMATCH"></a>
359
+
360
+ Installed Fastify plugin mismatched expected version.
361
+
362
+ <a name="FST_ERR_PLUGIN_CALLBACK_NOT_FN"></a>
363
+ #### FST_ERR_PLUGIN_CALLBACK_NOT_FN
364
+
365
+ Callback for a hook is not a function (mapped directly from `avvio`)
366
+
367
+ <a name="FST_ERR_PLUGIN_NOT_VALID"></a>
368
+ #### FST_ERR_PLUGIN_NOT_VALID
369
+
370
+ Plugin must be a function or a promise.
226
371
 
227
372
  <a name="FST_ERR_ROOT_PLG_BOOTED"></a>
228
373
  #### FST_ERR_ROOT_PLG_BOOTED
@@ -234,7 +379,7 @@ Root plugin has already booted (mapped directly from `avvio`)
234
379
 
235
380
  Impossible to load plugin because the parent (mapped directly from `avvio`)
236
381
 
237
- <a name="FST_ERR_PLUGIN_CALLBACK_NOT_FN"></a>
238
- #### FST_ERR_PLUGIN_CALLBACK_NOT_FN
382
+ <a name="FST_ERR_PLUGIN_TIMEOUT"></a>
383
+ #### FST_ERR_PLUGIN_TIMEOUT
239
384
 
240
- Callback for a hook is not a function (mapped directly from `avvio`)
385
+ Plugin did not start in time. Default timeout (in millis): `10000`
@@ -449,6 +449,33 @@ fastify.addHook('onRoute', (routeOptions) => {
449
449
  })
450
450
  ```
451
451
 
452
+ If you want to add more routes within an onRoute hook, you have to tag these
453
+ routes properly. If you don't, the hook will run into an infinite loop. The
454
+ recommended approach is shown below.
455
+
456
+ ```js
457
+ const kRouteAlreadyProcessed = Symbol('route-already-processed')
458
+
459
+ fastify.addHook('onRoute', function (routeOptions) {
460
+ const { url, method } = routeOptions
461
+
462
+ const isAlreadyProcessed = (routeOptions.custom && routeOptions.custom[kRouteAlreadyProcessed]) || false
463
+
464
+ if (!isAlreadyProcessed) {
465
+ this.route({
466
+ url,
467
+ method,
468
+ custom: {
469
+ [kRouteAlreadyProcessed]: true
470
+ },
471
+ handler: () => {}
472
+ })
473
+ }
474
+ })
475
+ ```
476
+
477
+ For more details, see this [issue](https://github.com/fastify/fastify/issues/4319).
478
+
452
479
  ### onRegister
453
480
  <a id="on-register"></a>
454
481
 
@@ -7,22 +7,22 @@
7
7
  - [.statusCode](#statuscode)
8
8
  - [.server](#server)
9
9
  - [.header(key, value)](#headerkey-value)
10
- - [set-cookie](#set-cookie)
11
10
  - [.headers(object)](#headersobject)
12
11
  - [.getHeader(key)](#getheaderkey)
13
12
  - [.getHeaders()](#getheaders)
13
+ - [set-cookie](#set-cookie)
14
14
  - [.removeHeader(key)](#removeheaderkey)
15
15
  - [.hasHeader(key)](#hasheaderkey)
16
16
  - [.trailer(key, function)](#trailerkey-function)
17
17
  - [.hasTrailer(key)](#hastrailerkey)
18
18
  - [.removeTrailer(key)](#removetrailerkey)
19
- - [.redirect([code,] dest)](#redirectcode--dest)
19
+ - [.redirect([code ,] dest)](#redirectcode--dest)
20
20
  - [.callNotFound()](#callnotfound)
21
21
  - [.getResponseTime()](#getresponsetime)
22
22
  - [.type(contentType)](#typecontenttype)
23
- - [.getSerializationFunction(schema | httpStatus)](#getserializationfunction)
24
- - [.compileSerializationSchema(schema, httpStatus)](#compileserializationschema)
25
- - [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinput)
23
+ - [.getSerializationFunction(schema | httpStatus)](#getserializationfunctionschema--httpstatus)
24
+ - [.compileSerializationSchema(schema, httpStatus)](#compileserializationschemaschema-httpstatus)
25
+ - [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinputdata-schema--httpstatus-httpstatus)
26
26
  - [.serializer(func)](#serializerfunc)
27
27
  - [.raw](#raw)
28
28
  - [.sent](#sent)