fastify 3.27.2 → 4.0.0-alpha.1

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 (116) hide show
  1. package/README.md +5 -4
  2. package/build/build-error-serializer.js +27 -0
  3. package/build/build-validation.js +47 -35
  4. package/docs/Migration-Guide-V4.md +12 -0
  5. package/docs/Reference/ContentTypeParser.md +4 -0
  6. package/docs/Reference/Errors.md +51 -6
  7. package/docs/Reference/Hooks.md +4 -7
  8. package/docs/Reference/LTS.md +5 -4
  9. package/docs/Reference/Reply.md +23 -22
  10. package/docs/Reference/Request.md +1 -3
  11. package/docs/Reference/Routes.md +17 -10
  12. package/docs/Reference/Server.md +48 -63
  13. package/docs/Reference/TypeScript.md +11 -13
  14. package/docs/Reference/Validation-and-Serialization.md +28 -53
  15. package/docs/Type-Providers.md +257 -0
  16. package/examples/hooks.js +1 -1
  17. package/examples/simple-stream.js +18 -0
  18. package/fastify.d.ts +34 -22
  19. package/fastify.js +37 -35
  20. package/lib/configValidator.js +902 -1023
  21. package/lib/contentTypeParser.js +6 -16
  22. package/lib/context.js +36 -10
  23. package/lib/decorate.js +3 -1
  24. package/lib/error-handler.js +158 -0
  25. package/lib/error-serializer.js +257 -0
  26. package/lib/errors.js +43 -9
  27. package/lib/fourOhFour.js +31 -20
  28. package/lib/handleRequest.js +10 -13
  29. package/lib/hooks.js +14 -9
  30. package/lib/pluginOverride.js +0 -3
  31. package/lib/pluginUtils.js +3 -2
  32. package/lib/reply.js +28 -157
  33. package/lib/request.js +13 -10
  34. package/lib/route.js +131 -138
  35. package/lib/schema-controller.js +2 -2
  36. package/lib/schemas.js +27 -1
  37. package/lib/server.js +219 -116
  38. package/lib/symbols.js +4 -3
  39. package/lib/validation.js +2 -1
  40. package/lib/warnings.js +2 -12
  41. package/lib/wrapThenable.js +4 -11
  42. package/package.json +31 -35
  43. package/test/404s.test.js +243 -110
  44. package/test/500s.test.js +2 -2
  45. package/test/async-await.test.js +13 -69
  46. package/test/content-parser.test.js +32 -0
  47. package/test/context-config.test.js +52 -0
  48. package/test/custom-http-server.test.js +14 -7
  49. package/test/custom-parser-async.test.js +0 -65
  50. package/test/custom-parser.test.js +54 -121
  51. package/test/decorator.test.js +1 -3
  52. package/test/delete.test.js +5 -5
  53. package/test/encapsulated-error-handler.test.js +50 -0
  54. package/test/esm/index.test.js +0 -14
  55. package/test/fastify-instance.test.js +4 -4
  56. package/test/fluent-schema.test.js +4 -4
  57. package/test/get.test.js +3 -3
  58. package/test/helper.js +18 -3
  59. package/test/hooks-async.test.js +14 -47
  60. package/test/hooks.on-ready.test.js +9 -4
  61. package/test/hooks.test.js +58 -99
  62. package/test/http2/closing.test.js +5 -11
  63. package/test/http2/unknown-http-method.test.js +3 -9
  64. package/test/https/custom-https-server.test.js +12 -6
  65. package/test/input-validation.js +2 -2
  66. package/test/internals/handleRequest.test.js +3 -40
  67. package/test/internals/initialConfig.test.js +33 -12
  68. package/test/internals/reply.test.js +245 -3
  69. package/test/internals/request.test.js +13 -7
  70. package/test/internals/server.test.js +88 -0
  71. package/test/listen.test.js +84 -1
  72. package/test/logger.test.js +80 -40
  73. package/test/maxRequestsPerSocket.test.js +6 -4
  74. package/test/middleware.test.js +2 -25
  75. package/test/nullable-validation.test.js +51 -14
  76. package/test/plugin.test.js +31 -5
  77. package/test/pretty-print.test.js +22 -10
  78. package/test/reply-error.test.js +123 -12
  79. package/test/request-error.test.js +2 -5
  80. package/test/route-hooks.test.js +17 -17
  81. package/test/route-prefix.test.js +2 -1
  82. package/test/route.test.js +204 -20
  83. package/test/router-options.test.js +1 -1
  84. package/test/schema-examples.test.js +11 -5
  85. package/test/schema-feature.test.js +24 -19
  86. package/test/schema-serialization.test.js +9 -9
  87. package/test/schema-special-usage.test.js +14 -81
  88. package/test/schema-validation.test.js +9 -9
  89. package/test/skip-reply-send.test.js +1 -1
  90. package/test/stream.test.js +23 -12
  91. package/test/throw.test.js +8 -5
  92. package/test/type-provider.test.js +20 -0
  93. package/test/types/fastify.test-d.ts +10 -18
  94. package/test/types/import.js +2 -0
  95. package/test/types/import.ts +1 -0
  96. package/test/types/instance.test-d.ts +35 -14
  97. package/test/types/logger.test-d.ts +44 -15
  98. package/test/types/route.test-d.ts +8 -2
  99. package/test/types/schema.test-d.ts +2 -39
  100. package/test/types/type-provider.test-d.ts +417 -0
  101. package/test/validation-error-handling.test.js +8 -8
  102. package/test/versioned-routes.test.js +28 -16
  103. package/test/wrapThenable.test.js +7 -6
  104. package/types/content-type-parser.d.ts +17 -8
  105. package/types/hooks.d.ts +102 -59
  106. package/types/instance.d.ts +124 -104
  107. package/types/logger.d.ts +18 -104
  108. package/types/plugin.d.ts +10 -4
  109. package/types/reply.d.ts +16 -11
  110. package/types/request.d.ts +10 -5
  111. package/types/route.d.ts +42 -31
  112. package/types/schema.d.ts +1 -1
  113. package/types/type-provider.d.ts +99 -0
  114. package/types/utils.d.ts +1 -1
  115. package/lib/schema-compilers.js +0 -12
  116. package/test/emit-warning.test.js +0 -166
