fastify 5.10.0 → 5.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 (43) hide show
  1. package/SPONSORS.md +1 -0
  2. package/docs/Guides/Ecosystem.md +2 -2
  3. package/docs/Guides/Getting-Started.md +8 -8
  4. package/docs/Reference/Errors.md +4 -0
  5. package/docs/Reference/Reply.md +2 -2
  6. package/docs/Reference/Request.md +11 -11
  7. package/docs/Reference/Server.md +16 -15
  8. package/docs/Reference/TypeScript.md +5 -7
  9. package/docs/Reference/Warnings.md +1 -0
  10. package/eslint.config.js +1 -1
  11. package/fastify.d.ts +55 -13
  12. package/fastify.js +7 -2
  13. package/lib/content-type.js +36 -18
  14. package/lib/errors.js +10 -0
  15. package/lib/handle-request.js +35 -7
  16. package/lib/hooks.js +5 -1
  17. package/lib/route.js +1 -1
  18. package/lib/schemas.js +3 -3
  19. package/lib/validation.js +1 -1
  20. package/package.json +2 -2
  21. package/test/content-type.test.js +31 -4
  22. package/test/find-route.test.js +35 -0
  23. package/test/fix-6411.test.js +65 -0
  24. package/test/internals/all.test.js +2 -1
  25. package/test/internals/errors.test.js +21 -1
  26. package/test/internals/reply.test.js +22 -0
  27. package/test/reply-error.test.js +35 -0
  28. package/test/rfc-10008.test.js +147 -0
  29. package/test/route-shorthand.test.js +14 -4
  30. package/test/schema-serialization.test.js +33 -0
  31. package/test/types/errors.tst.ts +2 -0
  32. package/test/types/route.tst.ts +3 -3
  33. package/types/content-type-parser.d.ts +26 -6
  34. package/types/errors.d.ts +2 -0
  35. package/types/hooks.d.ts +137 -76
  36. package/types/instance.d.ts +150 -47
  37. package/types/logger.d.ts +36 -3
  38. package/types/plugin.d.ts +7 -3
  39. package/types/register.d.ts +53 -10
  40. package/types/reply.d.ts +62 -14
  41. package/types/route.d.ts +68 -34
  42. package/types/type-provider.d.ts +29 -8
  43. package/types/utils.d.ts +2 -2
