fastify 2.10.0 → 2.13.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 (60) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +52 -28
  3. package/build/build-validation.js +12 -2
  4. package/docs/Decorators.md +136 -75
  5. package/docs/Ecosystem.md +13 -2
  6. package/docs/Errors.md +10 -0
  7. package/docs/Fluent-Schema.md +1 -3
  8. package/docs/Hooks.md +75 -37
  9. package/docs/Plugins-Guide.md +19 -2
  10. package/docs/Plugins.md +27 -0
  11. package/docs/Recommendations.md +159 -0
  12. package/docs/Reply.md +19 -2
  13. package/docs/Routes.md +96 -6
  14. package/docs/Server.md +86 -4
  15. package/docs/Serverless.md +1 -1
  16. package/docs/Testing.md +18 -3
  17. package/docs/TypeScript.md +37 -14
  18. package/docs/Validation-and-Serialization.md +214 -13
  19. package/fastify.d.ts +33 -40
  20. package/fastify.js +65 -6
  21. package/lib/configValidator.js +129 -52
  22. package/lib/contentTypeParser.js +4 -4
  23. package/lib/context.js +2 -1
  24. package/lib/decorate.js +13 -2
  25. package/lib/errors.js +8 -0
  26. package/lib/hooks.js +3 -1
  27. package/lib/logger.js +2 -2
  28. package/lib/reply.js +10 -4
  29. package/lib/route.js +37 -27
  30. package/lib/schemas.js +10 -2
  31. package/lib/server.js +11 -0
  32. package/lib/symbols.js +3 -1
  33. package/lib/validation.js +11 -5
  34. package/package.json +33 -29
  35. package/test/404s.test.js +40 -0
  36. package/test/decorator.test.js +80 -0
  37. package/test/esm/esm.mjs +13 -0
  38. package/test/esm/index.test.js +19 -0
  39. package/test/esm/other.mjs +7 -0
  40. package/test/esm/plugin.mjs +7 -0
  41. package/test/fastify-instance.test.js +29 -0
  42. package/test/genReqId.test.js +1 -1
  43. package/test/hooks-async.js +19 -0
  44. package/test/hooks.test.js +139 -7
  45. package/test/http2/closing.js +53 -0
  46. package/test/http2/plain.js +15 -0
  47. package/test/input-validation.js +63 -0
  48. package/test/input-validation.test.js +161 -0
  49. package/test/internals/decorator.test.js +37 -2
  50. package/test/internals/initialConfig.test.js +6 -2
  51. package/test/internals/reply.test.js +27 -3
  52. package/test/logger.test.js +310 -1
  53. package/test/proto-poisoning.test.js +76 -0
  54. package/test/reply-error.test.js +30 -0
  55. package/test/route-hooks.test.js +23 -14
  56. package/test/route.test.js +62 -24
  57. package/test/router-options.test.js +34 -0
  58. package/test/schemas.test.js +156 -0
  59. package/test/shared-schemas.test.js +65 -0
  60. package/test/types/index.ts +75 -2
package/docs/Server.md CHANGED
@@ -75,7 +75,7 @@ Defines the maximum payload, in bytes, the server is allowed to accept.
75
75
 
76
76
  Defines what action the framework must take when parsing a JSON object
77
77
  with `__proto__`. This functionality is provided by
