fastify 2.7.1 → 2.11.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 (72) hide show
  1. package/README.md +15 -4
  2. package/build/build-validation.js +8 -0
  3. package/docs/Benchmarking.md +2 -2
  4. package/docs/ContentTypeParser.md +12 -10
  5. package/docs/Decorators.md +14 -14
  6. package/docs/Ecosystem.md +7 -1
  7. package/docs/Errors.md +13 -8
  8. package/docs/Fluent-Schema.md +9 -12
  9. package/docs/Getting-Started.md +29 -25
  10. package/docs/HTTP2.md +1 -1
  11. package/docs/Hooks.md +201 -186
  12. package/docs/LTS.md +6 -7
  13. package/docs/Logging.md +10 -10
  14. package/docs/Middleware.md +59 -0
  15. package/docs/Plugins-Guide.md +52 -52
  16. package/docs/Plugins.md +3 -0
  17. package/docs/Reply.md +47 -3
  18. package/docs/Routes.md +120 -8
  19. package/docs/Server.md +69 -3
  20. package/docs/Serverless.md +76 -4
  21. package/docs/TypeScript.md +33 -10
  22. package/docs/Validation-and-Serialization.md +137 -1
  23. package/examples/typescript-server.ts +1 -1
  24. package/fastify.d.ts +52 -13
  25. package/fastify.js +68 -7
  26. package/lib/configValidator.js +99 -52
  27. package/lib/contentTypeParser.js +4 -4
  28. package/lib/context.js +2 -1
  29. package/lib/errors.js +21 -18
  30. package/lib/fourOhFour.js +10 -10
  31. package/lib/handleRequest.js +1 -2
  32. package/lib/logger.js +2 -2
  33. package/lib/pluginUtils.js +32 -0
  34. package/lib/reply.js +41 -6
  35. package/lib/route.js +37 -9
  36. package/lib/schemas.js +23 -12
  37. package/lib/symbols.js +4 -1
  38. package/lib/validation.js +15 -9
  39. package/lib/wrapThenable.js +1 -1
  40. package/package.json +34 -26
  41. package/test/404s.test.js +41 -1
  42. package/test/async-await.js +66 -0
  43. package/test/custom-parser.test.js +1 -1
  44. package/test/custom-querystring-parser.test.js +1 -1
  45. package/test/decorator.test.js +48 -0
  46. package/test/emit-warning.test.js +3 -3
  47. package/test/fastify-instance.test.js +29 -0
  48. package/test/helper.js +7 -7
  49. package/test/hooks-async.js +4 -3
  50. package/test/hooks.test.js +27 -8
  51. package/test/input-validation.test.js +126 -0
  52. package/test/internals/errors.test.js +9 -1
  53. package/test/internals/initialConfig.test.js +4 -2
  54. package/test/internals/plugin.test.js +4 -4
  55. package/test/internals/reply.test.js +78 -6
  56. package/test/internals/schemas.test.js +30 -0
  57. package/test/internals/validation.test.js +18 -0
  58. package/test/listen.test.js +1 -1
  59. package/test/logger.test.js +314 -1
  60. package/test/plugin.test.js +171 -0
  61. package/test/promises.test.js +55 -0
  62. package/test/proto-poisoning.test.js +76 -0
  63. package/test/route-hooks.test.js +109 -91
  64. package/test/route-prefix.test.js +1 -1
  65. package/test/schemas.test.js +450 -0
  66. package/test/shared-schemas.test.js +2 -2
  67. package/test/stream.test.js +10 -6
  68. package/test/throw.test.js +48 -2
  69. package/test/types/index.ts +86 -1
  70. package/test/validation-error-handling.test.js +3 -3
  71. package/test/versioned-routes.test.js +1 -1
  72. package/docs/Middlewares.md +0 -59
@@ -240,6 +240,77 @@ This example will returns:
240
240
  | /sub | one, two |
241
241
  | /deep | one, two, three |
242
242
 