package/SPONSORS.md CHANGED
@@ -18,6 +18,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
18
18
  - [Val Town](https://www.val.town/)
19
19
  - [atagon GmbH](https://github.com/atagon-GmbH)
20
20
  - [Photon](https://github.com/photon-hq)
21
+ - [N-iX](https://www.n-ix.com/)
21
22
 
22
23
  ## Tier 2
23
24
 
@@ -208,8 +208,6 @@ section.
208
208
  Type-safe dependency injection support for Fastify, powered by [InferDI](https://github.com/inferdi/inferdi).
209
209
  - [`@jerome1337/fastify-enforce-routes-pattern`](https://github.com/Jerome1337/fastify-enforce-routes-pattern)
210
210
  A Fastify plugin that enforces naming pattern for routes path.
211
- - [`@joggr/fastify-prisma`](https://github.com/joggrdocs/fastify-prisma)
212
- A plugin for accessing an instantiated PrismaClient on your server.
213
211
  - [`@matths/fastify-svelte-view`](https://github.com/matths/fastify-svelte-view)
214
212
  A Fastify plugin for rendering Svelte components with support for SSR
215
213
  (Server-Side Rendering), CSR (Client-Side Rendering), and SSR with hydration.
@@ -242,6 +240,8 @@ section.
242
240
  SeaweedFS for Fastify
243
241
  - [`@yeliex/fastify-problem-details`](https://github.com/yeliex/fastify-problem-details)
244
242
  RFC 9457 Problem Details implementation for Fastify, with typed HTTP errors.
243
+ - [`@zrosenbauer/fastify-prisma`](https://github.com/zrosenbauer/fastify-prisma)
244
+ A plugin for accessing an instantiated PrismaClient on your server.
245
245
  - [`apitally`](https://github.com/apitally/apitally-js) Fastify plugin to
246
246
  integrate with [Apitally](https://apitally.io/fastify), an API analytics,
247
247
  logging and monitoring tool.
@@ -146,7 +146,7 @@ declaration](../Reference/Routes.md) docs).
146
146
  ```js
147
147
  // ESM
148
148
  import Fastify from 'fastify'
149
- import firstRoute from './our-first-route.js'
149
+ import routes from './our-first-routes.js'
150
150
  /**
151
151
  * @type {import('fastify').FastifyInstance} Instance of Fastify
152
152
  */
@@ -154,7 +154,7 @@ const fastify = Fastify({
154
154
  logger: true
155
155
  })
156
156
 
157
- fastify.register(firstRoute)
157
+ fastify.register(routes)
158
158
 
159
159
  fastify.listen({ port: 3000 }, function (err, address) {
160
160
  if (err) {
@@ -174,7 +174,7 @@ const fastify = require('fastify')({
174
174
  logger: true
175
175
  })
176
176
 
177
- fastify.register(require('./our-first-route'))
177
+ fastify.register(require('./our-first-routes'))
178
178
 
179
179
  fastify.listen({ port: 3000 }, function (err, address) {
180
180
  if (err) {
@@ -187,7 +187,7 @@ fastify.listen({ port: 3000 }, function (err, address) {
187
187
 
188
188
 
189
189
  ```js
190
- // our-first-route.js
190
+ // our-first-routes.js
191
191
 
192
192
  /**
193
193
  * Encapsulates the routes
@@ -236,7 +236,7 @@ npm i fastify-plugin @fastify/mongodb
236
236
  // ESM
237
237
  import Fastify from 'fastify'
238
238
  import dbConnector from './our-db-connector.js'
239
- import firstRoute from './our-first-route.js'
239
+ import routes from './our-first-routes.js'
240
240
 
241
241
  /**
242
242
  * @type {import('fastify').FastifyInstance} Instance of Fastify
@@ -245,7 +245,7 @@ const fastify = Fastify({
245
245
  logger: true
246
246
  })
247
247
  fastify.register(dbConnector)
248
- fastify.register(firstRoute)
248
+ fastify.register(routes)
249
249
 
250
250
  fastify.listen({ port: 3000 }, function (err, address) {
251
251
  if (err) {
@@ -266,7 +266,7 @@ const fastify = require('fastify')({
266
266
  })
267
267
 
268
268
  fastify.register(require('./our-db-connector'))
269
- fastify.register(require('./our-first-route'))
269
+ fastify.register(require('./our-first-routes'))
270
270
 
271
271
  fastify.listen({ port: 3000 }, function (err, address) {
272
272
  if (err) {
@@ -325,7 +325,7 @@ module.exports = fastifyPlugin(dbConnector)
325
325
 
326
326
  ```
327
327
 
328
- **our-first-route.js**
328
+ **our-first-routes.js**
329
329
  ```js
330
330
  /**
331
331
  * A plugin that provide encapsulated routes
@@ -91,6 +91,8 @@
91
91
 
92
92
  - [FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT](#fst_err_route_handler_timeout_option_not_int)
93
93
  - [FST_ERR_ROUTE_REWRITE_NOT_STR](#fst_err_route_rewrite_not_str)
94
+ - [FST_ERR_ROUTE_MISSING_CONTENT_TYPE](#fst_err_route_missing_content_type)
95
+ - [FST_ERR_ROUTE_MISSING_CONTENT](#fst_err_route_missing_content)
94
96
  - [FST_ERR_REOPENED_CLOSE_SERVER](#fst_err_reopened_close_server)
95
97
  - [FST_ERR_REOPENED_SERVER](#fst_err_reopened_server)
96
98
  - [FST_ERR_PLUGIN_VERSION_MISMATCH](#fst_err_plugin_version_mismatch)
@@ -369,6 +371,8 @@ Below is a table with all the error codes used by Fastify.
369
371
  | <a id="fst_err_handler_timeout">FST_ERR_HANDLER_TIMEOUT</a> | Request timed out. | Increase the `handlerTimeout` option or optimize the handler. | - |
370
372
  | <a id="fst_err_route_handler_timeout_option_not_int">FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT</a> | `handlerTimeout` option must be a positive integer. | Use a positive integer for the `handlerTimeout` option. | - |
371
373
  | <a id="fst_err_route_rewrite_not_str">FST_ERR_ROUTE_REWRITE_NOT_STR</a> | `rewriteUrl` needs to be of type `string`. | Use a string for the `rewriteUrl`. | [#4554](https://github.com/fastify/fastify/pull/4554) |
374
+ | <a id="fst_err_route_missing_content_type">FST_ERR_ROUTE_MISSING_CONTENT_TYPE</a> | `Content-Type` header is required for the request. | Send request with `Content-Type` header. | [#6832](https://github.com/fastify/fastify/pull/6832) |
375
+ | <a id="fst_err_route_missing_content">FST_ERR_ROUTE_MISSING_CONTENT</a> | Body is required for the request. | Send request with payload. | [#6832](https://github.com/fastify/fastify/pull/6832) |
372
376
  | <a id="fst_err_reopened_close_server">FST_ERR_REOPENED_CLOSE_SERVER</a> | Fastify has already been closed and cannot be reopened. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
373
377
  | <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already listening. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
374
378
  | <a id="fst_err_plugin_version_mismatch">FST_ERR_PLUGIN_VERSION_MISMATCH</a> | Installed Fastify plugin mismatched expected version. | Use a compatible version of the plugin. | [#2549](https://github.com/fastify/fastify/pull/2549) |
@@ -271,7 +271,7 @@ as soon as possible.
271
271
  > in the error, you can turn on `debug` level logging.
272
272
 
273
273
  ```js
274
- reply.trailer('server-timing', function() {
274
+ reply.trailer('server-timing', async function () {
275
275
  return 'db;dur=53, app;dur=47.2'
276
276
  })
277
277
 
@@ -304,7 +304,7 @@ Returns a boolean indicating if the specified trailer has been set.
304
304
 
305
305
  Remove the value of a previously set trailer.
306
306
  ```js
307
- reply.trailer('server-timing', function() {
307
+ reply.trailer('server-timing', async function () {
308
308
  return 'db;dur=53, app;dur=47.2'
309
309
  })
310
310
  reply.removeTrailer('server-timing')
@@ -34,14 +34,14 @@ Request is a core Fastify object containing the following fields:
34
34
  value comes from `socket.encrypted` (or `X-Forwarded-Proto` when
35
35
  [`trustProxy`](./Server.md#factory-trust-proxy) is enabled).
36
36
 
37
- > ⚠️ Security:
38
- > `request.ip`, `request.ips`, `request.host`, `request.hostname`,
39
- > `request.port`, and `request.protocol` come from request metadata
40
- > (socket and/or forwarding headers) and should be treated as untrusted input.
41
- > Fastify does not perform security validation for business logic.
42
- > If these values are used in security-sensitive decisions, they must
43
- > be validated explicitly (for example: trusted proxy configuration,
44
- > allow-lists, strict parsing, and normalization).
37
+ > ⚠️ Security:
38
+ > `request.ip`, `request.ips`, `request.host`, `request.hostname`,
39
+ > `request.port`, and `request.protocol` come from request metadata
40
+ > (socket and/or forwarding headers) and should be treated as untrusted input.
41
+ > Fastify does not perform security validation for business logic.
42
+ > If these values are used in security-sensitive decisions, they must
43
+ > be validated explicitly (for example: trusted proxy configuration,
44
+ > allow-lists, strict parsing, and normalization).
45
45
 
46
46
  - `method` - The method of the incoming request.
47
47
  - `url` - The URL of the incoming request.
@@ -87,9 +87,9 @@ Request is a core Fastify object containing the following fields:
87
87
  default (or customized) `ValidationCompiler`. The optional `httpPart` is
88
88
  forwarded to the `ValidationCompiler` if provided, defaults to `null`.
89
89
  - [.validateInput(data, schema | httpPart, [httpPart])](#validate) -
90
- Validates the input using the specified schema and returns the serialized
91
- payload. If `httpPart` is provided, the function uses the serializer for
92
- that HTTP Status Code. Defaults to `null`.
90
+ Validates the input using the specified schema or HTTP part and returns
91
+ `true` if the input is valid, `false` otherwise. If `httpPart` is provided,
92
+ the function uses the validator for that HTTP part. Defaults to `null`.
93
93
 
94
94
  ### Headers
95
95
 
@@ -390,8 +390,8 @@ Pino interface by having the following methods: `info`, `error`, `debug`,
390
390
  ### `disableRequestLogging`
391
391
  <a id="factory-disable-request-logging"></a>
392
392
 
393
- > **Deprecated:** Use the [`logController`](#log-controller) option with
394
- > `disableRequestLogging` or `isLogDisabled` override instead.
393
+ > **Deprecated:** Use the [`logController`](#factory-log-controller)
394
+ > option with `disableRequestLogging` or `isLogDisabled` override instead.
395
395
  > This top-level option will be removed in `fastify@6`.
396
396
 
397
397
  + Default: `false`
@@ -407,6 +407,8 @@ and returns a boolean. This allows for conditional request logging based on the
407
407
  request properties (e.g., URL, headers, decorations).
408
408
 
409
409
  ```js
410
+ const { LogController } = require('fastify')
411
+
410
412
  // Deprecated
411
413
  const fastify = require('fastify')({
412
414
  logger: true,
@@ -418,11 +420,11 @@ const fastify = require('fastify')({
418
420
  // Recommended: use logController instead
419
421
  const fastify = require('fastify')({
420
422
  logger: true,
421
- logController: {
423
+ logController: new LogController({
422
424
  disableRequestLogging: (request) => {
423
425
  return request.url === '/health' || request.url === '/ready'
424
426
  }
425
- }
427
+ })
426
428
  })
427
429
  ```
428
430
 
@@ -567,7 +569,7 @@ enhance the server instance inside the `serverFactory` function before the
567
569
  ### `requestIdHeader`
568
570
  <a id="factory-request-id-header"></a>
569
571
 
570
- + Default: `'request-id'`
572
+ + Default: `false`
571
573
 
572
574
  The header name used to set the request-id. See [the
573
575
  request-id](./Logging.md#logging-request-id) section.
@@ -579,8 +581,6 @@ By default `requestIdHeader` is set to `false` and will immediately use [genReqI
579
581
  Setting `requestIdHeader` to an empty String (`""`) will set the
580
582
  requestIdHeader to `false`.
581
583
 
582
- + Default: `false`
583
-
584
584
  ```js
585
585
  const fastify = require('fastify')({
586
586
  requestIdHeader: 'x-custom-id', // -> use 'X-Custom-Id' header if available
@@ -596,8 +596,9 @@ const fastify = require('fastify')({
596
596
  ### `requestIdLogLabel`
597
597
  <a id="factory-request-id-log-label"></a>
598
598
 
599
- > **Deprecated:** Use the [`logController`](#log-controller) option with
600
- > `requestIdLogLabel` instead. This top-level option will be removed in `fastify@6`.
599
+ > **Deprecated:** Use the [`logController`](#factory-log-controller)
600
+ > option with `requestIdLogLabel` instead. This top-level option will be
601
+ > removed in `fastify@6`.
601
602
 
602
603
  + Default: `'reqId'`
603
604
 
@@ -659,11 +660,11 @@ For more examples, refer to the
659
660
  You may access the `ip`, `ips`, `host` and `protocol` values on the
660
661
  [`request`](./Request.md) object.
661
662
 
662
- > ⚠️ Security:
663
- > These values are derived from socket/forwarding metadata and must be treated
664
- > as untrusted input unless your proxy chain is explicitly trusted and
665
- > validated. Do not use them directly for authorization or other
666
- > security-sensitive decisions without explicit validation.
663
+ > ⚠️ Security:
664
+ > These values are derived from socket/forwarding metadata and must be treated
665
+ > as untrusted input unless your proxy chain is explicitly trusted and
666
+ > validated. Do not use them directly for authorization or other
667
+ > security-sensitive decisions without explicit validation.
667
668
 
668
669
  ```js
669
670
  fastify.get('/', (request, reply) => {
@@ -1702,7 +1703,7 @@ Fake HTTP injection (for testing purposes)
1702
1703
  <a id="addHttpMethod"></a>
1703
1704
 
1704
1705
  Fastify supports the `GET`, `HEAD`, `TRACE`, `DELETE`, `OPTIONS`,
1705
- `PATCH`, `PUT` and `POST` HTTP methods by default.
1706
+ `PATCH`, `PUT`, `POST` and `QUERY` HTTP methods by default.
1706
1707
  The `addHttpMethod` method allows to add any non standard HTTP
1707
1708
  methods to the server that are [supported by Node.js](https://nodejs.org/api/http.html#httpmethods).
1708
1709
 
@@ -15,7 +15,7 @@ fill in the gaps. Just make sure to read our
15
15
  [`CONTRIBUTING.md`](https://github.com/fastify/fastify/blob/main/CONTRIBUTING.md)
16
16
  file before getting started to make sure things go smoothly!
17
17
 
18
- > The documentation in this section covers Fastify version 3.x typings
18
+ > The documentation in this section covers Fastify typings
19
19
 
20
20
  > Plugins may or may not include typings. See [Plugins](#plugins) for more
21
21
  > information. We encourage users to send pull requests to improve typings
@@ -90,7 +90,7 @@ in a blank http Fastify server.
90
90
  🏓
91
91
 
92
92
  🎉 You now have a working Typescript Fastify server! This example demonstrates
93
- the simplicity of the version 3.x type system. By default, the type system
93
+ the simplicity of the type system. By default, the type system
94
94
  assumes you are using an `http` server. The later examples will demonstrate how
95
95
  to create more complex servers such as `https` and `http2`, how to specify route
96
96
  schemas, and more!
@@ -545,9 +545,9 @@ Fastify Plugin in a TypeScript Project.
545
545
  }
546
546
 
547
547
  // export plugin using fastify-plugin
548
- export default fp(myPluginCallback, '3.x')
548
+ export default fp(myPluginCallback)
549
549
  // or
550
- // export default fp(myPluginAsync, '3.x')
550
+ // export default fp(myPluginAsync)
551
551
  ```
552
552
  6. Run `npm run build` to compile the plugin code and produce both a JavaScript
553
553
  source file and a type definition file.
@@ -595,7 +595,6 @@ your plugin.
595
595
  }
596
596
 
597
597
  module.exports = fp(myPlugin, {
598
- fastify: '5.x',
599
598
  name: 'my-plugin' // this is used by fastify-plugin to derive the property name
600
599
  })
601
600
  ```
@@ -843,8 +842,7 @@ module.exports = async function (fastify, { optionA, optionB }) {
843
842
 
844
843
  ## API Type System Documentation
845
844
 
846
- This section is a detailed account of all the types available to you in Fastify
847
- version 3.x
845
+ This section is a detailed account of all the types available to you in Fastify.
848
846
 
849
847
  All `http`, `https`, and `http2` types are inferred from `@types/node`
850
848
 
@@ -33,6 +33,7 @@ one of the following methods:
33
33
  - Set the `NODE_NO_WARNINGS` environment variable to `1`
34
34
  - Pass the `--no-warnings` flag to the node process
35
35
  - Set `no-warnings` in the `NODE_OPTIONS` environment variable
36
+ - Pass `--disable-warning=FSTWRN004` to disable a specific warning
36
37
 
37
38
  For more information on disabling warnings, see [Node's documentation](https://nodejs.org/api/cli.html).
38
39
 
package/eslint.config.js CHANGED
@@ -28,7 +28,7 @@ module.exports = [
28
28
  {
29
29
  files: ['**/*.d.ts'],
30
30
  rules: {
31
- 'max-len': 'off'
31
+ '@stylistic/indent-binary-ops': ['error', 2]
32
32
  }
33
33
  },
34
34
  {
package/fastify.d.ts CHANGED
@@ -78,7 +78,9 @@ declare namespace fastify {
78
78
  http?: http.ServerOptions | null
79
79
  }
80
80
 
81
- type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server ? HTTPVersion.V1 : HTTPVersion.V2
81
+ type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server
82
+ ? HTTPVersion.V1
83
+ : HTTPVersion.V2
82
84
  type FindMyWayConfigForServer<RawServer extends RawServerBase> = FindMyWayConfig<FindMyWayVersion<RawServer>>
83
85
 
84
86
  export interface ConnectionError extends Error {
@@ -163,15 +165,39 @@ declare namespace fastify {
163
165
  };
164
166
  return503OnClosing?: boolean,
165
167
  ajv?: Parameters<BuildCompilerFromPool>[1],
166
- frameworkErrors?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema>(
168
+ frameworkErrors?: <
169
+ RequestGeneric extends RequestGenericInterface = RequestGenericInterface,
170
+ TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
171
+ SchemaCompiler extends FastifySchema = FastifySchema
172
+ >(
167
173
  error: FastifyError,
168
- req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>,
169
- res: FastifyReply<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, FastifyContextConfig, SchemaCompiler, TypeProvider>
174
+ req: FastifyRequest<
175
+ RequestGeneric,
176
+ RawServer,
177
+ RawRequestDefaultExpression<RawServer>,
178
+ FastifySchema,
179
+ TypeProvider
180
+ >,
181
+ res: FastifyReply<
182
+ RequestGeneric,
183
+ RawServer,
184
+ RawRequestDefaultExpression<RawServer>,
185
+ RawReplyDefaultExpression<RawServer>,
186
+ FastifyContextConfig,
187
+ SchemaCompiler,
188
+ TypeProvider
189
+ >
170
190
  ) => void,
171
191
  rewriteUrl?: (
172
192
  // The RawRequestDefaultExpression, RawReplyDefaultExpression, and FastifyTypeProviderDefault parameters
173
193
  // should be narrowed further but those generic parameters are not passed to this FastifyServerOptions type
174
- this: FastifyInstance<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, Logger, FastifyTypeProviderDefault>,
194
+ this: FastifyInstance<
195
+ RawServer,
196
+ RawRequestDefaultExpression<RawServer>,
197
+ RawReplyDefaultExpression<RawServer>,
198
+ Logger,
199
+ FastifyTypeProviderDefault
200
+ >,
175
201
  req: RawRequestDefaultExpression<RawServer>
176
202
  ) => string,
177
203
  schemaErrorFormatter?: SchemaErrorFormatter,
@@ -198,13 +224,25 @@ declare namespace fastify {
198
224
  FastifyListenOptions, FastifyInstance, PrintRoutesOptions, // './types/instance'
199
225
  FastifyLoggerOptions, FastifyBaseLogger, FastifyLoggerInstance, FastifyLogFn, LogLevel, // './types/logger'
200
226
  FastifyRequestContext, FastifyContextConfig, FastifyReplyContext, // './types/context'
201
- RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface, // './types/route'
227
+ RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions,
228
+ RouteShorthandOptionsWithHandler, RouteGenericInterface, // './types/route'
202
229
  FastifyRegister, FastifyRegisterOptions, RegisterOptions, // './types/register'
203
- FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction, // './types/content-type-parser'
230
+ FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser,
231
+ ProtoAction, ConstructorAction, // './types/content-type-parser'
204
232
  FastifyError, // '@fastify/error'
205
233
  FastifySchema, FastifySchemaValidationError, FastifySchemaCompiler, FastifySerializerCompiler, // './types/schema'
206
- HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault, // './types/utils'
207
- DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler, preCloseAsyncHookHandler, preCloseHookHandler, // './types/hooks'
234
+ HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault,
235
+ ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault,
236
+ // './types/utils'
237
+ DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler,
238
+ onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler,
239
+ onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler,
240
+ onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler,
241
+ onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler,
242
+ preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler,
243
+ preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler,
244
+ preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler, preCloseAsyncHookHandler,
245
+ preCloseHookHandler, // './types/hooks'
208
246
  FastifyServerFactory, FastifyServerFactoryHandler, // './types/serverFactory'
209
247
  FastifyTypeProvider, FastifyTypeProviderDefault, SafePromiseLike, // './types/type-provider'
210
248
  FastifyErrorCodes // './types/errors'
@@ -232,7 +270,8 @@ declare function fastify<
232
270
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
233
271
  Logger extends FastifyBaseLogger = FastifyBaseLogger,
234
272
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
235
- > (opts: fastify.FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
273
+ > (opts: fastify.FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger,
274
+ TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
236
275
 
237
276
  declare function fastify<
238
277
  Server extends http2.Http2Server,
@@ -240,7 +279,8 @@ declare function fastify<
240
279
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
241
280
  Logger extends FastifyBaseLogger = FastifyBaseLogger,
242
281
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
243
- > (opts: fastify.FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
282
+ > (opts: fastify.FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger,
283
+ TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
244
284
 
245
285
  declare function fastify<
246
286
  Server extends https.Server,
@@ -248,7 +288,8 @@ declare function fastify<
248
288
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
249
289
  Logger extends FastifyBaseLogger = FastifyBaseLogger,
250
290
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
251
- > (opts: fastify.FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
291
+ > (opts: fastify.FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger,
292
+ TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
252
293
 
253
294
  declare function fastify<
254
295
  Server extends http.Server,
@@ -256,7 +297,8 @@ declare function fastify<
256
297
  Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
257
298
  Logger extends FastifyBaseLogger = FastifyBaseLogger,
258
299
  TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
259
- > (opts?: fastify.FastifyHttpOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
300
+ > (opts?: fastify.FastifyHttpOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger,
301
+ TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
260
302
 
261
303
  // CJS export
262
304
  // const fastify = require('fastify')
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '5.10.0'
3
+ const VERSION = '5.11.0'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -143,7 +143,9 @@ function fastify (serverOptions) {
143
143
  'OPTIONS',
144
144
  'PATCH',
145
145
  'PUT',
146
- 'POST'
146
+ 'POST',
147
+ // RFC 10008
148
+ 'QUERY'
147
149
  ])
148
150
  },
149
151
  [kOptions]: options,
@@ -200,6 +202,9 @@ function fastify (serverOptions) {
200
202
  options: function _options (url, options, handler) {
201
203
  return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })
202
204
  },
205
+ query: function _query (url, options, handler) {
206
+ return router.prepareRoute.call(this, { method: 'QUERY', url, options, handler })
207
+ },
203
208
  all: function _all (url, options, handler) {
204
209
  return router.prepareRoute.call(this, { method: this.supportedMethods, url, options, handler })
205
210
  },
@@ -3,15 +3,37 @@
3
3
  const { LruMap: Lru } = require('toad-cache')
4
4
 
5
5
  /**
6
- * keyValuePairsReg is used to split the parameters list into associated
7
- * key value pairings. The leading `(?:^|;)\s*` anchor ensures the regex
8
- * only attempts matches at parameter boundaries, preventing quadratic
9
- * backtracking on malformed input.
6
+ * keyValuePairsReg matches a single `parameter-name "=" parameter-value`
7
+ * at a parameter boundary, following RFC 9110 §5.6.6 grammar:
8
+ *
9
+ * parameter = parameter-name "=" parameter-value
10
+ * parameter-name = token
11
+ * parameter-value = ( token / quoted-string )
12
+ * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
13
+ * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
14
+ * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
15
+ * obs-text = %x80-FF
16
+ *
17
+ * The value alternative accepts either a bare token or a full quoted-string
18
+ * whose content matches the qdtext / quoted-pair character ranges. Because
19
+ * the quoted-string branch is aware of `\"` as an escape, embedded `"` and
20
+ * `;` characters inside a quoted value do not terminate the value.
10
21
  *
11
22
  * @see https://httpwg.org/specs/rfc9110.html#parameter
12
23
  * @type {RegExp}
13
24
  */
14
- const keyValuePairsReg = /(?:^|;)\s*([\w!#$%&'*+.^`|~-]+)=([^;]*)/gm
25
+ const keyValuePairsReg = /(?:^|;)\s*([\w!#$%&'*+.^`|~-]+)=("(?:[\t\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\t\u0020-\u00ff])*"|[\w!#$%&'*+.^`|~-]+)/gu
26
+
27
+ /**
28
+ * quotedPairReg matches a quoted-pair (a backslash followed by a single
29
+ * quoted-pair-allowed octet) inside a quoted-string value. Per RFC 9110
30
+ * §5.6.4, recipients that process the value of a quoted-string MUST handle
31
+ * a quoted-pair as if it were replaced by the octet following the backslash.
32
+ *
33
+ * @see https://httpwg.org/specs/rfc9110.html#quoted.string
34
+ * @type {RegExp}
35
+ */
36
+ const quotedPairReg = /\\([\t\u0020-\u00ff])/gu
15
37
 
16
38
  /**
17
39
  * typeNameReg is used to validate that the first part of the media-type
@@ -140,21 +162,17 @@ class ContentType {
140
162
  const key = matches[1].toLowerCase()
141
163
  // Parameter values might or might not be case-sensitive,
142
164
  // depending on the semantics of the parameter name.
143
- const value = matches[2]
144
- if (value[0] === '"') {
145
- if (value.at(-1) !== '"') {
146
- this.#parameters.set(key, 'invalid quoted string')
147
- matches = keyValuePairsReg.exec(paramsList)
148
- continue
165
+ let value = matches[2]
166
+ if (value.charCodeAt(0) === 0x22 /* " */) {
167
+ // Strip the surrounding DQUOTEs and unescape quoted-pairs per
168
+ // RFC 9110 §5.6.4. The regex guarantees the value starts and ends
169
+ // with `"` and only contains permitted qdtext / quoted-pair octets.
170
+ value = value.slice(1, -1)
171
+ if (value.indexOf('\\') !== -1) {
172
+ value = value.replace(quotedPairReg, '$1')
149
173
  }
150
- // We should probably verify the value matches a quoted string
151
- // (https://httpwg.org/specs/rfc9110.html#rule.quoted-string) value.
152
- // But we are not really doing much with the parameter values, so we
153
- // are omitting that at this time.
154
- this.#parameters.set(key, value.slice(1, value.length - 1))
155
- } else {
156
- this.#parameters.set(key, value)
157
174
  }
175
+ this.#parameters.set(key, value)
158
176
  matches = keyValuePairsReg.exec(paramsList)
159
177
  }
160
178
  }
package/lib/errors.js CHANGED
@@ -455,6 +455,16 @@ const codes = {
455
455
  500,
456
456
  TypeError
457
457
  ),
458
+ FST_ERR_ROUTE_MISSING_CONTENT_TYPE: createError(
459
+ 'FST_ERR_ROUTE_MISSING_CONTENT_TYPE',
460
+ "Method '%s' must provide a 'Content-Type' header.",
461
+ 400
462
+ ),
463
+ FST_ERR_ROUTE_MISSING_CONTENT: createError(
464
+ 'FST_ERR_ROUTE_MISSING_CONTENT',
465
+ "Method '%s' must provide a request body.",
466
+ 400
467
+ ),
458
468
 
459
469
  /**
460
470
  * again listen when close server
@@ -4,7 +4,11 @@ const diagnostics = require('node:diagnostics_channel')
4
4
  const wrapThenable = require('./wrap-thenable')
5
5
  const { validate: validateSchema } = require('./validation')
6
6
  const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
7
- const { FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('./errors')
7
+ const {
8
+ FST_ERR_CTP_INVALID_MEDIA_TYPE,
9
+ FST_ERR_ROUTE_MISSING_CONTENT_TYPE,
10
+ FST_ERR_ROUTE_MISSING_CONTENT
11
+ } = require('./errors')
8
12
  const { setErrorStatusCode } = require('./error-status')
9
13
  const {
10
14
  kReplyIsError,
@@ -37,13 +41,30 @@ function handleRequest (err, request, reply) {
37
41
  const headers = request.headers
38
42
  const ctHeader = headers['content-type']
39
43
 
40
- if (ctHeader === undefined) {
41
- const contentLength = headers['content-length']
42
- const transferEncoding = headers['transfer-encoding']
43
- const isEmptyBody = transferEncoding === undefined &&
44
- (contentLength === undefined || contentLength === '0')
44
+ if (method === 'QUERY') {
45
+ if (ctHeader === undefined) {
46
+ // https://datatracker.ietf.org/doc/html/rfc10008#section-2
47
+ // Servers MUST fail the request if the Content-Type request field
48
+ // ([HTTP], Section 8.3) is missing or is inconsistent with the
49
+ // request content.
50
+ reply[kReplyIsError] = true
51
+ reply.status(400).send(new FST_ERR_ROUTE_MISSING_CONTENT_TYPE(method))
52
+ return
53
+ }
45
54
 
46
- if (isEmptyBody) {
55
+ if (isEmptyBody(headers)) {
56
+ // https://datatracker.ietf.org/doc/html/rfc10008#section-2
57
+ // Servers MUST fail the request if the Content-Type request field
58
+ // ([HTTP], Section 8.3) is missing or is inconsistent with the
59
+ // request content.
60
+ reply[kReplyIsError] = true
61
+ reply.status(400).send(new FST_ERR_ROUTE_MISSING_CONTENT(method))
62
+ return
63
+ }
64
+ }
65
+
66
+ if (ctHeader === undefined) {
67
+ if (isEmptyBody(headers)) {
47
68
  // Request has no body to parse
48
69
  handler(request, reply)
49
70
  return
@@ -88,6 +109,13 @@ function handler (request, reply) {
88
109
  }
89
110
  }
90
111
 
112
+ function isEmptyBody (headers) {
113
+ const contentLength = headers['content-length']
114
+ const transferEncoding = headers['transfer-encoding']
115
+ return transferEncoding === undefined &&
116
+ (contentLength === undefined || contentLength === '0')
117
+ }
118
+
91
119
  function preValidationCallback (err, request, reply) {
92
120
  if (reply.sent === true) return
93
121
 
package/lib/hooks.js CHANGED
@@ -306,7 +306,11 @@ function onSendHookRunner (functions, request, reply, payload, cb) {
306
306
  }
307
307
 
308
308
  function handleResolve (newPayload) {
309
- next(null, newPayload)
309
+ try {
310
+ next(null, newPayload)
311
+ } catch (err) {
312
+ cb(err, request, reply, payload)
313
+ }
310
314
  }
311
315
 
312
316
  function handleReject (err) {