78
- [bourne](https://github.com/hapijs/bourne).
78
+ [secure-json-parse](https://github.com/fastify/secure-json-parse).
79
79
  See https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061
80
80
  for more details about prototype poisoning attacks.
81
81
 
@@ -83,6 +83,19 @@ Possible values are `'error'`, `'remove'` and `'ignore'`.
83
83
 
84
84
  + Default: `'error'`
85
85
 
86
+ <a name="factory-on-constructor-poisoning"></a>
87
+ ### `onConstructorPoisoning`
88
+
89
+ Defines what action the framework must take when parsing a JSON object
90
+ with `constructor`. This functionality is provided by
91
+ [secure-json-parse](https://github.com/fastify/secure-json-parse).
92
+ See https://hueniverse.com/a-tale-of-prototype-poisoning-2610fa170061
93
+ for more details about prototype poisoning attacks.
94
+
95
+ Possible values are `'error'`, `'remove'` and `'ignore'`.
96
+
97
+ + Default: `'ignore'`
98
+
86
99
  <a name="factory-logger"></a>
87
100
  ### `logger`
88
101
 
@@ -156,7 +169,7 @@ fastify.addHook('onRequest', (req, reply, done) => {
156
169
  })
157
170
 
158
171
  fastify.addHook('onResponse', (req, reply, done) => {
159
- req.log.info({ url: req.req.originalUrl, statusCode: res.res.statusCode }, 'request completed')
172
+ req.log.info({ url: req.req.originalUrl, statusCode: reply.res.statusCode }, 'request completed')
160
173
  done()
161
174
  })
162
175
  ```
@@ -223,7 +236,7 @@ Defines the label used for the request identifier when logging the request.
223
236
 
224
237
  Function for generating the request id. It will receive the incoming request as a parameter.
225
238
 
226
- + Default: `value of 'request-id' if provided or monotonically increasing integers`
239
+ + Default: `value of 'request-id' header if provided or monotonically increasing integers`
227
240
 
228
241
  Especially in distributed systems, you may want to override the default id generation behaviour as shown below. For generating `UUID`s you may want to checkout [hyperid](https://github.com/mcollina/hyperid)
229
242
 
@@ -234,7 +247,7 @@ const fastify = require('fastify')({
234
247
  })
235
248
  ```
236
249
 
237
- **Note: genReqId will _not_ be called if the 'request-id' header is available.**
250
+ **Note: genReqId will _not_ be called if the header set in <code>[requestIdHeader](#requestidheader)</code> is available (defaults to 'request-id').**
238
251
 
239
252
  <a name="factory-trust-proxy"></a>
240
253
  ### `trustProxy`
@@ -360,6 +373,75 @@ If `false`, the server routes the incoming request as usual.
360
373
 
361
374
  + Default: `true`
362
375
 
376
+ <a name="factory-ajv"></a>
377
+ ### `ajv`
378
+
379
+ Configure the ajv instance used by Fastify without providing a custom one.
380
+
381
+ + Default:
382
+
383
+ ```js
384
+ {
385
+ customOptions: {
386
+ removeAdditional: true,
387
+ useDefaults: true,
388
+ coerceTypes: true,
389
+ allErrors: true,
390
+ nullable: true
391
+ },
392
+ plugins: []
393
+ }
394
+ ```
395
+
396
+ ```js
397
+ const fastify = require('fastify')({
398
+ ajv: {
399
+ customOptions: {
400
+ nullable: false // Refer to [ajv options](https://ajv.js.org/#options)
401
+ },
402
+ plugins: [
403
+ require('ajv-merge-patch')
404
+ [require('ajv-keywords'), 'instanceof'];
405
+ // Usage: [plugin, pluginOptions] - Plugin with options
406
+ // Usage: plugin - Plugin without options
407
+ ]
408
+ }
409
+ })
410
+ ```
411
+
412
+ <a name="http2-session-timeout"></a>
413
+ ### `http2SessionTimeout`
414
+
415
+ Set a default
416
+ [timeout](https://nodejs.org/api/http2.html#http2_http2session_settimeout_msecs_callback) to every incoming http2 session. The session will be closed on the timeout. Default: `5000` ms.
417
+
418
+ Note that this is needed to offer the graceful "close" experience when
419
+ using http2. Node core defaults this to `0`.
420
+
421
+ <a name="framework-errors"></a>
422
+ ### `frameworkErrors`
423
+
424
+ + Default: `null`
425
+
426
+ Fastify provides default error handlers for the most common use cases.
427
+ Using this option it is possible to override one or more of those handlers with custom code.
428
+
429
+ *Note: Only `FST_ERR_BAD_URL` is implemented at the moment.*
430
+
431
+ ```js
432
+ const fastify = require('fastify')({
433
+ frameworkErrors: function (error, req, res) {
434
+ if (error instanceof FST_ERR_BAD_URL) {
435
+ res.code(400)
436
+ return res.send("Provided url is not valid")
437
+ } else {
438
+ res.send(err)
439
+ }
440
+ }
441
+ })
442
+ ```
443
+
444
+
363
445
  ## Instance
364
446
 
365
447
  ### Server Methods
@@ -98,7 +98,7 @@ Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless **
98
98
 
99
99
  *Follow the steps below to deploy to Google Cloud Run if you are already familiar with gcloud or just follow their [quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*.
100
100
 
101
- ### Adjust Fastfiy server
101
+ ### Adjust Fastify server
102
102
 
103
103
  In order for Fastify to properly listen for requests within the container, be sure to set the correct port and address:
104
104
 
package/docs/Testing.md CHANGED
@@ -14,12 +14,26 @@ fastify.inject({
14
14
  url: String,
15
15
  query: Object,
16
16
  payload: Object,
17
- headers: Object
17
+ headers: Object,
18
+ cookies: Object
18
19
  }, (error, response) => {
19
20
  // your tests
20
21
  })
21
22
  ```
22
23
 
24
+ `.inject` methods can also be chained by omitting the callback function:
25
+
26
+ ```js
27
+ fastify
28
+ .inject()
29
+ .get('/')
30
+ .headers({ foo: 'bar' })
31
+ .query({ foo: 'bar' })
32
+ .end((err, res) => { // the .end call will trigger the request
33
+ console.log(res.payload)
34
+ })
35
+ ```
36
+
23
37
  or in the promisified version
24
38
 
25
39
  ```js
@@ -29,7 +43,8 @@ fastify
29
43
  url: String,
30
44
  query: Object,
31
45
  payload: Object,
32
- headers: Object
46
+ headers: Object,
47
+ cookies: Object
33
48
  })
34
49
  .then(response => {
35
50
  // your tests
@@ -89,7 +104,7 @@ tap.test('GET `/` route', t => {
89
104
  t.error(err)
90
105
  t.strictEqual(response.statusCode, 200)
91
106
  t.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
92
- t.deepEqual(JSON.parse(response.payload), { hello: 'world' })
107
+ t.deepEqual(response.json(), { hello: 'world' })
93
108
  })
94
109
  })
95
110
  ```
@@ -21,6 +21,7 @@ import { Server, IncomingMessage, ServerResponse } from 'http'
21
21
  // Create a http server. We pass the relevant typings for our http version used.
22
22
  // By passing types we get correctly typed access to the underlying http objects in routes.
23
23
  // If using http2 we'd pass <http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>
24
+ // For https pass http2.Http2SecureServer or http.SecureServer instead of Server.
24
25
  const server: fastify.FastifyInstance<Server, IncomingMessage, ServerResponse> = fastify({})
25
26
 
26
27
  const opts: fastify.RouteShorthandOptions = {
@@ -109,8 +110,8 @@ const opts: fastify.RouteShorthandOptions = {
109
110
  server.get<Query, Params, Headers, Body>('/ping/:bar', opts, (request, reply) => {
110
111
  console.log(request.query) // this is of type Query!
111
112
  console.log(request.params) // this is of type Params!
112
- console.log(request.body) // this is of type Body!
113
113
  console.log(request.headers) // this is of type Headers!
114
+ console.log(request.body) // this is of type Body!
114
115
  reply.code(200).send({ pong: 'it worked!' })
115
116
  })
116
117
  ```
@@ -142,8 +143,8 @@ const opts: fastify.RouteShorthandOptions = {
142
143
  server.get<fastify.DefaultQuery, Params, unknown>('/ping/:bar', opts, (request, reply) => {
143
144
  console.log(request.query) // this is of type fastify.DefaultQuery!
144
145
  console.log(request.params) // this is of type Params!
145
- console.log(request.body) // this is of type unknown!
146
- console.log(request.headers) // this is of type fastify.DefaultHeader because typescript will use the default type value!
146
+ console.log(request.headers) // this is of type unknown!
147
+ console.log(request.body) // this is of type fastify.DefaultBody because typescript will use the default type value!
147
148
  reply.code(200).send({ pong: 'it worked!' })
148
149
  })
149
150
 
@@ -154,8 +155,8 @@ server.get<fastify.DefaultQuery, Params, unknown>('/ping/:bar', opts, (request,
154
155
  server.get<unknown, Params, unknown, unknown>('/ping/:bar', opts, (request, reply) => {
155
156
  console.log(request.query) // this is of type unknown!
156
157
  console.log(request.params) // this is of type Params!
157
- console.log(request.body) // this is of type unknown!
158
158
  console.log(request.headers) // this is of type unknown!
159
+ console.log(request.body) // this is of type unknown!
159
160
  reply.code(200).send({ pong: 'it worked!' })
160
161
  })
161
162
  ```
@@ -189,8 +190,8 @@ application.
189
190
  ## Contributing
190
191
  TypeScript related changes can be considered to fall into one of two categories:
191
192
 
192
- * Core - The typings bundled with fastify
193
- * Plugins - Fastify ecosystem plugins
193
+ * [`Core`](#core-types) - The typings bundled with fastify
194
+ * [`Plugins`](#plugin-types) - Fastify ecosystem plugins
194
195
 
195
196
  Make sure to read our [`CONTRIBUTING.md`](https://github.com/fastify/fastify/blob/master/CONTRIBUTING.md) file before getting started to make sure things go smoothly!
196
197
 
@@ -215,29 +216,46 @@ Some types might not be available yet, so don't be shy about contributing.
215
216
  ### Authoring Plugin Types
216
217
  Typings for many plugins that extend the `FastifyRequest`, `FastifyReply` or `FastifyInstance` objects can be achieved as shown below.
217
218
 
218
- This code shows the typings for the `fastify-static` plugin.
219
+ This code shows the typings for the [`fastify-static`](https://github.com/fastify/fastify-static) plugin.
219
220
 
220
221
  ```ts
222
+ /// <reference types="node" />
223
+
221
224
  // require fastify typings
222
- import fastify = require("fastify");
223
- // require necessary http typings
225
+ import * as fastify from 'fastify';
226
+
227
+ // require necessary http, http2, https typings
224
228
  import { Server, IncomingMessage, ServerResponse } from "http";
229
+ import { Http2SecureServer, Http2Server, Http2ServerRequest, Http2ServerResponse } from "http2";
230
+ import * as https from "https";
231
+
232
+ type HttpServer = Server | Http2Server | Http2SecureServer | https.Server;
233
+ type HttpRequest = IncomingMessage | Http2ServerRequest;
234
+ type HttpResponse = ServerResponse | Http2ServerResponse;
225
235
 
226
236
  // extend fastify typings
227
237
  declare module "fastify" {
228
- interface FastifyReply<HttpResponse> {
229
- sendFile(filename: string): FastifyReply<HttpResponse>;
230
- }
238
+ interface FastifyReply<HttpResponse> {
239
+ sendFile(filename: string): FastifyReply<HttpResponse>;
240
+ }
231
241
  }
232
242
 
233
243
  // declare plugin type using fastify.Plugin
234
- declare const fastifyStatic: fastify.Plugin<Server, IncomingMessage, ServerResponse, {
244
+ declare function fastifyStatic(): fastify.Plugin<
245
+ Server,
246
+ IncomingMessage,
247
+ ServerResponse,
248
+ {
235
249
  root: string;
236
250
  prefix?: string;
237
251
  serve?: boolean;
238
252
  decorateReply?: boolean;
239
253
  schemaHide?: boolean;
240
254
  setHeaders?: (...args: any[]) => void;
255
+ redirect?: boolean;
256
+ wildcard?: boolean | string;
257
+
258
+ // Passed on to `send`
241
259
  acceptRanges?: boolean;
242
260
  cacheControl?: boolean;
243
261
  dotfiles?: boolean;
@@ -247,7 +265,12 @@ declare const fastifyStatic: fastify.Plugin<Server, IncomingMessage, ServerRespo
247
265
  index?: string[];
248
266
  lastModified?: boolean;
249
267
  maxAge?: string | number;
250
- }>;
268
+ }
269
+ >;
270
+
271
+ declare namespace fastifyStatic {
272
+ interface FastifyStaticOptions {}
273
+ }
251
274
 
252
275
  // export plugin type
253
276
  export = fastifyStatic;
@@ -50,11 +50,26 @@ const bodyJsonSchema = {
50
50
  }
51
51
  }
52
52
 
53
+ const queryStringJsonSchema = {
54
+ type: 'object',
55
+ required: ['name'],
56
+ properties: {
57
+ name: { type: 'string' },
58
+ excitement: { type: 'integer' }
59
+ }
60
+ }
61
+
62
+ /* If you don't need required query strings,
63
+ * A short hand syntax is also there:
64
+
53
65
  const queryStringJsonSchema = {
54
66
  name: { type: 'string' },
55
67
  excitement: { type: 'integer' }
56
68
  }
57
69
 
70
+ */
71
+
72
+
58
73
  const paramsJsonSchema = {
59
74
  type: 'object',
60
75
  properties: {
@@ -240,6 +255,77 @@ This example will returns:
240
255
  | /sub | one, two |
241
256
  | /deep | one, two, three |
242
257
 
258
+ <a name="ajv-plugins"></a>
259
+ #### Ajv Plugins
260
+
261
+ You can provide a list of plugins you want to use with Ajv:
262
+
263
+ > Refer to [`ajv options`](https://github.com/fastify/fastify/blob/master/docs/Server.md#factory-ajv) to check plugins format
264
+
265
+ ```js
266
+ const fastify = require('fastify')({
267
+ ajv: {
268
+ plugins: [
269
+ require('ajv-merge-patch')
270
+ ]
271
+ }
272
+ })
273
+
274
+ fastify.route({
275
+ method: 'POST',
276
+ url: '/',
277
+ schema: {
278
+ body: {
279
+ $patch: {
280
+ source: {
281
+ type: 'object',
282
+ properties: {
283
+ q: {
284
+ type: 'string'
285
+ }
286
+ }
287
+ },
288
+ with: [
289
+ {
290
+ op: 'add',
291
+ path: '/properties/q',
292
+ value: { type: 'number' }
293
+ }
294
+ ]
295
+ }
296
+ }
297
+ },
298
+ handler (req, reply) {
299
+ reply.send({ ok: 1 })
300
+ }
301
+ })
302
+
303
+ fastify.route({
304
+ method: 'POST',
305
+ url: '/',
306
+ schema: {
307
+ body: {
308
+ $merge: {
309
+ source: {
310
+ type: 'object',
311
+ properties: {
312
+ q: {
313
+ type: 'string'
314
+ }
315
+ }
316
+ },
317
+ with: {
318
+ required: ['q']
319
+ }
320
+ }
321
+ }
322
+ },
323
+ handler (req, reply) {
324
+ reply.send({ ok: 1 })
325
+ }
326
+ })
327
+ ```
328
+
243
329
  <a name="schema-compiler"></a>
244
330
  #### Schema Compiler
245
331
 
@@ -257,7 +343,9 @@ Fastify's [baseline ajv configuration](https://github.com/epoberezkin/ajv#option
257
343
  }
258
344
  ```
259
345
 
260
- This baseline configuration cannot be modified. If you want to change or set additional config options, you will need to create your own instance and override the existing one like:
346
+ This baseline configuration can be modified by providing [`ajv.customOptions`](https://github.com/fastify/fastify/blob/master/docs/Server.md#factory-ajv) to your Fastify factory.
347
+
348
+ If you want to change or set additional config options, you will need to create your own instance and override the existing one like:
261
349
 
262
350
  ```js
263
351
  const fastify = require('fastify')()
@@ -282,24 +370,136 @@ fastify.schemaCompiler = function (schema) { return ajv.compile(schema) })
282
370
  ```
283
371
  _**Note:** If you use a custom instance of any validator (even Ajv), you have to add schemas to the validator instead of fastify, since fastify's default validator is no longer used, and fastify's `addSchema` method has no idea what validator you are using._
284
372
 
285
- But maybe you want to change the validation library. Perhaps you like `Joi`. In this case, you can use it to validate the url parameters, body, and query string!
373
+ <a name="using-other-validation-libraries"></a>
374
+ #### Using other validation libraries
375
+
376
+ The `schemaCompiler` function makes it easy to substitute `ajv` with almost any Javascript validation library ([joi](https://github.com/hapijs/joi/), [yup](https://github.com/jquense/yup/), ...).
377
+
378
+ However, in order to make your chosen validation engine play well with Fastify's request/response pipeline, the function returned by your `schemaCompiler` function should return an object with either :
379
+
380
+ * in case of validation failure: an `error` property, filled with an instance of `Error` or a string that describes the validation error
381
+ * in case of validation success: an `value` property, filled with the coerced value that passed the validation
382
+
383
+ The examples below are therefore equivalent:
384
+
385
+ ```js
386
+ const joi = require('joi')
387
+
388
+ // Validation options to match ajv's baseline options used in Fastify
389
+ const joiOptions = {
390
+ abortEarly: false, // return all errors
391
+ convert: true, // change data type of data to match type keyword
392
+ allowUnknown : false, // remove additional properties
393
+ noDefaults: false
394
+ }
395
+
396
+ const joiBodySchema = joi.object().keys({
397
+ age: joi.number().integer().required(),
398
+ sub: joi.object().keys({
399
+ name: joi.string().required()
400
+ }).required()
401
+ })
402
+
403
+ const joiSchemaCompiler = schema => data => {
404
+ // joi `validate` function returns an object with an error property (if validation failed) and a value property (always present, coerced value if validation was successful)
405
+ const { error, value } = joiSchema.validate(data, joiOptions)
406
+ if (error) {
407
+ return { error }
408
+ } else {
409
+ return { value }
410
+ }
411
+ }
412
+
413
+ // or more simply...
414
+ const joiSchemaCompiler = schema => data => joiSchema.validate(data, joiOptions)
415
+
416
+ fastify.post('/the/url', {
417
+ schema: {
418
+ body: joiBodySchema
419
+ },
420
+ schemaCompiler: joiSchemaCompiler
421
+ }, handler)
422
+ ```
286
423
 
287
424
  ```js
288
- const Joi = require('joi')
425
+ const yup = require('yup')
426
+
427
+ // Validation options to match ajv's baseline options used in Fastify
428
+ const yupOptions = {
429
+ strict: false,
430
+ abortEarly: false, // return all errors
431
+ stripUnknown: true, // remove additional properties
432
+ recursive: true
433
+ }
434
+
435
+ const yupBodySchema = yup.object({
436
+ age: yup.number().integer().required(),
437
+ sub: yup.object().shape({
438
+ name: yup.string().required()
439
+ }).required()
440
+ })
441
+
442
+ const yupSchemaCompiler = schema => data => {
443
+ // with option strict = false, yup `validateSync` function returns the coerced value if validation was successful, or throws if validation failed
444
+ try {
445
+ const result = schema.validateSync(data, yupOptions)
446
+ return { value: result }
447
+ } catch (e) {
448
+ return { error: e }
449
+ }
450
+ }
289
451
 
290
452
  fastify.post('/the/url', {
291
453
  schema: {
292
- body: Joi.object().keys({
293
- hello: Joi.string().required()
294
- }).required()
454
+ body: yupBodySchema
295
455
  },
296
- schemaCompiler: schema => data => Joi.validate(data, schema)
456
+ schemaCompiler: yupSchemaCompiler
297
457
  }, handler)
298
458
  ```
299
459
 
300
- In that case the function returned by `schemaCompiler` returns an object like:
301
- * `error`: filled with an instance of `Error` or a string that describes the validation error
302
- * `value`: the coerced value that passed the validation
460
+ ##### Validation messages with other validation libraries
461
+
462
+ Fastify's validation error messages are tightly coupled to the default validation engine: errors returned from `ajv` are eventually run through the `schemaErrorsText` function which is responsible for building human-friendly error messages. However, the `schemaErrorsText` function is written with `ajv` in mind : as a result, you may run into odd or incomplete error messages when using other validation librairies.
463
+
464
+ To circumvent this issue, you have 2 main options :
465
+
466
+ 1. make sure your validation function (returned by your custom `schemaCompiler`) returns errors in the exact same structure and format as `ajv` (although this could prove to be difficult and tricky due to differences between validation engines)
467
+ 2. or use a custom `errorHandler` to intercept and format your 'custom' validation errors
468
+
469
+ To help you in writing a custom `errorHandler`, Fastify adds 2 properties to all validation errors:
470
+
471
+ * validation: the content of the `error` property of the object returned by the validation function (returned by your custom `schemaCompiler`)
472
+ * validationContext: the 'context' (body, params, query, headers) where the validation error occurred
473
+
474
+ A very contrived example of such a custom `errorHandler` handling validation errors is shown below:
475
+
476
+ ```js
477
+ const errorHandler = (error, request, reply) => {
478
+
479
+ const statusCode = error.statusCode
480
+ let response
481
+
482
+ const { validation, validationContext } = error
483
+
484
+ // check if we have a validation error
485
+ if (validation) {
486
+ response = {
487
+ message: `A validation error occured when validating the ${validationContext}...`, // validationContext will be 'body' or 'params' or 'headers' or 'query'
488
+ errors: validation // this is the result of your validation library...
489
+ }
490
+ } else {
491
+ response = {
492
+ message: 'An error occurred...'
493
+ }
494
+ }
495
+
496
+ // any additional work here, eg. log error
497
+ // ...
498
+
499
+ reply.status(statusCode).send(response)
500
+
501
+ }
502
+ ```
303
503
 
304
504
  <a name="schema-resolver"></a>
305
505
  #### Schema Resolver
@@ -427,10 +627,10 @@ const schema = {
427
627
  and fail to satisfy it, the route will immediately return a response with the following payload
428
628
 
429
629
  ```js
430
- {
630
+ {
431
631
  "statusCode": 400,
432
632
  "error": "Bad Request",
433
- "message": "body should have required property 'name'"
633
+ "message": "body should have required property 'name'"
434
634
  }
435
635
  ```
436
636
 
@@ -452,7 +652,8 @@ You can also use [setErrorHandler](https://www.fastify.io/docs/latest/Server/#se
452
652
  ```js
453
653
  fastify.setErrorHandler(function (error, request, reply) {
454
654
  if (error.validation) {
455
- reply.status(422).send(new Error('validation failed'))
655
+ // error.validationContext can be on of [body, params, querystring, headers]
656
+ reply.status(422).send(new Error(`validation failed of the ${error.validationContext}`))
456
657
  }
457
658
  })
458
659
  ```