243
+ <a name="ajv-plugins"></a>
244
+ #### Ajv Plugins
245
+
246
+ You can provide a list of plugins you want to use with Ajv:
247
+
248
+ > Refer to [`ajv options`](https://github.com/fastify/fastify/blob/master/docs/Server.md#factory-ajv) to check plugins format
249
+
250
+ ```js
251
+ const fastify = require('fastify')({
252
+ ajv: {
253
+ plugins: [
254
+ require('ajv-merge-patch')
255
+ ]
256
+ }
257
+ })
258
+
259
+ fastify.route({
260
+ method: 'POST',
261
+ url: '/',
262
+ schema: {
263
+ body: {
264
+ $patch: {
265
+ source: {
266
+ type: 'object',
267
+ properties: {
268
+ q: {
269
+ type: 'string'
270
+ }
271
+ }
272
+ },
273
+ with: [
274
+ {
275
+ op: 'add',
276
+ path: '/properties/q',
277
+ value: { type: 'number' }
278
+ }
279
+ ]
280
+ }
281
+ }
282
+ },
283
+ handler (req, reply) {
284
+ reply.send({ ok: 1 })
285
+ }
286
+ })
287
+
288
+ fastify.route({
289
+ method: 'POST',
290
+ url: '/',
291
+ schema: {
292
+ body: {
293
+ $merge: {
294
+ source: {
295
+ type: 'object',
296
+ properties: {
297
+ q: {
298
+ type: 'string'
299
+ }
300
+ }
301
+ },
302
+ with: {
303
+ required: ['q']
304
+ }
305
+ }
306
+ }
307
+ },
308
+ handler (req, reply) {
309
+ reply.send({ ok: 1 })
310
+ }
311
+ })
312
+ ```
313
+
243
314
  <a name="schema-compiler"></a>
244
315
  #### Schema Compiler
245
316
 
@@ -257,7 +328,9 @@ Fastify's [baseline ajv configuration](https://github.com/epoberezkin/ajv#option
257
328
  }
258
329
  ```
259
330
 
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:
331
+ 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.
332
+
333
+ 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
334
 
262
335
  ```js
263
336
  const fastify = require('fastify')()