@@ -47,6 +47,7 @@ describes the properties available in that options object.
47
47
  - [after](#after)
48
48
  - [ready](#ready)
49
49
  - [listen](#listen)
50
+ - [addresses](#addresses)
50
51
  - [getDefaultRoute](#getdefaultroute)
51
52
  - [setDefaultRoute](#setdefaultroute)
52
53
  - [routing](#routing)
@@ -126,7 +127,7 @@ property](https://nodejs.org/api/http.html#http_server_keepalivetimeout) to
126
127
  understand the effect of this option. This option only applies when HTTP/1 is in
127
128
  use. Also, when `serverFactory` option is specified, this option is ignored.
128
129
 
129
- + Default: `5000` (5 seconds)
130
+ + Default: `72000` (72 seconds)
130
131
 
131
132
  ### `forceCloseConnections`
132
133
  <a id="forcecloseconnections"></a>
@@ -553,7 +554,7 @@ Automatically creates a sibling `HEAD` route for each `GET` route defined. If
553
554
  you want a custom `HEAD` handler without disabling this option, make sure to
554
555
  define it before the `GET` route.
555
556
 
556
- + Default: `false`
557
+ + Default: `true`
557
558
 
558
559
  ### `constraints`
559
560
  <a id="constraints"></a>
@@ -599,28 +600,14 @@ the incoming request as usual.
599
600
  ### `ajv`
600
601
  <a id="factory-ajv"></a>
601
602
 
602
- Configure the Ajv v6 instance used by Fastify without providing a custom one.
603
-
604
- + Default:
605
-
606
- ```js
607
- {
608
- customOptions: {
609
- removeAdditional: true,
610
- useDefaults: true,
611
- coerceTypes: true,
612
- allErrors: false,
613
- nullable: true
614
- },
615
- plugins: []
616
- }
617
- ```
603
+ Configure the Ajv v8 instance used by Fastify without providing a custom one.
604
+ The default configuration is explained in the [#schema-validator](Validation-and-Serialization.md#schema-validator) section.
618
605
 
619
606
  ```js
620
607
  const fastify = require('fastify')({
621
608
  ajv: {
622
609
  customOptions: {
623
- nullable: false // Refer to [ajv options](https://github.com/ajv-validator/ajv/tree/v6#options)
610
+ removeAdditional: 'all' // Refer to [ajv options](https://ajv.js.org/#options)
624
611
  },
625
612
  plugins: [
626
613
  require('ajv-merge-patch'),
@@ -653,7 +640,7 @@ const fastify = require('fastify')({
653
640
  Set a default
654
641
  [timeout](https://nodejs.org/api/http2.html#http2_http2session_settimeout_msecs_callback)
655
642
  to every incoming HTTP/2 session. The session will be closed on the timeout.
656
- Default: `5000` ms.
643
+ Default: `72000` ms.
657
644
 
658
645
  Note that this is needed to offer the graceful "close" experience when using
659
646
  HTTP/2. The low default has been chosen to mitigate denial of service attacks.
@@ -834,14 +821,25 @@ fastify.ready().then(() => {
834
821
  <a id="listen"></a>
835
822
 
836
823
  Starts the server on the given port after all the plugins are loaded, internally
837
- waits for the `.ready()` event. The callback is the same as the Node core. By
838
- default, the server will listen on the address resolved by `localhost` when no
839
- specific address is provided (`127.0.0.1` or `::1` depending on the operating
840
- system). If listening on any available interface is desired, then specifying
841
- `0.0.0.0` for the address will listen on all IPv4 addresses. Using `::` for the
842
- address will listen on all IPv6 addresses and, depending on OS, may also listen
843
- on all IPv4 addresses. Be careful when deciding to listen on all interfaces; it
844
- comes with inherent [security
824
+ waits for the `.ready()` event. The callback is the same as the Node core.
825
+
826
+ By default, the server will listen on the address(es) resolved by `localhost` when no
827
+ specific address is provided. If listening on any available interface is desired,
828
+ then specifying `0.0.0.0` for the address will listen on all IPv4 addresses.
829
+
830
+ Host | IPv4 | IPv6
831
+ --------------|------|-------
832
+ `::` | ✅<sup>*</sup> | ✅
833
+ `::` + [`ipv6Only`](https://nodejs.org/api/net.html#serverlistenoptions-callback) | 🚫 | ✅
834
+ `0.0.0.0` | ✅ | 🚫
835
+ `localhost` | ✅ | ✅
836
+ `127.0.0.1` | ✅ | 🚫
837
+ `::1` | 🚫 | ✅
838
+
839
+ <sup>*</sup> Using `::` for the address will listen on all IPv6 addresses and, depending on OS,
840
+ may also listen on [all IPv4 addresses](https://nodejs.org/api/net.html#serverlistenport-host-backlog-callback).
841
+
842
+ Be careful when deciding to listen on all interfaces; it comes with inherent [security
845
843
  risks](https://web.archive.org/web/20170831174611/https://snyk.io/blog/mongodb-hack-and-secure-defaults/).
846
844
 
847
845
  ```js
@@ -950,6 +948,24 @@ fastify.listen({
950
948
  }, (err) => {})
951
949
  ```
952
950
 
951
+ #### addresses
952
+ <a id="addresses"></a>
953
+
954
+ This method returns an array of addresses that the server is listening on.
955
+ If you call it before `listen()` is called or after the `close()` function,
956
+ it will return an empty array.
957
+
958
+ ```js
959
+ await fastify.listen(8080)
960
+ const addresses = fastify.addresses()
961
+ // [
962
+ // { port: 8080, family: 'IPv6', address: '::1' },
963
+ // { port: 8080, family: 'IPv4', address: '127.0.0.1' }
964
+ // ]
965
+ ```
966
+
967
+ Note that the array contains the `fastify.server.address()` too.
968
+
953
969
  #### getDefaultRoute
954
970
  <a id="getDefaultRoute"></a>
955
971
 
@@ -1188,8 +1204,10 @@ unknown to Fastify. See [issue
1188
1204
  #2446](https://github.com/fastify/fastify/issues/2446) for an example of what
1189
1205
  this property helps to resolve.
1190
1206
 
1191
- Another use case is to tweak all the schemas processing. Doing so it is possible
1192
- to use Ajv v8, instead of the default v6! We will see an example of this later.
1207
+ Another use case is to tweak all the schemas processing.
1208
+ Doing so it is possible to use Ajv v8 JTD or Standalone feature. To use such
1209
+ as JTD or the Standalone mode, refers to the
1210
+ [`@fastify/ajv-compiler` documentation](https://github.com/fastify/ajv-compiler#usage).
1193
1211
 
1194
1212
  ```js
1195
1213
  const fastify = Fastify({
@@ -1264,39 +1282,6 @@ const fastify = Fastify({
1264
1282
  });
1265
1283
  ```
1266
1284
 
1267
- ##### Ajv 8 as default schema validator
1268
-
1269
- Ajv 8 is the evolution of Ajv 6, and it has a lot of improvements and new
1270
- features. To use the new Ajv 8 features such as JTD or the Standalone mode,
1271
- refer to the [`@fastify/ajv-compiler`
1272
- documentation](https://github.com/fastify/ajv-compiler#usage).
1273
-
1274
- To use Ajv 8 as default schema validator, you can use the following code:
1275
-
1276
- ```js
1277
- const AjvCompiler = require('@fastify/ajv-compiler') // It must be the v2.x.x version
1278
-
1279
- // Note that the `format` schema's keyword is no longer supported on Ajv 8 by default.
1280
- // So you need to add it manually.
1281
- const ajvFormats = require('ajv-formats')
1282
-
1283
- const app = fastify({
1284
- ajv: {
1285
- customOptions: {
1286
- validateFormats: true
1287
- },
1288
- plugins: [ajvFormats]
1289
- },
1290
- schemaController: {
1291
- compilersFactory: {
1292
- buildValidator: AjvCompiler()
1293
- }
1294
- }
1295
- })
1296
-
1297
- // Done! You can now use Ajv 8 options and keywords in your schemas!
1298
- ```
1299
-
1300
1285
  #### setNotFoundHandler
1301
1286
  <a id="set-not-found-handler"></a>
1302
1287
 
@@ -399,7 +399,7 @@ const todo = {
399
399
  done: { type: 'boolean' },
400
400
  },
401
401
  required: ['name'],
402
- } as const;
402
+ } as const; // don't forget to use const !
403
403
  ```
404
404
 
405
405
  With the provided type `FromSchema` you can build a type from your schema and
@@ -487,6 +487,7 @@ Fastify Plugin in a TypeScript Project.
487
487
  import fp from 'fastify-plugin'
488
488
 
489
489
  // using declaration merging, add your plugin props to the appropriate fastify interfaces
490
+ // if prop type is defined here, the value will be typechecked when you call decorate{,Request,Reply}
490
491
  declare module 'fastify' {
491
492
  interface FastifyRequest {
492
493
  myPluginProp: string
@@ -877,26 +878,23 @@ server.get('/', async (request, reply) => {
877
878
 
878
879
  ###### Example 5: Specifying logger types
879
880
 
880
- Fastify uses [Pino](https://getpino.io/#/) logging library under the hood. Some
881
- of it's properties can be configured via `logger` field when constructing
882
- Fastify's instance. If properties you need aren't exposed, it's also possible to
883
- pass a preconfigured external instance of Pino (or any other compatible logger)
884
- to Fastify via the same field. This allows creating custom serializers as well,
885
- see the [Logging](./Logging.md) documentation for more info.
886
-
887
- To use an external instance of Pino, add `@types/pino` to devDependencies and
888
- pass the instance to `logger` field:
881
+ Fastify uses [Pino](https://getpino.io/#/) logging library under the hood. Since
882
+ `pino@7`, all of it's properties can be configured via `logger` field when
883
+ constructing Fastify's instance. If properties you need aren't exposed, please
884
+ open an Issue to [`Pino`](https://github.com/pinojs/pino/issues) or pass a
885
+ preconfigured external instance of Pino (or any other compatible logger) as
886
+ temporary fix to Fastify via the same field. This allows creating custom
887
+ serializers as well, see the [Logging](Logging.md) documentation for more info.
889
888
 
890
889
  ```typescript
891
890
  import fastify from 'fastify'
892
- import pino from 'pino'
893
891
 
894
892
  const server = fastify({
895
- logger: pino({
893
+ logger: {
896
894
  level: 'info',
897
895
  redact: ['x-userinfo'],
898
896
  messageKey: 'message'
899
- })
897
+ }
900
898
  })
901
899
 
902
900
  server.get('/', async (request, reply) => {
@@ -9,6 +9,8 @@ highly performant function.
9
9
  Validation will only be attempted if the content type is `application-json`,
10
10
  as described in the documentation for the [content type parser](./ContentTypeParser.md).
11
11
 
12
+ All the examples in this section are using the [JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7) specification.
13
+
12
14
  > ## ⚠ Security Notice
13
15
  > Treat the schema definition as application code. Validation and serialization
14
16
  > features dynamically evaluate code with `new Function()`, which is not safe to
@@ -26,8 +28,7 @@ as described in the documentation for the [content type parser](./ContentTypePar
26
28
  ### Core concepts
27
29
  The validation and the serialization tasks are processed by two different, and
28
30
  customizable, actors:
29
- - [Ajv v6](https://www.npmjs.com/package/ajv/v/6.12.6) for the validation of a
30
- request
31
+ - [Ajv v8](https://www.npmjs.com/package/ajv) for the validation of a request
31
32
  - [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) for
32
33
  the serialization of a response's body
33
34
 
@@ -146,10 +147,11 @@ fastify.register((instance, opts, done) => {
146
147
 
147
148
 
148
149
  ### Validation
149
- The route validation internally relies upon [Ajv
150
- v6](https://www.npmjs.com/package/ajv/v/6.12.6) which is a high-performance JSON
151
- Schema validator. Validating the input is very easy: just add the fields that
152
- you need inside the route schema, and you are done!
150
+ The route validation internally relies upon
151
+ [Ajv v8](https://www.npmjs.com/package/ajv) which is a high-performance
152
+ JSON Schema validator.
153
+ Validating the input is very easy: just add the fields that you need
154
+ inside the route schema, and you are done!
153
155
 
154
156
  The supported validations are:
155
157
  - `body`: validates the body of the request if it is a POST, PUT, or PATCH
@@ -232,15 +234,13 @@ const schema = {
232
234
  fastify.post('/the/url', { schema }, handler)
233
235
  ```
234
236
 
235
- *Note that Ajv will try to
236
- [coerce](https://github.com/epoberezkin/ajv#coercing-data-types) the values to
237
+ *Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values to
237
238
  the types specified in your schema `type` keywords, both to pass the validation
238
239
  and to use the correctly typed data afterwards.*
239
240
 
240
- The Ajv default configuration in Fastify doesn't support coercing array
241
- parameters in querystring. However, Fastify allows
242
- [`customOptions`](./Server.md#ajv) in Ajv instance. The `coerceTypes: 'array'`
243
- will coerce one parameter to a single element in array. Example:
241
+ The Ajv default configuration in Fastify supports coercing array
242
+ parameters in `querystring`.
243
+ Example:
244
244
 
245
245
  ```js
246
246
  const opts = {
@@ -258,7 +258,7 @@ const opts = {
258
258
  }
259
259
 
260
260
  fastify.get('/', opts, (request, reply) => {
261
- reply.send({ params: request.query })
261
+ reply.send({ params: request.query }) // echo the querystring
262
262
  })
263
263
 
264
264
  fastify.listen(3000, (err) => {
@@ -266,29 +266,6 @@ fastify.listen(3000, (err) => {
266
266
  })
267
267
  ```
268
268
 
269
- Using Fastify defaults the following request will result in `400` status code:
270
-
271
- ```sh
272
- curl -X GET "http://localhost:3000/?ids=1
273
-
274
- {"statusCode":400,"error":"Bad Request","message":"querystring/hello should be array"}
275
- ```
276
-
277
- Using `coerceTypes` as 'array' will fix it:
278
-
279
- ```js
280
- const ajv = new Ajv({
281
- removeAdditional: true,
282
- useDefaults: true,
283
- coerceTypes: 'array', // This line
284
- allErrors: true
285
- })
286
-
287
- fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
288
- return ajv.compile(schema)
289
- })
290
- ```
291
-
292
269
  ```sh
293
270
  curl -X GET "http://localhost:3000/?ids=1
294
271
 
@@ -342,8 +319,8 @@ For further information see [here](https://ajv.js.org/coercion.html)
342
319
  #### Ajv Plugins
343
320
  <a id="ajv-plugins"></a>
344
321
 
345
- You can provide a list of plugins you want to use with the default `ajv`
346
- instance. Note that the plugin must be **compatible with Ajv v6**.
322
+ You can provide a list of plugins you want to use with the default `ajv` instance.
323
+ Note that the plugin must be **compatible with the Ajv version shipped within Fastify**.
347
324
 
348
325
  > Refer to [`ajv options`](./Server.md#ajv) to check plugins format
349
326
 
@@ -412,16 +389,16 @@ body, URL parameters, headers, and query string. The default
412
389
  [ajv](https://ajv.js.org/) validation interface. Fastify uses it internally to
413
390
  speed the validation up.
414
391
 
415
- Fastify's [baseline ajv
416
- configuration](https://github.com/epoberezkin/ajv#options-to-modify-validated-data)
417
- is:
392
+ Fastify's [baseline ajv configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:
418
393
 
419
394
  ```js
420
395
  {
421
- removeAdditional: true, // remove additional properties
422
- useDefaults: true, // replace missing properties and items with the values from corresponding default keyword
423
396
  coerceTypes: true, // change data type of data to match type keyword
424
- nullable: true // support keyword "nullable" from Open API 3 specification.
397
+ useDefaults: true, // replace missing properties and items with the values from corresponding default keyword
398
+ removeAdditional: true, // remove additional properties
399
+ // Explicitly set allErrors to `false`.
400
+ // When set to `true`, a DoS attack is possible.
401
+ allErrors: false
425
402
  }
426
403
  ```
427
404
 
@@ -435,11 +412,9 @@ your own instance and override the existing one like:
435
412
  const fastify = require('fastify')()
436
413
  const Ajv = require('ajv')
437
414
  const ajv = new Ajv({
438
- // the fastify defaults (if needed)
439
- removeAdditional: true,
415
+ removeAdditional: 'all',
440
416
  useDefaults: true,
441
- coerceTypes: true,
442
- nullable: true,
417
+ coerceTypes: 'array',
443
418
  // any other options
444
419
  // ...
445
420
  })
@@ -460,7 +435,7 @@ almost any Javascript validation library ([joi](https://github.com/hapijs/joi/),
460
435
  [yup](https://github.com/jquense/yup/), ...) or a custom one:
461
436
 
462
437
  ```js
463
- const Joi = require('@hapi/joi')
438
+ const Joi = require('joi')
464
439
 
465
440
  fastify.post('/the/url', {
466
441
  schema: {
@@ -512,8 +487,8 @@ fastify.post('/the/url', {
512
487
 
513
488
  Fastify's validation error messages are tightly coupled to the default
514
489
  validation engine: errors returned from `ajv` are eventually run through the
515
- `schemaErrorsText` function which is responsible for building human-friendly
516
- error messages. However, the `schemaErrorsText` function is written with `ajv`
490
+ `schemaErrorFormatter` function which is responsible for building human-friendly
491
+ error messages. However, the `schemaErrorFormatter` function is written with `ajv`
517
492
  in mind : as a result, you may run into odd or incomplete error messages when
518
493
  using other validation libraries.
519
494
 
@@ -529,9 +504,9 @@ To circumvent this issue, you have 2 main options :
529
504
  To help you in writing a custom `errorHandler`, Fastify adds 2 properties to all
530
505
  validation errors:
531
506
 
532
- * validation: the content of the `error` property of the object returned by the
507
+ * `validation`: the content of the `error` property of the object returned by the
533
508
  validation function (returned by your custom `schemaCompiler`)
534
- * validationContext: the 'context' (body, params, query, headers) where the
509
+ * `validationContext`: the 'context' (body, params, query, headers) where the
535
510
  validation error occurred
536
511
 
537
512
  A very contrived example of such a custom `errorHandler` handling validation
@@ -0,0 +1,257 @@
1
+ <h1 align="center">Fastify</h1>
2
+
3
+ ## Type Providers
4
+
5
+ Type Providers are a TypeScript only feature that enables Fastify to statically infer type information directly from inline JSON Schema. They are an alternative to specifying generic arguments on routes; and can greatly reduce the need to keep associated types for each schema defined in your project.
6
+
7
+ ### Providers
8
+
9
+ Type Providers are offered as additional packages you will need to install into your project. Each provider uses a different inference library under the hood; allowing you to select the library most appropriate for your needs. Type Provider packages follow a `fastify-type-provider-{provider-name}` naming convention.
10
+
11
+ The following inference packages are supported:
12
+
13
+ - `json-schema-to-ts` - [github](https://github.com/ThomasAribart/json-schema-to-ts)
14
+ - `typebox` - [github](https://github.com/sinclairzx81/typebox)
15
+
16
+ ### Json Schema to Ts
17
+
18
+ The following sets up a `json-schema-to-ts` Type Provider
19
+
20
+ ```bash
21
+ $ npm install fastify-type-provider-json-schema-to-ts --save
22
+ ```
23
+
24
+ ```typescript
25
+ import { JsonSchemaToTsTypeProvider } from 'fastify-type-provider-json-schema-to-ts'
26
+
27
+ import fastify from 'fastify'
28
+
29
+ const server = fastify().withTypeProvider<JsonSchemaToTsTypeProvider>()
30
+
31
+ server.get('/route', {
32
+ schema: {
33
+ querystring: {
34
+ type: 'object',
35
+ properties: {
36
+ foo: { type: 'number' },
37
+ bar: { type: 'string' },
38
+ },
39
+ required: ['foo', 'bar']
40
+ }
41
+ } as const // don't forget to use const !
42
+
43
+ }, (request, reply) => {
44
+
45
+ // type Query = { foo: number, bar: string }
46
+
47
+ const { foo, bar } = request.query // type safe!
48
+ })
49
+ ```
50
+
51
+ ### TypeBox
52
+
53
+ The following sets up a TypeBox Type Provider
54
+
55
+ ```bash
56
+ $ npm install fastify-type-provider-typebox --save
57
+ ```
58
+
59
+ ```typescript
60
+ import { TypeBoxTypeProvider, Type } from 'fastify-type-provider-typebox'
61
+
62
+ import fastify from 'fastify'
63
+
64
+ const server = fastify({
65
+ ajv: {
66
+ customOptions: {
67
+ strict: 'log',
68
+ keywords: ['kind', 'modifier'],
69
+ },
70
+ },
71
+ }).withTypeProvider<TypeBoxTypeProvider>()
72
+
73
+ server.get('/route', {
74
+ schema: {
75
+ querystring: Type.Object({
76
+ foo: Type.Number(),
77
+ bar: Type.String()
78
+ })
79
+ }
80
+ }, (request, reply) => {
81
+
82
+ // type Query = { foo: number, bar: string }
83
+
84
+ const { foo, bar } = request.query // type safe!
85
+ })
86
+ ```
87
+
88
+ TypeBox uses the properties `kind` and `modifier` internally. These properties are not strictly valid JSON schema which will cause `AJV@7` and newer versions to throw an invalid schema error. To remove the error it's either necessary to omit the properties by using [`Type.Strict()`](https://github.com/sinclairzx81/typebox#strict) or use the AJV options for adding custom keywords.
89
+
90
+ See also the [TypeBox documentation](https://github.com/sinclairzx81/typebox#validation) on how to set up AJV to work with TypeBox.
91
+
92
+ ### Scoped Type-Provider
93
+
94
+ The provider types don't propagate globally. In encapsulated usage, one can remap the context to use one or more providers (for example, `typebox` and `json-schema-to-ts` can be used in the same application).
95
+
96
+ Example:
97
+
98
+ ```ts
99
+ import Fastify from 'fastify'
100
+ import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
101
+ import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
102
+ import { Type } from '@sinclair/typebox'
103
+
104
+ const fastify = Fastify()
105
+
106
+ function pluginWithTypebox(fastify: FastifyInstance, _opts, done): void {
107
+ fastify.withTypeProvider<TypeBoxTypeProvider>()
108
+ .get('/', {
109
+ schema: {
110
+ body: Type.Object({
111
+ x: Type.String(),
112
+ y: Type.Number(),
113
+ z: Type.Boolean()
114
+ })
115
+ }
116
+ }, (req) => {
117
+ const { x, y, z } = req.body // type safe
118
+ });
119
+ done()
120
+ }
121
+
122
+ function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void {
123
+ fastify.withTypeProvider<JsonSchemaToTsProvider>()
124
+ .get('/', {
125
+ schema: {
126
+ body: {
127
+ type: 'object',
128
+ properties: {
129
+ x: { type: 'string' },
130
+ y: { type: 'number' },
131
+ z: { type: 'boolean' }
132
+ },
133
+ } as const
134
+ }
135
+ }, (req) => {
136
+ const { x, y, z } = req.body // type safe
137
+ });
138
+ done()
139
+ }
140
+
141
+ fastify.register(pluginWithJsonSchema)
142
+ fastify.register(pluginWithTypebox)
143
+ ```
144
+
145
+ It's also important to mention that once the types don't propagate globally, _currently_ is not possible to avoid multiple registrations on routes when dealing with several scopes, see bellow:
146
+
147
+ ```ts
148
+ import Fastify from 'fastify'
149
+ import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
150
+ import { Type } from '@sinclair/typebox'
151
+
152
+ const server = Fastify({
153
+ ajv: {
154
+ customOptions: {
155
+ strict: 'log',
156
+ keywords: ['kind', 'modifier'],
157
+ },
158
+ },
159
+ }).withTypeProvider<TypeBoxTypeProvider>()
160
+
161
+ server.register(plugin1) // wrong
162
+ server.register(plugin2) // correct
163
+
164
+ function plugin1(fastify: FastifyInstance, _opts, done): void {
165
+ fastify.get('/', {
166
+ schema: {
167
+ body: Type.Object({
168
+ x: Type.String(),
169
+ y: Type.Number(),
170
+ z: Type.Boolean()
171
+ })
172
+ }
173
+ }, (req) => {
174
+ // it doesn't works! in a new scope needs to call `withTypeProvider` again
175
+ const { x, y, z } = req.body
176
+ });
177
+ done()
178
+ }
179
+
180
+ function plugin2(fastify: FastifyInstance, _opts, done): void {
181
+ const server = fastify.withTypeProvider<TypeBoxTypeProvider>()
182
+
183
+ server.get('/', {
184
+ schema: {
185
+ body: Type.Object({
186
+ x: Type.String(),
187
+ y: Type.Number(),
188
+ z: Type.Boolean()
189
+ })
190
+ }
191
+ }, (req) => {
192
+ // works
193
+ const { x, y, z } = req.body
194
+ });
195
+ done()
196
+ }
197
+ ```
198
+
199
+ ### Type Definition of FastifyInstance + TypeProvider
200
+
201
+ When working with modules one has to make use of `FastifyInstance` with Type Provider generics. See the example below:
202
+
203
+ ```ts
204
+ // index.ts
205
+ import Fastify from 'fastify'
206
+ import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
207
+ import { registerRoutes } from './routes'
208
+
209
+ const server = Fastify({
210
+ ajv: {
211
+ customOptions: {
212
+ strict: 'log',
213
+ keywords: ['kind', 'modifier'],
214
+ },
215
+ },
216
+ }).withTypeProvider<TypeBoxTypeProvider>()
217
+
218
+ registerRoutes(server)
219
+
220
+ server.listen(3000)
221
+ ```
222
+
223
+ ```ts
224
+ // routes.ts
225
+ import { Type } from '@sinclair/typebox'
226
+ import {
227
+ FastifyInstance,
228
+ FastifyLoggerInstance,
229
+ RawReplyDefaultExpression,
230
+ RawRequestDefaultExpression,
231
+ RawServerDefault
232
+ } from 'fastify'
233
+ import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
234
+
235
+ type FastifyTypebox = FastifyInstance<
236
+ RawServerDefault,
237
+ RawRequestDefaultExpression<RawServerDefault>,
238
+ RawReplyDefaultExpression<RawServerDefault>,
239
+ FastifyLoggerInstance,
240
+ TypeBoxTypeProvider
241
+ >;
242
+
243
+ export function registerRoutes(fastify: FastifyTypebox): void {
244
+ fastify.get('/', {
245
+ schema: {
246
+ body: Type.Object({
247
+ x: Type.String(),
248
+ y: Type.Number(),
249
+ z: Type.Boolean()
250
+ })
251
+ }
252
+ }, (req) => {
253
+ // works
254
+ const { x, y, z } = req.body
255
+ });
256
+ }
257
+ ```
package/examples/hooks.js CHANGED
@@ -37,7 +37,7 @@ fastify
37
37
  console.log('onRequest')
38
38
  done()
39
39
  })
40
- .addHook('preParsing', function (request, reply, done) {
40
+ .addHook('preParsing', function (request, reply, payload, done) {
41
41
  console.log('preParsing')
42
42
  done()
43
43
  })
@@ -0,0 +1,18 @@
1
+ 'use strict'
2
+
3
+ const fastify = require('../fastify')({
4
+ logger: false
5
+ })
6
+
7
+ const Readable = require('stream').Readable
8
+
9
+ fastify
10
+ .get('/', function (req, reply) {
11
+ const stream = Readable.from(['hello world'])
12
+ reply.send(stream)
13
+ })
14
+
15
+ fastify.listen(3000, (err, address) => {
16
+ if (err) throw err
17
+ fastify.log.info(`server listening on ${address}`)
18
+ })