fastify 3.22.0 → 3.24.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 (42) hide show
  1. package/build/build-validation.js +2 -0
  2. package/build/sync-version.js +11 -0
  3. package/docs/Getting-Started.md +14 -0
  4. package/docs/Hooks.md +1 -1
  5. package/docs/Recommendations.md +55 -19
  6. package/docs/Request.md +1 -1
  7. package/docs/Routes.md +2 -0
  8. package/docs/Server.md +13 -1
  9. package/docs/TypeScript.md +3 -0
  10. package/docs/Validation-and-Serialization.md +10 -2
  11. package/fastify.d.ts +15 -0
  12. package/fastify.js +7 -24
  13. package/lib/configValidator.js +455 -423
  14. package/lib/logger.js +1 -1
  15. package/lib/pluginUtils.js +1 -3
  16. package/lib/reply.js +8 -3
  17. package/lib/request.js +9 -3
  18. package/lib/schema-controller.js +11 -2
  19. package/lib/server.js +1 -0
  20. package/lib/symbols.js +1 -0
  21. package/package.json +5 -5
  22. package/test/bundler/README.md +7 -7
  23. package/test/bundler/webpack/bundler-test.js +2 -6
  24. package/test/internals/initialConfig.test.js +2 -0
  25. package/test/internals/logger.test.js +20 -0
  26. package/test/internals/reply.test.js +43 -1
  27. package/test/internals/request.test.js +47 -0
  28. package/test/internals/version.test.js +6 -34
  29. package/test/reply-error.test.js +28 -0
  30. package/test/requestTimeout.test.js +50 -0
  31. package/test/route-hooks.test.js +55 -0
  32. package/test/schema-feature.test.js +315 -0
  33. package/test/schema-special-usage.test.js +1 -1
  34. package/test/stream.test.js +2 -2
  35. package/test/types/content-type-parser.test-d.ts +1 -1
  36. package/test/types/fastify.test-d.ts +15 -0
  37. package/test/types/instance.test-d.ts +1 -1
  38. package/test/types/logger.test-d.ts +1 -1
  39. package/test/types/register.test-d.ts +13 -1
  40. package/types/instance.d.ts +1 -1
  41. package/types/register.d.ts +2 -1
  42. package/out +0 -431