@@ -280,6 +353,7 @@ fastify.setSchemaCompiler(function (schema) {
280
353
  // Alternatively, you can set the schema compiler using the setter property:
281
354
  fastify.schemaCompiler = function (schema) { return ajv.compile(schema) })
282
355
  ```
356
+ _**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._
283
357
 
284
358
  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!
285
359
 
@@ -300,6 +374,68 @@ In that case the function returned by `schemaCompiler` returns an object like:
300
374
  * `error`: filled with an instance of `Error` or a string that describes the validation error
301
375
  * `value`: the coerced value that passed the validation
302
376
 
377
+ <a name="schema-resolver"></a>
378
+ #### Schema Resolver
379
+
380
+ The `schemaResolver` is a function that works together with the `schemaCompiler`: you can't use it
381
+ with the default schema compiler. This feature is useful when you use complex schemas with `$ref` keyword
382
+ in your routes and a custom validator.
383
+
384
+ This is needed because all the schemas you add to your custom compiler are unknown to Fastify but it
385
+ need to resolve the `$ref` paths.
386
+
387
+ ```js
388
+ const fastify = require('fastify')()
389
+ const Ajv = require('ajv')
390
+ const ajv = new Ajv()
391
+
392
+ ajv.addSchema({
393
+ $id: 'urn:schema:foo',
394
+ definitions: {
395
+ foo: { type: 'string' }
396
+ },
397
+ type: 'object',
398
+ properties: {
399
+ foo: { $ref: '#/definitions/foo' }
400
+ }
401
+ })
402
+ ajv.addSchema({
403
+ $id: 'urn:schema:response',
404
+ type: 'object',
405
+ required: ['foo'],
406
+ properties: {
407
+ foo: { $ref: 'urn:schema:foo#/definitions/foo' }
408
+ }
409
+ })
410
+ ajv.addSchema({
411
+ $id: 'urn:schema:request',
412
+ type: 'object',
413
+ required: ['foo'],
414
+ properties: {
415
+ foo: { $ref: 'urn:schema:foo#/definitions/foo' }
416
+ }
417
+ })
418
+
419
+ fastify.setSchemaCompiler(schema => ajv.compile(schema))
420
+ fastify.setSchemaResolver((ref) => {
421
+ return ajv.getSchema(ref).schema
422
+ })
423
+
424
+ fastify.route({
425
+ method: 'POST',
426
+ url: '/',
427
+ schema: {
428
+ body: ajv.getSchema('urn:schema:request').schema,
429
+ response: {
430
+ '2xx': ajv.getSchema('urn:schema:response').schema
431
+ }
432
+ },
433
+ handler (req, reply) {
434
+ reply.send({ foo: 'bar' })
435
+ }
436
+ })
437
+ ```
438
+
303
439
  <a name="serialization"></a>
304
440
  ### Serialization
305
441
  Usually you will send your data to the clients via JSON, and Fastify has a powerful tool to help you, [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify), which is used if you have provided an output schema in the route options. We encourage you to use an output schema, as it will increase your throughput by 100-400% depending on your payload and will prevent accidental disclosure of sensitive information.
@@ -3,7 +3,7 @@
3
3
  * Most type annotations in this file are not strictly necessary but are
4
4
  * included for this example.
5
5
  *
6
- * To run this example exectute the following commands to install typescript,
6
+ * To run this example execute the following commands to install typescript,
7
7
  * transpile the code, and start the server:
8
8
  *
9
9
  * npm i -g typescript
package/fastify.d.ts CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  /// <reference types="node" />
6
6
 
7
+ import * as ajv from 'ajv'
7
8
  import * as http from 'http'
8
9
  import * as http2 from 'http2'
9
10
  import * as https from 'https'
@@ -21,7 +22,10 @@ declare function fastify(opts?: fastify.ServerOptionsAsSecureHttp2): fastify.Fas
21
22
  // eslint-disable-next-line no-redeclare
22
23
  declare namespace fastify {
23
24
 
24
- type Plugin < HttpServer, HttpRequest, HttpResponse, T > = (instance: FastifyInstance< HttpServer, HttpRequest, HttpResponse >, opts: T, callback: (err?: FastifyError) => void) => void
25
+ type Plugin<HttpServer, HttpRequest, HttpResponse, Options, PluginInstance extends Function = Function> =
26
+ PluginInstance extends () => Promise<void> ?
27
+ ((instance: FastifyInstance< HttpServer, HttpRequest, HttpResponse >, options: Options) => Promise<void>) :
28
+ (instance: FastifyInstance<HttpServer, HttpRequest, HttpResponse>, options: Options, callback: (err?: FastifyError) => void) => void;
25
29
 
26
30
  type Middleware < HttpServer, HttpRequest, HttpResponse > = (this: FastifyInstance<HttpServer, HttpRequest, HttpResponse>, req: HttpRequest, res: HttpResponse, callback: (err?: FastifyError) => void) => void
27
31
 
@@ -81,7 +85,7 @@ declare namespace fastify {
81
85
  req: FastifyRequest<HttpRequest, Query, Params, Headers, Body>,
82
86
  reply: FastifyReply<HttpResponse>,
83
87
  done: (err?: Error) => void,
84
- ) => void
88
+ ) => void | Promise<any>
85
89
 
86
90
  type FastifyMiddlewareWithPayload<
87
91
  HttpServer = http.Server,
@@ -97,7 +101,7 @@ declare namespace fastify {
97
101
  reply: FastifyReply<HttpResponse>,
98
102
  payload: any,
99
103
  done: (err?: Error, value?: any) => void,
100
- ) => void
104
+ ) => void | Promise<any>
101
105
 
102
106
  type RequestHandler<
103
107
  HttpRequest = http.IncomingMessage,
@@ -113,6 +117,7 @@ declare namespace fastify {
113
117
  ) => void | Promise<any>
114
118
 
115
119
  type SchemaCompiler = (schema: Object) => Function
120
+ type SchemaResolver = (ref: string) => Object
116
121
 
117
122
  type BodyParser<HttpRequest, RawBody extends string | Buffer> =
118
123
  | ((req: HttpRequest, rawBody: RawBody, done: (err: Error | null, body?: any) => void) => void)
@@ -165,6 +170,7 @@ declare namespace fastify {
165
170
  headers(headers: { [key: string]: any }): FastifyReply<HttpResponse>
166
171
  getHeader(name: string): string | undefined
167
172
  hasHeader(name: string): boolean
173
+ removeHeader(name: string): FastifyReply<HttpResponse>
168
174
  callNotFound(): void
169
175
  getResponseTime(): number
170
176
  type(contentType: string): FastifyReply<HttpResponse>
@@ -176,8 +182,12 @@ declare namespace fastify {
176
182
  sent: boolean
177
183
  res: HttpResponse
178
184
  context: FastifyContext
185
+ request: FastifyRequest
179
186
  }
180
187
  type TrustProxyFunction = (addr: string, index: number) => boolean
188
+ type ServerFactoryHandlerFunction = (request: http.IncomingMessage | http2.Http2ServerRequest, response: http.ServerResponse | http2.Http2ServerResponse) => void
189
+ type ServerFactoryFunction = (handler: ServerFactoryHandlerFunction, options: ServerOptions) => http.Server | http2.Http2Server
190
+
181
191
  interface ServerOptions {
182
192
  caseSensitive?: boolean,
183
193
  ignoreTrailingSlash?: boolean,
@@ -185,6 +195,7 @@ declare namespace fastify {
185
195
  pluginTimeout?: number,
186
196
  disableRequestLogging?: boolean,
187
197
  onProtoPoisoning?: 'error' | 'remove' | 'ignore',
198
+ onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
188
199
  logger?: any,
189
200
  trustProxy?: string | number | boolean | Array<string> | TrustProxyFunction,
190
201
  maxParamLength?: number,
@@ -199,7 +210,15 @@ declare namespace fastify {
199
210
  deriveVersion<Context>(req: Object, ctx?: Context) : String,
200
211
  },
201
212
  modifyCoreObjects?: boolean,
202
- return503OnClosing?: boolean
213
+ return503OnClosing?: boolean,
214
+ genReqId?: () => number | string,
215
+ requestIdHeader?: string,
216
+ requestIdLogLabel?: string,
217
+ serverFactory?: ServerFactoryFunction,
218
+ ajv?: {
219
+ customOptions?: ajv.Options,
220
+ plugins?: Array<Array<any>|String>
221
+ }
203
222
  }
204
223
  interface ServerOptionsAsSecure extends ServerOptions {
205
224
  https: http2.SecureServerOptions
@@ -241,9 +260,15 @@ declare namespace fastify {
241
260
  > {
242
261
  schema?: RouteSchema
243
262
  attachValidation?: boolean
263
+ onSend?:
264
+ | FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
265
+ | Array<FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
244
266
  onRequest?:
245
267
  | FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
246
268
  | Array<FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
269
+ onResponse?:
270
+ | FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
271
+ | Array<FastifyMiddleware<HttpServer, HttpRequest, HttpResponse>>
247
272
  preParsing?:
248
273
  | FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
249
274
  | Array<FastifyMiddleware<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
@@ -256,10 +281,13 @@ declare namespace fastify {
256
281
  preSerialization?:
257
282
  FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>
258
283
  | Array<FastifyMiddlewareWithPayload<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>>
284
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>
259
285
  schemaCompiler?: SchemaCompiler
260
286
  bodyLimit?: number
261
287
  logLevel?: string
288
+ logSerializers?: Object
262
289
  config?: any
290
+ version?: string
263
291
  prefixTrailingSlash?: 'slash' | 'no-slash' | 'both'
264
292
  }
265
293
 
@@ -286,6 +314,7 @@ declare namespace fastify {
286
314
  interface RegisterOptions<HttpServer, HttpRequest, HttpResponse> {
287
315
  [key: string]: any,
288
316
  prefix?: string,
317
+ logSerializers?: Object
289
318
  }
290
319
 
291
320
  /**
@@ -362,7 +391,7 @@ declare namespace fastify {
362
391
  get<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
363
392
  url: string,
364
393
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
365
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
394
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
366
395
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
367
396
 
368
397
  /**
@@ -379,7 +408,7 @@ declare namespace fastify {
379
408
  put<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
380
409
  url: string,
381
410
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
382
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
411
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
383
412
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
384
413
 
385
414
  /**
@@ -396,7 +425,7 @@ declare namespace fastify {
396
425
  patch<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
397
426
  url: string,
398
427
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
399
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
428
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
400
429
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
401
430
 
402
431
  /**
@@ -413,7 +442,7 @@ declare namespace fastify {
413
442
  post<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
414
443
  url: string,
415
444
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
416
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
445
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
417
446
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
418
447
 
419
448
  /**
@@ -430,7 +459,7 @@ declare namespace fastify {
430
459
  head<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
431
460
  url: string,
432
461
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
433
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
462
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
434
463
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
435
464
 
436
465
  /**
@@ -447,7 +476,7 @@ declare namespace fastify {
447
476
  delete<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
448
477
  url: string,
449
478
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
450
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
479
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
451
480
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
452
481
 
453
482
  /**
@@ -464,7 +493,7 @@ declare namespace fastify {
464
493
  options<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
465
494
  url: string,
466
495
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
467
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
496
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
468
497
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
469
498
 
470
499
  /**
@@ -481,7 +510,7 @@ declare namespace fastify {
481
510
  all<Query = DefaultQuery, Params = DefaultParams, Headers = DefaultHeaders, Body = DefaultBody>(
482
511
  url: string,
483
512
  opts: RouteShorthandOptions<HttpServer, HttpRequest, HttpResponse, Query, Params, Headers, Body>,
484
- handler: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
513
+ handler?: RequestHandler<HttpRequest, HttpResponse, Query, Params, Headers, Body>,
485
514
  ): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
486
515
 
487
516
  /**
@@ -532,10 +561,15 @@ declare namespace fastify {
532
561
  */
533
562
  use(path: string, middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
534
563
 
564
+ /**
565
+ * Apply the given middleware to routes matching the given multiple path
566
+ */
567
+ use(path: string[], middleware: Middleware<HttpServer, HttpRequest, HttpResponse>): void
568
+
535
569
  /**
536
570
  * Registers a plugin
537
571
  */
538
- register<T extends RegisterOptions<HttpServer, HttpRequest, HttpResponse>>(plugin: Plugin<HttpServer, HttpRequest, HttpResponse, T>, opts?: T): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
572
+ register<Options extends RegisterOptions<HttpServer, HttpRequest, HttpResponse>, PluginInstance extends Function>(plugin: Plugin<HttpServer, HttpRequest, HttpResponse, Options, PluginInstance>, options?: Options): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
539
573
 
540
574
  /**
541
575
  * `Register a callback that will be executed just after a register.
@@ -671,6 +705,11 @@ declare namespace fastify {
671
705
  */
672
706
  setSchemaCompiler(schemaCompiler: SchemaCompiler): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
673
707
 
708
+ /**
709
+ * Set the schema resolver to find the `$ref` schema object
710
+ */
711
+ setSchemaResolver(schemaResolver: SchemaResolver): FastifyInstance<HttpServer, HttpRequest, HttpResponse>
712
+
674
713
  /**
675
714
  * Create a shared schema
676
715
  */
package/fastify.js CHANGED
@@ -10,9 +10,11 @@ const {
10
10
  kBodyLimit,
11
11
  kRoutePrefix,
12
12
  kLogLevel,
13
+ kLogSerializers,
13
14
  kHooks,
14
15
  kSchemas,
15
16
  kSchemaCompiler,
17
+ kSchemaResolver,
16
18
  kReplySerializerDefault,
17
19
  kContentTypeParser,
18
20
  kReply,
@@ -21,7 +23,8 @@ const {
21
23
  kFourOhFour,
22
24
  kState,
23
25
  kOptions,
24
- kGlobalHooks
26
+ kGlobalHooks,
27
+ kPluginNameChain
25
28
  } = require('./lib/symbols.js')
26
29
 
27
30
  const { createServer } = require('./lib/server')
@@ -55,7 +58,7 @@ function build (options) {
55
58
  validateBodyLimitOption(options.bodyLimit)
56
59
 
57
60
  if (options.logger && options.logger.genReqId) {
58
- process.emitWarning(`Using 'genReqId' in logger options is deprecated. Use fastify options instead. See: https://www.fastify.io/docs/latest/Server/#gen-request-id`)
61
+ process.emitWarning("Using 'genReqId' in logger options is deprecated. Use fastify options instead. See: https://www.fastify.io/docs/latest/Server/#gen-request-id")
59
62
  options.genReqId = options.logger.genReqId
60
63
  }
61
64
 
@@ -66,6 +69,21 @@ function build (options) {
66
69
  const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
67
70
  const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
68
71
  const disableRequestLogging = options.disableRequestLogging || false
72
+ const ajvOptions = Object.assign({
73
+ customOptions: {},
74
+ plugins: []
75
+ }, options.ajv)
76
+
77
+ // Ajv options
78
+ if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
79
+ throw new Error(`ajv.customOptions option should be an object, instead got '${typeof ajvOptions.customOptions}'`)
80
+ }
81
+ if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
82
+ throw new Error(`ajv.plugins option should be an array, instead got '${typeof ajvOptions.customOptions}'`)
83
+ }
84
+ ajvOptions.plugins = ajvOptions.plugins.map(plugin => {
85
+ return Array.isArray(plugin) ? plugin : [plugin]
86
+ })
69
87
 
70
88
  // Instance Fastify components
71
89
  const { logger, hasLogger } = createLogger(options)
@@ -79,11 +97,13 @@ function build (options) {
79
97
  options.requestIdLogLabel = requestIdLogLabel
80
98
  options.modifyCoreObjects = modifyCoreObjects
81
99
  options.disableRequestLogging = disableRequestLogging
100
+ options.ajv = ajvOptions
82
101
 
83
102
  // Default router
84
103
  const router = buildRouting({
85
104
  config: {
86
105
  defaultRoute: defaultRoute,
106
+ onBadUrl: onBadUrl,
87
107
  ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
88
108
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
89
109
  caseSensitive: options.caseSensitive,
@@ -116,11 +136,17 @@ function build (options) {
116
136
  [kBodyLimit]: bodyLimit,
117
137
  [kRoutePrefix]: '',
118
138
  [kLogLevel]: '',
139
+ [kLogSerializers]: null,
119
140
  [kHooks]: new Hooks(),
120
141
  [kSchemas]: schemas,
121
142
  [kSchemaCompiler]: null,
143
+ [kSchemaResolver]: null,
122
144
  [kReplySerializerDefault]: null,
123
- [kContentTypeParser]: new ContentTypeParser(bodyLimit, (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning)),
145
+ [kContentTypeParser]: new ContentTypeParser(
146
+ bodyLimit,
147
+ (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
148
+ (options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
149
+ ),
124
150
  [kReply]: Reply.buildReply(Reply),
125
151
  [kRequest]: Request.buildRequest(Request),
126
152
  [kMiddlewares]: [],
@@ -130,6 +156,7 @@ function build (options) {
130
156
  onRegister: []
131
157
  },
132
158
  [pluginUtils.registeredPlugins]: [],
159
+ [kPluginNameChain]: [],
133
160
  // routes shorthand methods
134
161
  delete: function _delete (url, opts, handler) {
135
162
  return router.prepareRoute.call(this, 'DELETE', url, opts, handler)
@@ -169,6 +196,7 @@ function build (options) {
169
196
  addSchema: addSchema,
170
197
  getSchemas: schemas.getSchemas.bind(schemas),
171
198
  setSchemaCompiler: setSchemaCompiler,
199
+ setSchemaResolver: setSchemaResolver,
172
200
  setReplySerializer: setReplySerializer,
173
201
  // custom parsers
174
202
  addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
@@ -224,6 +252,15 @@ function build (options) {
224
252
  }
225
253
  })
226
254
 
255
+ Object.defineProperty(fastify, 'pluginName', {
256
+ get: function () {
257
+ if (this[kPluginNameChain].length > 1) {
258
+ return this[kPluginNameChain].join(' -> ')
259
+ }
260
+ return this[kPluginNameChain][0]
261
+ }
262
+ })
263
+
227
264
  // Install and configure Avvio
228
265
  // Avvio will update the following Fastify methods:
229
266
  // - register
@@ -330,13 +367,13 @@ function build (options) {
330
367
  throwIfAlreadyStarted('Cannot call "addHook" when fastify instance is already started!')
331
368
 
332
369
  // TODO: v3 instead of log a warning, throw an error
333
- if (name === 'onSend' || name === 'preSerialization') {
370
+ if (name === 'onSend' || name === 'preSerialization' || name === 'onError') {
334
371
  if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {
335
- fastify.log.warn(`Async function has too many arguments. Async hooks should not use the 'next' argument.`, new Error().stack)
372
+ fastify.log.warn("Async function has too many arguments. Async hooks should not use the 'next' argument.", new Error().stack)
336
373
  }
337
374
  } else {
338
375
  if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
339
- fastify.log.warn(`Async function has too many arguments. Async hooks should not use the 'next' argument.`, new Error().stack)
376
+ fastify.log.warn("Async function has too many arguments. Async hooks should not use the 'next' argument.", new Error().stack)
340
377
  }
341
378
  }
342
379
 
@@ -390,6 +427,15 @@ function build (options) {
390
427
  fourOhFour.router.lookup(req, res)
391
428
  }
392
429
 
430
+ function onBadUrl (path, req, res) {
431
+ const body = `{"error":"Bad Request","message":"'${path}' is not a valid url component","statusCode":400}`
432
+ res.writeHead(400, {
433
+ 'Content-Type': 'application/json',
434
+ 'Content-Length': body.length
435
+ })
436
+ res.end(body)
437
+ }
438
+
393
439
  function setNotFoundHandler (opts, handler) {
394
440
  throwIfAlreadyStarted('Cannot call "setNotFoundHandler" when fastify instance is already started!')
395
441
 
@@ -404,6 +450,13 @@ function build (options) {
404
450
  return this
405
451
  }
406
452
 
453
+ function setSchemaResolver (schemaRefResolver) {
454
+ throwIfAlreadyStarted('Cannot call "setSchemaResolver" when fastify instance is already started!')
455
+
456
+ this[kSchemaResolver] = schemaRefResolver
457
+ return this
458
+ }
459
+
407
460
  function setReplySerializer (replySerializer) {
408
461
  throwIfAlreadyStarted('Cannot call "setReplySerializer" when fastify instance is already started!')
409
462
 
@@ -424,7 +477,10 @@ function build (options) {
424
477
  // Everything that need to be encapsulated must be handled in this function.
425
478
  function override (old, fn, opts) {
426
479
  const shouldSkipOverride = pluginUtils.registerPlugin.call(old, fn)
480
+
427
481
  if (shouldSkipOverride) {
482
+ // after every plugin registration we will enter a new name
483
+ old[kPluginNameChain].push(pluginUtils.getDisplayName(fn))
428
484
  return old
429
485
  }
430
486
 
@@ -441,12 +497,17 @@ function override (old, fn, opts) {
441
497
  instance[kSchemas] = buildSchemas(old[kSchemas])
442
498
  instance.getSchemas = instance[kSchemas].getSchemas.bind(instance[kSchemas])
443
499
  instance[pluginUtils.registeredPlugins] = Object.create(instance[pluginUtils.registeredPlugins])
500
+ instance[kPluginNameChain] = [pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)]
501
+
502
+ if (instance[kLogSerializers] || opts.logSerializers) {
503
+ instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
504
+ }
444
505
 
445
506
  if (opts.prefix) {
446
507
  instance[kFourOhFour].arrange404(instance)
447
508
  }
448
509
 
449
- for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance)
510
+ for (const hook of instance[kGlobalHooks].onRegister) hook.call(this, instance, opts)
450
511
 
451
512
  return instance
452
513
  }