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.
- package/README.md +5 -4
- package/build/build-error-serializer.js +27 -0
- package/build/build-validation.js +47 -35
- package/docs/Migration-Guide-V4.md +12 -0
- package/docs/Reference/ContentTypeParser.md +4 -0
- package/docs/Reference/Errors.md +51 -6
- package/docs/Reference/Hooks.md +4 -7
- package/docs/Reference/LTS.md +5 -4
- package/docs/Reference/Reply.md +23 -22
- package/docs/Reference/Request.md +1 -3
- package/docs/Reference/Routes.md +17 -10
- package/docs/Reference/Server.md +48 -63
- package/docs/Reference/TypeScript.md +11 -13
- package/docs/Reference/Validation-and-Serialization.md +28 -53
- package/docs/Type-Providers.md +257 -0
- package/examples/hooks.js +1 -1
- package/examples/simple-stream.js +18 -0
- package/fastify.d.ts +34 -22
- package/fastify.js +37 -35
- package/lib/configValidator.js +902 -1023
- package/lib/contentTypeParser.js +6 -16
- package/lib/context.js +36 -10
- package/lib/decorate.js +3 -1
- package/lib/error-handler.js +158 -0
- package/lib/error-serializer.js +257 -0
- package/lib/errors.js +43 -9
- package/lib/fourOhFour.js +31 -20
- package/lib/handleRequest.js +10 -13
- package/lib/hooks.js +14 -9
- package/lib/pluginOverride.js +0 -3
- package/lib/pluginUtils.js +3 -2
- package/lib/reply.js +28 -157
- package/lib/request.js +13 -10
- package/lib/route.js +131 -138
- package/lib/schema-controller.js +2 -2
- package/lib/schemas.js +27 -1
- package/lib/server.js +219 -116
- package/lib/symbols.js +4 -3
- package/lib/validation.js +2 -1
- package/lib/warnings.js +2 -12
- package/lib/wrapThenable.js +4 -11
- package/package.json +31 -35
- package/test/404s.test.js +243 -110
- package/test/500s.test.js +2 -2
- package/test/async-await.test.js +13 -69
- package/test/content-parser.test.js +32 -0
- package/test/context-config.test.js +52 -0
- package/test/custom-http-server.test.js +14 -7
- package/test/custom-parser-async.test.js +0 -65
- package/test/custom-parser.test.js +54 -121
- package/test/decorator.test.js +1 -3
- package/test/delete.test.js +5 -5
- package/test/encapsulated-error-handler.test.js +50 -0
- package/test/esm/index.test.js +0 -14
- package/test/fastify-instance.test.js +4 -4
- package/test/fluent-schema.test.js +4 -4
- package/test/get.test.js +3 -3
- package/test/helper.js +18 -3
- package/test/hooks-async.test.js +14 -47
- package/test/hooks.on-ready.test.js +9 -4
- package/test/hooks.test.js +58 -99
- package/test/http2/closing.test.js +5 -11
- package/test/http2/unknown-http-method.test.js +3 -9
- package/test/https/custom-https-server.test.js +12 -6
- package/test/input-validation.js +2 -2
- package/test/internals/handleRequest.test.js +3 -40
- package/test/internals/initialConfig.test.js +33 -12
- package/test/internals/reply.test.js +245 -3
- package/test/internals/request.test.js +13 -7
- package/test/internals/server.test.js +88 -0
- package/test/listen.test.js +84 -1
- package/test/logger.test.js +80 -40
- package/test/maxRequestsPerSocket.test.js +6 -4
- package/test/middleware.test.js +2 -25
- package/test/nullable-validation.test.js +51 -14
- package/test/plugin.test.js +31 -5
- package/test/pretty-print.test.js +22 -10
- package/test/reply-error.test.js +123 -12
- package/test/request-error.test.js +2 -5
- package/test/route-hooks.test.js +17 -17
- package/test/route-prefix.test.js +2 -1
- package/test/route.test.js +204 -20
- package/test/router-options.test.js +1 -1
- package/test/schema-examples.test.js +11 -5
- package/test/schema-feature.test.js +24 -19
- package/test/schema-serialization.test.js +9 -9
- package/test/schema-special-usage.test.js +14 -81
- package/test/schema-validation.test.js +9 -9
- package/test/skip-reply-send.test.js +1 -1
- package/test/stream.test.js +23 -12
- package/test/throw.test.js +8 -5
- package/test/type-provider.test.js +20 -0
- package/test/types/fastify.test-d.ts +10 -18
- package/test/types/import.js +2 -0
- package/test/types/import.ts +1 -0
- package/test/types/instance.test-d.ts +35 -14
- package/test/types/logger.test-d.ts +44 -15
- package/test/types/route.test-d.ts +8 -2
- package/test/types/schema.test-d.ts +2 -39
- package/test/types/type-provider.test-d.ts +417 -0
- package/test/validation-error-handling.test.js +8 -8
- package/test/versioned-routes.test.js +28 -16
- package/test/wrapThenable.test.js +7 -6
- package/types/content-type-parser.d.ts +17 -8
- package/types/hooks.d.ts +102 -59
- package/types/instance.d.ts +124 -104
- package/types/logger.d.ts +18 -104
- package/types/plugin.d.ts +10 -4
- package/types/reply.d.ts +16 -11
- package/types/request.d.ts +10 -5
- package/types/route.d.ts +42 -31
- package/types/schema.d.ts +1 -1
- package/types/type-provider.d.ts +99 -0
- package/types/utils.d.ts +1 -1
- package/lib/schema-compilers.js +0 -12
- package/test/emit-warning.test.js +0 -166
package/docs/Reference/Server.md
CHANGED
|
@@ -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: `
|
|
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: `
|
|
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
|
|
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
|
-
|
|
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: `
|
|
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.
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
`0.0.0.0` for the address will listen on all IPv4 addresses.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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.
|
|
1192
|
-
|
|
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.
|
|
881
|
-
of it's properties can be configured via `logger` field when
|
|
882
|
-
Fastify's instance. If properties you need aren't exposed,
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
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:
|
|
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
|
|
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
|
|
150
|
-
|
|
151
|
-
Schema validator.
|
|
152
|
-
|
|
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
|
|
241
|
-
parameters in querystring
|
|
242
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
439
|
-
removeAdditional: true,
|
|
415
|
+
removeAdditional: 'all',
|
|
440
416
|
useDefaults: true,
|
|
441
|
-
coerceTypes:
|
|
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('
|
|
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
|
-
`
|
|
516
|
-
error messages. However, the `
|
|
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
|
|
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
|
|
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
|
@@ -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
|
+
})
|