@@ -16,6 +16,7 @@ const defaultInitOptions = {
16
16
  connectionTimeout: 0, // 0 sec
17
17
  keepAliveTimeout: 5000, // 5 sec
18
18
  maxRequestsPerSocket: 0, // no limit
19
+ requestTimeout: 0, // no limit
19
20
  bodyLimit: 1024 * 1024, // 1 MiB
20
21
  caseSensitive: true,
21
22
  disableRequestLogging: false,
@@ -49,6 +50,7 @@ const schema = {
49
50
  connectionTimeout: { type: 'integer', default: defaultInitOptions.connectionTimeout },
50
51
  keepAliveTimeout: { type: 'integer', default: defaultInitOptions.keepAliveTimeout },
51
52
  maxRequestsPerSocket: { type: 'integer', default: defaultInitOptions.maxRequestsPerSocket, nullable: true },
53
+ requestTimeout: { type: 'integer', default: defaultInitOptions.requestTimeout },
52
54
  bodyLimit: { type: 'integer', default: defaultInitOptions.bodyLimit },
53
55
  caseSensitive: { type: 'boolean', default: defaultInitOptions.caseSensitive },
54
56
  http2: { type: 'boolean' },
@@ -0,0 +1,11 @@
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+
6
+ // package.json:version -> fastify.js:VERSION
7
+ const { version } = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString('utf8'))
8
+
9
+ const fastifyJs = path.join(__dirname, '..', 'fastify.js')
10
+
11
+ fs.writeFileSync(fastifyJs, fs.readFileSync(fastifyJs).toString('utf8').replace(/const\s*VERSION\s*=.*/, `const VERSION = '${version}'`))
@@ -363,6 +363,20 @@ fastify.get('/', opts, async (request, reply) => {
363
363
  By specifying a schema as shown, you can speed up serialization by a factor of 2-3. This also helps to protect against leakage of potentially sensitive data, since Fastify will serialize only the data present in the response schema.
364
364
  Read [Validation and Serialization](Validation-and-Serialization.md) to learn more.
365
365
 
366
+ <a name="request-payload"></a>
367
+ ### Parsing request payloads
368
+ Fastify parses `'application/json'` and `'text/plain'` request payloads natively, with the result accessible from the [Fastify request](Request.md) object at `request.body`.<br>
369
+ The following example returns the parsed body of a request back to the client:
370
+
371
+ ```js
372
+ const opts = {}
373
+ fastify.post('/', opts, async (request, reply) => {
374
+ return request.body
375
+ })
376
+ ```
377
+
378
+ Read [Content Type Parser](ContentTypeParser.md) to learn more about Fastify's default parsing functionality and how to support other content types.
379
+
366
380
  <a name="extend-server"></a>
367
381
  ### Extend your server
368
382
  Fastify is built to be extremely extensible and minimal, we believe that a bare-bones framework is all that is necessary to make great applications possible.<br>
package/docs/Hooks.md CHANGED
@@ -222,7 +222,7 @@ fastify.addHook('onTimeout', async (request, reply) => {
222
222
  await asyncMethod()
223
223
  })
224
224
  ```
225
- `onTimeout` is useful if you need to monitor the request timed out in your service (if the `connectionTimeout` property is set on the Fastify instance). The `onTimeout` hook is executed when a request is timed out and the HTTP socket has been hanged up. Therefore ,you will not be able to send data to the client.
225
+ `onTimeout` is useful if you need to monitor the request timed out in your service (if the `connectionTimeout` property is set on the Fastify instance). The `onTimeout` hook is executed when a request is timed out and the HTTP socket has been hanged up. Therefore, you will not be able to send data to the client.
226
226
 
227
227
 
228
228
  ### Manage Errors from a hook
@@ -166,62 +166,94 @@ backend static-backend
166
166
  ### Nginx
167
167
 
168
168
  ```nginx
169
+ # This upstream block groups 3 servers into one named backend fastify_app
170
+ # with 2 primary servers distributed via round-robin
171
+ # and one backup which is used when the first 2 are not reachable
172
+ # This also assumes your fastify servers are listening on port 80.
173
+ # more info: http://nginx.org/en/docs/http/ngx_http_upstream_module.html
169
174
  upstream fastify_app {
170
- # more info: http://nginx.org/en/docs/http/ngx_http_upstream_module.html
171
175
  server 10.10.11.1:80;
172
176
  server 10.10.11.2:80;
173
177
  server 10.10.11.3:80 backup;
174
178
  }
175
179
 
180
+ # This server block asks NGINX to respond with a redirect when
181
+ # an incoming request from port 80 (typically plain HTTP), to
182
+ # the same request URL but with HTTPS as protocol.
183
+ # This block is optional, and usually used if you are handling
184
+ # SSL termination in NGINX, like in the example here.
176
185
  server {
177
- # default server
186
+ # default server is a special parameter to ask NGINX
187
+ # to set this server block to the default for this address/port
188
+ # which in this case is any address and port 80
178
189
  listen 80 default_server;
179
190
  listen [::]:80 default_server;
180
191
 
181
- # specify host
192
+ # With a server_name directive you can also ask NGINX to
193
+ # use this server block only with matching server name(s)
182
194
  # listen 80;
183
195
  # listen [::]:80;
184
196
  # server_name example.tld;
185
197
 
198
+ # This matches all paths from the request and responds with
199
+ # the redirect mentioned above.
186
200
  location / {
187
201
  return 301 https://$host$request_uri;
188
202
  }
189
203
  }
190
204
 
205
+ # This server block asks NGINX to respond to requests from
206
+ # port 443 with SSL enabled and accept HTTP/2 connections.
207
+ # This is where the request is then proxied to the fastify_app
208
+ # server group via port 3000.
191
209
  server {
192
- # default server
210
+ # This listen directive asks NGINX to accept requests
211
+ # coming to any address, port 443, with SSL, and HTTP/2
212
+ # if possible.
193
213
  listen 443 ssl http2 default_server;
194
214
  listen [::]:443 ssl http2 default_server;
195
-
196
- # specify host
215
+
216
+ # With a server_name directive you can also ask NGINX to
217
+ # use this server block only with matching server name(s)
197
218
  # listen 443 ssl http2;
198
219
  # listen [::]:443 ssl http2;
199
220
  # server_name example.tld;
200
221
 
201
- # public private keys
222
+ # Your SSL/TLS certificate (chain) and secret key in the PEM format
202
223
  ssl_certificate /path/to/fullchain.pem;
203
224
  ssl_certificate_key /path/to/private.pem;
204
- ssl_trusted_certificate /path/to/chain.pem;
205
225
 
206
- # use https://ssl-config.mozilla.org/ for best practice configuration
226
+ # A generic best practice baseline for based
227
+ # on https://ssl-config.mozilla.org/
207
228
  ssl_session_timeout 1d;
208
229
  ssl_session_cache shared:FastifyApp:10m;
209
230
  ssl_session_tickets off;
210
-
211
- # modern configuration
231
+
232
+ # This tells NGINX to only accept TLS 1.3, which should be fine
233
+ # with most modern browsers including IE 11 with certain updates.
234
+ # If you want to support older browsers you might need to add
235
+ # additional fallback protocols.
212
236
  ssl_protocols TLSv1.3;
213
237
  ssl_prefer_server_ciphers off;
214
-
215
- # HSTS (ngx_http_headers_module is required) (63072000 seconds)
238
+
239
+ # This adds a header that tells browsers to only ever use HTTPS
240
+ # with this server.
216
241
  add_header Strict-Transport-Security "max-age=63072000" always;
217
-
218
- # OCSP stapling
242
+
243
+ # The following directives are only necessary if you want to
244
+ # enable OCSP Stapling.
219
245
  ssl_stapling on;
220
246
  ssl_stapling_verify on;
247
+ ssl_trusted_certificate /path/to/chain.pem;
221
248
 
222
- # custom resolver
249
+ # Custom nameserver to resolve upstream server names
223
250
  # resolver 127.0.0.1;
224
-
251
+
252
+ # This section matches all paths and proxies it to the backend server
253
+ # group specified above. Note the additional headers that forward
254
+ # information about the original request. You might want to set
255
+ # trustProxy to the address of your NGINX server so the X-Forwarded
256
+ * fields are used by fastify.
225
257
  location / {
226
258
  # more info: http://nginx.org/en/docs/http/ngx_http_proxy_module.html
227
259
  proxy_http_version 1.1;
@@ -232,8 +264,12 @@ server {
232
264
  proxy_set_header X-Real-IP $remote_addr;
233
265
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
234
266
  proxy_set_header X-Forwarded-Proto $scheme;
235
-
236
- proxy_pass http://fastify_app:3000;
267
+
268
+ # This is the directive that proxies requests to the specified server.
269
+ # If you are using an upstream group, then you do not need to specify a port.
270
+ # If you are directly proxying to a server e.g.
271
+ # proxy_pass http://127.0.0.1:3000 then specify a port.
272
+ proxy_pass http://fastify_app;
237
273
  }
238
274
  }
239
275
  ```
package/docs/Request.md CHANGED
@@ -4,7 +4,7 @@
4
4
  The first parameter of the handler function is `Request`.<br>
5
5
  Request is a core Fastify object containing the following fields:
6
6
  - `query` - the parsed querystring, its format is specified by [`querystringParser`](Server.md#querystringparser)
7
- - `body` - the body
7
+ - `body` - the request payload, see [Content Type Parser](ContentTypeParser.md) for details on what request payloads Fastify natively parses and how to support other content types
8
8
  - `params` - the params matching the URL
9
9
  - [`headers`](#headers) - the headers getter and setter
10
10
  - `raw` - the incoming HTTP request from Node core
package/docs/Routes.md CHANGED
@@ -50,6 +50,8 @@ They need to be in
50
50
  * `preSerialization(request, reply, payload, done)`: a [function](Hooks.md#preserialization) called just before the serialization, it could also be an array of functions.
51
51
  * `onSend(request, reply, payload, done)`: a [function](Hooks.md#route-hooks) called right before a response is sent, it could also be an array of functions.
52
52
  * `onResponse(request, reply, done)`: a [function](Hooks.md#onresponse) called when a response has been sent, so you will not be able to send more data to the client. It could also be an array of functions.
53
+ * `onTimeout(request, reply, done)`: a [function](Hooks.md#ontimeout) called when a request is timed out and the HTTP socket has been hanged up.
54
+ * `onError(request, reply, error, done)`: a [function](Hooks.md#onerror) called when an Error is thrown or send to the client by the route handler.
53
55
  * `handler(request, reply)`: the function that will handle this request. The [Fastify server](Server.md) will be bound to `this` when the handler is called. Note: using an arrow function will break the binding of `this`.
54
56
  * `errorHandler(error, request, reply)`: a custom error handler for the scope of the request. Overrides the default error global handler, and anything set by [`setErrorHandler`](Server.md#setErrorHandler), for requests to the route. To access the default handler, you can access `instance.errorHandler`. Note that this will point to fastify's default `errorHandler` only if a plugin hasn't overridden it already.
55
57
  * `validatorCompiler({ schema, method, url, httpPart })`: function that builds schemas for request validations. See the [Validation and Serialization](Validation-and-Serialization.md#schema-validator) documentation.
package/docs/Server.md CHANGED
@@ -13,6 +13,7 @@ document describes the properties available in that options object.
13
13
  - [connectionTimeout](./Server.md#connectiontimeout)
14
14
  - [keepAliveTimeout](./Server.md#keepalivetimeout)
15
15
  - [maxRequestsPerSocket](./Server.md#maxRequestsPerSocket)
16
+ - [requestTimeout](./Server.md#requestTimeout)
16
17
  - [ignoreTrailingSlash](./Server.md#ignoretrailingslash)
17
18
  - [maxParamLength](./Server.md#maxparamlength)
18
19
  - [onProtoPoisoning](./Server.md#onprotopoisoning)
@@ -94,6 +95,17 @@ is in use. Also, when `serverFactory` option is specified, this option is ignore
94
95
 
95
96
  + Default: `0` (no limit)
96
97
 
98
+ <a name="factory-request-timeout"></a>
99
+ ### `requestTimeout`
100
+
101
+ Defines the maximum number of milliseconds for receiving the entire request from the client.
102
+ [`server.requestTimeout` property](https://nodejs.org/dist/latest/docs/api/http.html#http_server_requesttimeout)
103
+ to understand the effect of this option. Also, when `serverFactory` option is specified, this option is ignored.
104
+ It must be set to a non-zero value (e.g. 120 seconds) to protect against potential Denial-of-Service attacks in case the server is deployed without a reverse proxy in front.
105
+ > At the time of this writing, only node version greater or equal to 14.11.0 support this option. Check the Node.js documentation for availability in the version you are running.
106
+
107
+ + Default: `0` (no limit)
108
+
97
109
  <a name="factory-ignore-slash"></a>
98
110
  ### `ignoreTrailingSlash`
99
111
 
@@ -480,7 +492,7 @@ Configure the Ajv v6 instance used by Fastify without providing a custom one.
480
492
  const fastify = require('fastify')({
481
493
  ajv: {
482
494
  customOptions: {
483
- nullable: false // Refer to [ajv options](https://ajv.js.org/#options)
495
+ nullable: false // Refer to [ajv options](https://github.com/ajv-validator/ajv/tree/v6#options)
484
496
  },
485
497
  plugins: [
486
498
  require('ajv-merge-patch'),
@@ -35,6 +35,9 @@ This example will get you up and running with Fastify and TypeScript. It results
35
35
  }
36
36
  }
37
37
  ```
38
+
39
+ *Note: Set `target` property in `tsconfig.json` to `es2017` or greater to avoid [FastifyDeprecation](https://github.com/fastify/fastify/issues/3284) warning.*
40
+
38
41
  3. Initialize a TypeScript configuration file:
39
42
  ```bash
40
43
  npx tsc --init
@@ -10,6 +10,10 @@ Fastify uses a schema-based approach, and even if it is not mandatory we recomme
10
10
  > user-provided schemas. See [Ajv](https://npm.im/ajv) and
11
11
  > [fast-json-stringify](https://npm.im/fast-json-stringify) for more
12
12
  > details.
13
+ >
14
+ > Moreover, the [`$async` Ajv feature](https://ajv.js.org/guide/async-validation.html) should not be used as part of the first validation strategy.
15
+ > This option is used to access Databases and reading them during the validation process may lead to Denial of Service Attacks to your
16
+ > application. If you need to run `async` tasks, use [Fastify's hooks](./Hooks.md) instead after validation completes, such as `preHandler`.
13
17
 
14
18
 
15
19
  ### Core concepts
@@ -257,7 +261,7 @@ curl -X GET "http://localhost:3000/?ids=1
257
261
 
258
262
  You can also specify a custom schema validator for each parameter type (body, querystring, params, headers).
259
263
 
260
- For example, the following code disable type cohercion only for the `body` parameters, changing the ajv default options:
264
+ For example, the following code disable type coercion only for the `body` parameters, changing the ajv default options:
261
265
 
262
266
  ```js
263
267
  const schemaCompilers = {
@@ -642,6 +646,7 @@ fastify.setErrorHandler(function (error, request, reply) {
642
646
  ```
643
647
 
644
648
  If you want custom error response in schema without headaches and quickly, you can take a look at [`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Check out the [example](https://github.com/fastify/example/blob/HEAD/validation-messages/custom-errors-messages.js) usage.
649
+ > Make sure to install version 1.0.1 of `ajv-errors`, because later versions of it are not compatible with AJV v6 (the version shipped by Fastify v3).
645
650
 
646
651
  Below is an example showing how to add **custom error messages for each property** of a schema by supplying custom AJV options.
647
652
  Inline comments in the schema below describe how to configure it to show a different error message for each case:
@@ -649,7 +654,10 @@ Inline comments in the schema below describe how to configure it to show a diffe
649
654
  ```js
650
655
  const fastify = Fastify({
651
656
  ajv: {
652
- customOptions: { jsonPointers: true },
657
+ customOptions: {
658
+ jsonPointers: true,
659
+ allErrors: true // Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/
660
+ },
653
661
  plugins: [
654
662
  require('ajv-errors')
655
663
  ]
package/fastify.d.ts CHANGED
@@ -15,6 +15,8 @@ import { FastifySchemaValidationError } from './types/schema'
15
15
  import { ConstructorAction, ProtoAction } from "./types/content-type-parser";
16
16
  import { Socket } from 'net'
17
17
  import { Options as FJSOptions } from 'fast-json-stringify'
18
+ import { ValidatorCompiler } from '@fastify/ajv-compiler'
19
+ import { FastifySerializerCompiler } from './types/schema';
18
20
 
19
21
  /**
20
22
  * Fastify factory function for the standard fastify http, https, or http2 server instance.
@@ -95,6 +97,8 @@ export type FastifyServerOptions<
95
97
  ignoreTrailingSlash?: boolean,
96
98
  connectionTimeout?: number,
97
99
  keepAliveTimeout?: number,
100
+ maxRequestsPerSocket?: number,
101
+ requestTimeout?: number,
98
102
  pluginTimeout?: number,
99
103
  bodyLimit?: number,
100
104
  maxParamLength?: number,
@@ -126,6 +130,17 @@ export type FastifyServerOptions<
126
130
  constraints?: {
127
131
  [name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>,
128
132
  },
133
+ schemaController?: {
134
+ bucket?: (parentSchemas?: unknown) => {
135
+ addSchema(schema: unknown): FastifyInstance;
136
+ getSchema(schemaId: string): unknown;
137
+ getSchemas(): Record<string, unknown>;
138
+ };
139
+ compilersFactory?: {
140
+ buildValidator?: ValidatorCompiler;
141
+ buildSerializer?: (externalSchemas: unknown, serializerOptsServerOption: FastifyServerOptions["serializerOpts"]) => FastifySerializerCompiler<unknown>;
142
+ };
143
+ };
129
144
  return503OnClosing?: boolean,
130
145
  ajv?: {
131
146
  customOptions?: AjvOptions,
package/fastify.js CHANGED
@@ -1,11 +1,11 @@
1
1
  'use strict'
2
2
 
3
+ const VERSION = '3.24.0'
4
+
3
5
  const Avvio = require('avvio')
4
6
  const http = require('http')
5
7
  const querystring = require('querystring')
6
8
  let lightMyRequest
7
- let version
8
- let versionLoaded = false
9
9
 
10
10
  const {
11
11
  kAvvioBoot,
@@ -133,6 +133,7 @@ function fastify (options) {
133
133
  options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
134
134
  options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
135
135
  options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
136
+ options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
136
137
  options.logger = logger
137
138
  options.genReqId = genReqId
138
139
  options.requestIdHeader = requestIdHeader
@@ -325,12 +326,7 @@ function fastify (options) {
325
326
  get () { return this[kSchemaController].getSerializerCompiler() }
326
327
  },
327
328
  version: {
328
- get () {
329
- if (versionLoaded === false) {
330
- version = loadVersion()
331
- }
332
- return version
333
- }
329
+ get () { return VERSION }
334
330
  },
335
331
  errorHandler: {
336
332
  get () {
@@ -421,7 +417,7 @@ function fastify (options) {
421
417
  // If the server is not ready yet, this
422
418
  // utility will automatically force it.
423
419
  function inject (opts, cb) {
424
- // lightMyRequest is dynamically laoded as it seems very expensive
420
+ // lightMyRequest is dynamically loaded as it seems very expensive
425
421
  // because of Ajv
426
422
  if (lightMyRequest === undefined) {
427
423
  lightMyRequest = require('light-my-request')
@@ -635,7 +631,8 @@ function fastify (options) {
635
631
  function setSchemaController (schemaControllerOpts) {
636
632
  throwIfAlreadyStarted('Cannot call "setSchemaController" when fastify instance is already started!')
637
633
  const old = this[kSchemaController]
638
- const schemaController = SchemaController.buildSchemaController(old.parent, Object.assign({}, old.opts, schemaControllerOpts))
634
+ const parent = old.parent || old
635
+ const schemaController = SchemaController.buildSchemaController(parent, Object.assign({}, old.opts, schemaControllerOpts))
639
636
  this[kSchemaController] = schemaController
640
637
  this.getSchema = schemaController.getSchema.bind(schemaController)
641
638
  this.getSchemas = schemaController.getSchemas.bind(schemaController)
@@ -691,20 +688,6 @@ function wrapRouting (httpHandler, { rewriteUrl, logger }) {
691
688
  }
692
689
  }
693
690
 
694
- function loadVersion () {
695
- versionLoaded = true
696
- const fs = require('fs')
697
- const path = require('path')
698
- try {
699
- const pkgPath = path.join(__dirname, 'package.json')
700
- fs.accessSync(pkgPath, fs.constants.R_OK)
701
- const pkg = JSON.parse(fs.readFileSync(pkgPath))
702
- return pkg.name === 'fastify' ? pkg.version : undefined
703
- } catch (e) {
704
- return undefined
705
- }
706
- }
707
-
708
691
  /**
709
692
  * These export configurations enable JS and TS developers
710
693
  * to consumer fastify in whatever way best suits their needs.