fastify 6.0.0-alpha.3 → 6.0.0-alpha.5

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 (41) hide show
  1. package/SECURITY.md +6 -0
  2. package/SPONSORS.md +1 -0
  3. package/docs/Guides/Ecosystem.md +10 -53
  4. package/docs/Reference/Errors.md +0 -2
  5. package/docs/Reference/Reply.md +20 -2
  6. package/docs/Reference/Request.md +8 -0
  7. package/docs/Reference/Routes.md +6 -2
  8. package/docs/Reference/Server.md +2 -2
  9. package/docs/Reference/Validation-and-Serialization.md +33 -0
  10. package/docs/Reference/Warnings.md +2 -0
  11. package/fastify.js +1 -1
  12. package/lib/content-type-parser.js +28 -13
  13. package/lib/content-type.js +4 -4
  14. package/lib/errors.js +0 -5
  15. package/lib/handle-request.js +7 -20
  16. package/lib/reply.js +11 -4
  17. package/lib/request.js +9 -5
  18. package/lib/route.js +6 -2
  19. package/lib/validation.js +73 -14
  20. package/lib/warnings.js +10 -1
  21. package/package.json +1 -1
  22. package/test/child-logger-factory.test.js +25 -0
  23. package/test/content-parser.test.js +60 -0
  24. package/test/content-type-parser-async-context.test.js +76 -0
  25. package/test/custom-parser.4.test.js +40 -0
  26. package/test/http2/plain.test.js +45 -0
  27. package/test/internals/errors.test.js +1 -11
  28. package/test/internals/reply-serialize.test.js +20 -0
  29. package/test/internals/reply.test.js +30 -1
  30. package/test/internals/request.test.js +50 -0
  31. package/test/internals/validation.test.js +66 -0
  32. package/test/reply-media-type.test.js +57 -0
  33. package/test/rfc-10008.test.js +6 -9
  34. package/test/schema-feature.test.js +1 -1
  35. package/test/schema-validation.test.js +251 -1
  36. package/test/types/errors.tst.ts +0 -1
  37. package/test/types/fastify.tst.ts +5 -0
  38. package/test/types/route.tst.ts +21 -0
  39. package/types/errors.d.ts +0 -1
  40. package/types/instance.d.ts +4 -4
  41. package/types/route.d.ts +3 -2
package/SECURITY.md CHANGED
@@ -30,6 +30,12 @@ The following are **not** considered vulnerabilities in Fastify:
30
30
 
31
31
  - **Application code vulnerabilities**: XSS, SQL injection, or other flaws in
32
32
  user-written route handlers, hooks, or plugins
33
+ - **Unvalidated template selection**: Passing untrusted input directly as a
34
+ template name or path (for example, `reply.view(request.query.page)`) without
35
+ validating or allowlisting it is an application-level vulnerability. A template
36
+ root used to resolve filenames is not a security sandbox unless the API
37
+ explicitly guarantees path containment. Bypasses of documented containment
38
+ or built-in path validation remain in scope.
33
39
  - **Malicious application code**: Issues caused by intentionally malicious
34
40
  plugins or handlers (application code is trusted)
35
41
  - **Validation schema issues**: Weak or incorrect schemas provided by developers
package/SPONSORS.md CHANGED
@@ -10,6 +10,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
10
10
  ## Tier 4
11
11
 
12
12
  - [SerpApi](https://serpapi.com/?utm_source=fastify)
13
+ - [Cloudways](https://www.cloudways.com/en/velocity.php?id=1258368&data1=fastify)
13
14
 
14
15
  ## Tier 3
15
16
 
@@ -172,8 +172,6 @@ section.
172
172
  A plugin to help with caching API responses using AWS DynamoDB.
173
173
  - [`@clerk/fastify`](https://github.com/clerk/javascript/tree/main/packages/fastify)
174
174
  Add authentication and user management to your Fastify application with Clerk.
175
- - [`@coobaha/typed-fastify`](https://github.com/Coobaha/typed-fastify) Strongly
176
- typed routes with a runtime validation using JSON schema generated from types.
177
175
  - [`@dnlup/fastify-doc`](https://github.com/dnlup/fastify-doc) A plugin for
178
176
  sampling process metrics.
179
177
  - [`@dnlup/fastify-traps`](https://github.com/dnlup/fastify-traps) A plugin to
@@ -211,6 +209,9 @@ section.
211
209
  - [`@matths/fastify-svelte-view`](https://github.com/matths/fastify-svelte-view)
212
210
  A Fastify plugin for rendering Svelte components with support for SSR
213
211
  (Server-Side Rendering), CSR (Client-Side Rendering), and SSR with hydration.
212
+ - [`@melroy89/fastify-mariadb`](https://github.com/melroy89/fastify-mariadb)
213
+ Fastify MariaDB connection plugin that shares a connection pool across the
214
+ server.
214
215
  - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
215
216
  A plugin to close the server gracefully
216
217
  - [`@mgcrea/fastify-request-logger`](https://github.com/mgcrea/fastify-request-logger)
@@ -240,6 +241,9 @@ section.
240
241
  method with a body).
241
242
  - [`@trubavuong/fastify-seaweedfs`](https://github.com/trubavuong/fastify-seaweedfs)
242
243
  SeaweedFS for Fastify
244
+ - [`@validex/fastify`](https://github.com/chiptoma/validex/tree/main/packages/fastify)
245
+ Zod 4 validation plugin with 25 typed rules, structured error codes, and
246
+ `request.validate()` decorator.
243
247
  - [`@yeliex/fastify-problem-details`](https://github.com/yeliex/fastify-problem-details)
244
248
  RFC 9457 Problem Details implementation for Fastify, with typed HTTP errors.
245
249
  - [`@zrosenbauer/fastify-prisma`](https://github.com/zrosenbauer/fastify-prisma)
@@ -249,8 +253,6 @@ section.
249
253
  logging and monitoring tool.
250
254
  - [`arecibo`](https://github.com/ducktors/arecibo) Fastify ping responder for
251
255
  Kubernetes Liveness and Readiness Probes.
252
- - [`aws-xray-sdk-fastify`](https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fastify)
253
- A Fastify plugin to log requests and subsegments through AWSXray.
254
256
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware
255
257
  for CLS-based request ID generation. An out-of-the-box solution for adding
256
258
  request IDs into your logs.
@@ -274,14 +276,10 @@ section.
274
276
  - [`fastify-amqp-async`](https://github.com/kffl/fastify-amqp-async) Fastify
275
277
  AMQP plugin with a Promise-based API provided by
276
278
  [`amqplib-as-promised`](https://github.com/twawszczak/amqplib-as-promised).
277
- - [`fastify-angular-universal`](https://github.com/exequiel09/fastify-angular-universal)
278
- Angular server-side rendering support using
279
- [`@angular/platform-server`](https://github.com/angular/angular/tree/master/packages/platform-server)
280
- for Fastify
281
279
  - [`fastify-api-key`](https://github.com/arkerone/fastify-api-key) Fastify
282
280
  plugin to authenticate HTTP requests based on API key and signature
283
- - [`fastify-appwrite`](https://github.com/maniecodes/fastify-appwrite) Fastify
284
- Plugin for interacting with Appwrite server.
281
+ - [`fastify-arktype`](https://github.com/BlairCurrey/fastify-arktype) ArkType
282
+ type provider with validation and @fastify/swagger OpenAPI generation.
285
283
  - [`fastify-asyncforge`](https://github.com/mcollina/fastify-asyncforge) Plugin
286
284
  to access Fastify instance, logger, request and reply from Node.js [Async
287
285
  Local Storage](https://nodejs.org/api/async_context.html#class-asynclocalstorage).
@@ -296,15 +294,9 @@ section.
296
294
  Auth0 verification plugin for Fastify, internally uses
297
295
  [fastify-jwt](https://www.npmjs.com/package/fastify-jwt) and
298
296
  [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken).
299
- - [`fastify-autoroutes`](https://github.com/GiovanniCardamone/fastify-autoroutes)
300
- Plugin to scan and load routes based on filesystem path from a custom
301
- directory.
302
297
  - [`fastify-aws-sns`](https://github.com/gzileni/fastify-aws-sns) Fastify plugin
303
298
  for AWS Simple Notification Service (AWS SNS) that coordinates and manages
304
299
  the delivery or sending of messages to subscribing endpoints or clients.
305
- - [`fastify-aws-timestream`](https://github.com/gzileni/fastify-aws-timestream)
306
- Fastify plugin for managing databases, tables, and querying and creating
307
- scheduled queries with AWS Timestream.
308
300
  - [`fastify-axios`](https://github.com/davidedantonio/fastify-axios) Plugin to
309
301
  send HTTP requests via [axios](https://github.com/axios/axios).
310
302
  - [`fastify-babel`](https://github.com/cfware/fastify-babel) Fastify plugin for
@@ -339,9 +331,6 @@ section.
339
331
  Fastify plugin for CloudFlare Turnstile.
340
332
  - [`fastify-cloudinary`](https://github.com/Vanilla-IceCream/fastify-cloudinary)
341
333
  Plugin to share a common Cloudinary connection across Fastify.
342
- - [`fastify-cockroachdb`](https://github.com/alex-ppg/fastify-cockroachdb)
343
- Fastify plugin to connect to a CockroachDB PostgreSQL instance via the
344
- Sequelize ORM.
345
334
  - [`fastify-constraints`](https://github.com/nearform/fastify-constraints)
346
335
  Fastify plugin to add constraints to multiple routes
347
336
  - [`fastify-couchdb`](https://github.com/nigelhanlon/fastify-couchdb) Fastify
@@ -359,10 +348,6 @@ section.
359
348
  - [`fastify-disablecache`](https://github.com/Fdawgs/fastify-disablecache)
360
349
  Fastify plugin to disable client-side caching, inspired by
361
350
  [nocache](https://github.com/helmetjs/nocache).
362
- - [`fastify-dynamodb`](https://github.com/matrus2/fastify-dynamodb) AWS DynamoDB
363
- plugin for Fastify. It exposes
364
- [AWS.DynamoDB.DocumentClient()](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html)
365
- object.
366
351
  - [`fastify-dynareg`](https://github.com/greguz/fastify-dynareg) Dynamic plugin
367
352
  register for Fastify.
368
353
  - [`fastify-envalid`](https://github.com/alemagio/fastify-envalid) Fastify
@@ -370,9 +355,6 @@ section.
370
355
  project.
371
356
  - [`fastify-error-page`](https://github.com/hemerajs/fastify-error-page) Fastify
372
357
  plugin to print errors in structured HTML to the browser.
373
- - [`fastify-esso`](https://github.com/patrickpissurno/fastify-esso) The easiest
374
- authentication plugin for Fastify, with built-in support for Single sign-on
375
- (and great documentation).
376
358
  - [`fastify-event-bus`](https://github.com/Shiva127/fastify-event-bus) Event bus
377
359
  support for Fastify. Built upon [js-event-bus](https://github.com/bcerati/js-event-bus).
378
360
  - [`fastify-evervault`](https://github.com/Briscoooe/fastify-evervault/) Fastify
@@ -455,14 +437,10 @@ section.
455
437
  plugin to serialize JSON responses into XML.
456
438
  - [`fastify-jwt-authz`](https://github.com/Ethan-Arrowood/fastify-jwt-authz) JWT
457
439
  user scope verifier.
458
- - [`fastify-jwt-webapp`](https://github.com/charlesread/fastify-jwt-webapp) JWT
459
- authentication for Fastify-based web apps.
460
440
  - [`fastify-kafkajs`](https://github.com/kffl/fastify-kafkajs) Fastify plugin
461
441
  that adds support for KafkaJS - a modern Apache Kafka client library.
462
442
  - [`fastify-keycloak-adapter`](https://github.com/yubinTW/fastify-keycloak-adapter)
463
443
  A keycloak adapter for a Fastify app.
464
- - [`fastify-koa`](https://github.com/rozzilla/fastify-koa) Convert Koa
465
- middlewares into Fastify plugins
466
444
  - [`fastify-kubernetes`](https://github.com/greguz/fastify-kubernetes) Fastify
467
445
  Kubernetes client plugin.
468
446
  - [`fastify-kysely`](https://github.com/alenap93/fastify-kysely) Fastify
@@ -502,6 +480,8 @@ middlewares into Fastify plugins
502
480
  - [`fastify-mongoose-driver`](https://github.com/alex-ppg/fastify-mongoose)
503
481
  Fastify Mongoose plugin that connects to a MongoDB via the Mongoose plugin
504
482
  with support for Models.
483
+ - [`fastify-mosquitto-dynsec`](https://github.com/LuizTakeda/fastify-mosquitto-dynsec)
484
+ Fastify plugin to manage Mosquitto Dynamic Security over MQTT.
505
485
  - [`fastify-mqtt`](https://github.com/love-lena/fastify-mqtt) Plugin to share
506
486
  [mqtt](https://www.npmjs.com/package/mqtt) client across Fastify.
507
487
  - [`fastify-msgpack`](https://github.com/kenriortega/fastify-msgpack) Fastify
@@ -544,8 +524,6 @@ middlewares into Fastify plugins
544
524
  [`oracledb`](https://github.com/oracle/node-oracledb) connection pool to a
545
525
  Fastify server instance.
546
526
  - [`fastify-orama`](https://github.com/mateonunez/fastify-orama)
547
- - [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
548
- OSM plugin to run overpass queries by OpenStreetMap.
549
527
  - [`fastify-override`](https://github.com/matthyk/fastify-override)
550
528
  Fastify plugin to override decorators, plugins and hooks for testing purposes
551
529
  - [`fastify-param-schema-validation`](https://github.com/Player1205/fastify-param-schema-validation)
@@ -566,8 +544,6 @@ middlewares into Fastify plugins
566
544
  - [`fastify-postgraphile`](https://github.com/alemagio/fastify-postgraphile)
567
545
  Plugin to integrate [PostGraphile](https://postgraphile.org/postgraphile/4/) in
568
546
  a Fastify project.
569
- - [`fastify-postgres-dot-js`](https://github.com/kylerush/fastify-postgresjs) Fastify
570
- PostgreSQL connection plugin that uses [Postgres.js](https://github.com/porsager/postgres).
571
547
  - [`fastify-prettier`](https://github.com/hsynlms/fastify-prettier) A Fastify
572
548
  plugin that uses [prettier](https://github.com/prettier/prettier) under the
573
549
  hood to beautify outgoing responses and/or other things in the Fastify server.
@@ -615,8 +591,6 @@ middlewares into Fastify plugins
615
591
  using name and parameters.
616
592
  - [`fastify-rob-config`](https://github.com/jeromemacias/fastify-rob-config)
617
593
  Fastify Rob-Config integration.
618
- - [`fastify-route-group`](https://github.com/TakNePoidet/fastify-route-group)
619
- Convenient grouping and inheritance of routes.
620
594
  - [`fastify-route-preset`](https://github.com/inyourtime/fastify-route-preset)
621
595
  A Fastify plugin that enables you to create route configurations that can be
622
596
  applied to multiple routes.
@@ -624,9 +598,6 @@ middlewares into Fastify plugins
624
598
  Ensure the existence of defined S3 buckets on the application startup.
625
599
  - [`fastify-schema-constraint`](https://github.com/Eomm/fastify-schema-constraint)
626
600
  Choose the JSON schema to use based on request parameters.
627
- - [`fastify-schema-to-typescript`](https://github.com/thomasthiebaud/fastify-schema-to-typescript)
628
- Generate typescript types based on your JSON/YAML validation schemas so they
629
- are always in sync.
630
601
  - [`fastify-sentry`](https://github.com/alex-ppg/fastify-sentry) Fastify plugin
631
602
  to add the Sentry SDK error handler to requests.
632
603
  - [`fastify-sequelize`](https://github.com/lyquocnam/fastify-sequelize) Fastify
@@ -637,8 +608,6 @@ middlewares into Fastify plugins
637
608
  `fastify-caching`.
638
609
  - [`fastify-ses-mailer`](https://github.com/KaranHotwani/fastify-ses-mailer) A
639
610
  Fastify plugin for sending emails via AWS SES using AWS SDK v3.
640
- - [`fastify-shared-schema`](https://github.com/Adibla/fastify-shared-schema) Plugin
641
- for sharing schemas between different routes.
642
611
  - [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
643
612
  to delay the response from the server.
644
613
  - [`fastify-split-validator`](https://github.com/MetCoder95/fastify-split-validator)
@@ -663,10 +632,6 @@ middlewares into Fastify plugins
663
632
  localhost-only, TLS certificate.
664
633
  - [`fastify-totp`](https://github.com/beliven-it/fastify-totp) A plugin to handle
665
634
  TOTP (e.g. for 2FA).
666
- - [`fastify-type-provider-effect-schema`](https://github.com/daotl/fastify-type-provider-effect-schema)
667
- Fastify
668
- [type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
669
- for [@effect/schema](https://github.com/Effect-TS/effect).
670
635
  - [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
671
636
  Fastify
672
637
  [type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
@@ -677,10 +642,6 @@ middlewares into Fastify plugins
677
642
  request's `user-agent` header.
678
643
  - [`fastify-uws`](https://github.com/geut/fastify-uws) A Fastify plugin to
679
644
  use the web server [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js).
680
- - [`fastify-vhost`](https://github.com/patrickpissurno/fastify-vhost) Proxy
681
- subdomain HTTP requests to another server (useful if you want to point
682
- multiple subdomains to the same IP address, while running different servers on
683
- the same machine).
684
645
  - [`fastify-vue-plugin`](https://github.com/TheNoim/fastify-vue)
685
646
  [Nuxt.js](https://nuxt.com) plugin for Fastify. Control the routes nuxt
686
647
  should use.
@@ -723,10 +684,6 @@ middlewares into Fastify plugins
723
684
 
724
685
  #### [Community Tools](#community-tools)
725
686
 
726
- - [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
727
- generator by directory structure.
728
- - [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building
729
- Fastify APIs using decorators and convert Typescript interface to JSON Schema.
730
687
  - [`fastify-intlayer`](https://intlayer.org/doc/environment/fastify)
731
688
  i18n solution for error handling, email template
732
689
  - [`jeasx`](https://www.jeasx.dev)
@@ -94,7 +94,6 @@
94
94
  - [FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT](#fst_err_route_handler_timeout_option_not_int)
95
95
  - [FST_ERR_ROUTE_REWRITE_NOT_STR](#fst_err_route_rewrite_not_str)
96
96
  - [FST_ERR_ROUTE_MISSING_CONTENT_TYPE](#fst_err_route_missing_content_type)
97
- - [FST_ERR_ROUTE_MISSING_CONTENT](#fst_err_route_missing_content)
98
97
  - [FST_ERR_REOPENED_CLOSE_SERVER](#fst_err_reopened_close_server)
99
98
  - [FST_ERR_REOPENED_SERVER](#fst_err_reopened_server)
100
99
  - [FST_ERR_PLUGIN_VERSION_MISMATCH](#fst_err_plugin_version_mismatch)
@@ -447,7 +446,6 @@ Below is a table with all the error codes used by Fastify.
447
446
  | <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. | - |
448
447
  | <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) |
449
448
  | <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) |
450
- | <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) |
451
449
  | <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) |
452
450
  | <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already started. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
453
451
  | <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) |
@@ -277,8 +277,9 @@ requires heavy resources to be sent after the `data`, for example,
277
277
  as soon as possible.
278
278
 
279
279
  > ℹ️ Note:
280
- > The header `Transfer-Encoding: chunked` will be added once you use
281
- > the trailer. It is a hard requirement for using trailer in Node.js.
280
+ > For HTTP/1 responses, the header `Transfer-Encoding: chunked` will be added
281
+ > once you use a trailer. HTTP/2 does not use chunked transfer encoding and
282
+ > sends trailers using its native trailing headers support.
282
283
 
283
284
  > ℹ️ Note:
284
285
  > Any error passed to `done` callback will be ignored. If you are interested
@@ -339,6 +340,23 @@ to `302` (if status code is not already set by calling `code`).
339
340
  > [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
340
341
  > result in a 500 `TypeError` response.
341
342
 
343
+ > ⚠️ Security:
344
+ > Encoding alone does **not** prevent open redirects. Neither `encodeURI`
345
+ > nor `encodeurl` escapes `/`, so a caller-supplied target such as
346
+ > `//evil.com` or `/\evil.com` survives encoding and is resolved by the
347
+ > browser as a new host (a protocol-relative, authority-opening
348
+ > reference). When `dest` can be influenced by request input
349
+ > (`req.query`, `req.body`, `req.params`, cookies, or headers), validate
350
+ > that the resolved target is same-origin or on an explicit allowlist
351
+ > instead of relying on encoding:
352
+ >
353
+ > ```js
354
+ > const target = new URL(dest, 'https://your-site.example')
355
+ > if (target.origin !== 'https://your-site.example') {
356
+ > return reply.code(400).send()
357
+ > }
358
+ > ```
359
+
342
360
  Example (no `reply.code()` call) sets status code to `302` and redirects to
343
361
  `/home`
344
362
  ```js
@@ -122,6 +122,14 @@ to headers on `not found` routes.
122
122
  > Schema validation may mutate the `request.headers` and
123
123
  > `request.raw.headers` objects, causing the headers to become empty.
124
124
 
125
+ > ℹ️ Note:
126
+ > The setter is additive, so it cannot be used to *remove* a header, and a
127
+ > `headers` schema validator's synchronous `{ value }` result is applied through
128
+ > this same setter. `request.headers` is therefore the incoming request's
129
+ > headers (plus any additive values), not a sanitized or authoritative view. If
130
+ > your application needs an allow-listed header subset, build and consume it
131
+ > explicitly from the validated data or from `request.raw.headers`.
132
+
125
133
  ```js
126
134
  fastify.post('/:params', options, function (request, reply) {
127
135
  console.log(request.body)
@@ -32,7 +32,7 @@ fastify.route(options)
32
32
  <a id="options"></a>
33
33
 
34
34
  * `method`: currently it supports `GET`, `HEAD`, `TRACE`, `DELETE`,
35
- `OPTIONS`, `PATCH`, `PUT` and `POST`. To accept more methods,
35
+ `OPTIONS`, `PATCH`, `PUT`, `POST` and `QUERY`. To accept more methods,
36
36
  the [`addHttpMethod`](./Server.md#addHttpMethod) must be used.
37
37
  It could also be an array of methods.
38
38
  * `url`: the path of the URL to match this route (alias: `path`).
@@ -41,7 +41,7 @@ fastify.route(options)
41
41
  [here](./Validation-and-Serialization.md) for more info.
42
42
 
43
43
  * `body`: validates the body of the request if it is a POST, PUT, PATCH,
44
- TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method.
44
+ TRACE, SEARCH, PROPFIND, PROPPATCH, LOCK or QUERY method.
45
45
  * `querystring` or `query`: validates the querystring. This can be a complete
46
46
  JSON Schema object, with the property `type` of `object` and `properties`
47
47
  object of parameters, or simply the values of what would be contained in the
@@ -198,6 +198,10 @@ The above route declaration is more *Hapi*-like, but if you prefer an
198
198
 
199
199
  `fastify.patch(path, [options], handler)`
200
200
 
201
+ `fastify.query(path, [options], handler)`
202
+
203
+ > The `QUERY` method (RFC 10008) requires a `Content-Type` header.
204
+
201
205
  Example:
202
206
  ```js
203
207
  const opts = {
@@ -213,7 +213,7 @@ ignored.
213
213
 
214
214
  Defines the maximum number of milliseconds for receiving the entire request from
215
215
  the client. See [`server.requestTimeout`
216
- property](https://nodejs.org/dist/latest/docs/api/http.html#servertimeout)
216
+ property](https://nodejs.org/dist/latest/docs/api/http.html#serverrequesttimeout)
217
217
  to understand the effect of this option.
218
218
 
219
219
  When `serverFactory` option is specified, this option is ignored.
@@ -1046,7 +1046,7 @@ be invoked.
1046
1046
 
1047
1047
  This can be useful especially if you have a regex-based route, protecting you
1048
1048
  against [ReDoS
1049
- attacks](https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS).
1049
+ attacks](https://community.owasp.org/attacks/Regular_expression_Denial_of_Service_-_ReDoS).
1050
1050
 
1051
1051
 
1052
1052
  ### `onBadUrl`
@@ -167,6 +167,22 @@ parameters at the top level.
167
167
  > ℹ For using the latest Ajv (v8), refer to the
168
168
  > [`schemaController`](./Server.md#schema-controller) section.
169
169
 
170
+ ##### Header name case
171
+
172
+ Node.js exposes request header names in lowercase. Fastify normalizes header
173
+ names in a `headers` schema to lowercase before compilation, so assertions
174
+ written in the canonical header-name case still match the request, for example
175
+ `required: ['X-Api-Key']`, `dependencies`, and a `propertyNames` deny-list that
176
+ uses `enum` or `const`.
177
+
178
+ Regular-expression constraints on header names are the exception. A
179
+ `patternProperties` key and a `propertyNames` `pattern` are regular expressions
180
+ matched against the already-lowercased header names, and Fastify cannot safely
181
+ rewrite a regular expression. Author them to match lowercase (for example
182
+ `^x-debug-.*$`, not `^X-Debug-.*$`) or make them case-insensitive. A pattern
183
+ written in canonical case will not match, and the assertion it guards will not
184
+ apply.
185
+
170
186
  Example:
171
187
  ```js
172
188
  const bodyJsonSchema = {
@@ -639,6 +655,23 @@ an application.
639
655
  > failure by rejecting (throwing), and it cannot coerce the request part through
640
656
  > a resolved `value`.
641
657
 
658
+ ##### `headers` and the `{ value }` result
659
+
660
+ For the `headers` part specifically, the synchronous success `value` is applied
661
+ through the additive [`request.headers`](./Request.md#headers) setter. That
662
+ setter *adds or overrides* headers on top of the raw request headers; it does
663
+ **not** replace them. Raw request headers remain separately available as
664
+ `request.raw.headers`, and any incoming header that is not present in the
665
+ returned `value` is still visible on `request.headers`.
666
+
667
+ Consequently, a synchronous custom `headers` validator cannot remove a header by
668
+ simply omitting it from `value`. The same applies to the default Ajv validator:
669
+ `request.headers` is the incoming request's headers (plus any additive values)
670
+ and is not a sanitized, authoritative view. If your application needs a
671
+ sanitized or allow-listed header view — for example to drive an authorization
672
+ decision — build and consume that view explicitly from the validated data rather
673
+ than re-reading `request.headers`.
674
+
642
675
  ##### .statusCode property
643
676
 
644
677
  All validation errors have a `.statusCode` property set to `400`, ensuring the
@@ -10,6 +10,7 @@
10
10
  - [Fastify Security Codes](#fastify-security-codes)
11
11
  - [FSTSEC001](#FSTSEC001)
12
12
  - [FSTSEC002](#FSTSEC002)
13
+ - [FSTSEC003](#FSTSEC003)
13
14
  - [Fastify Deprecation Codes](#fastify-deprecation-codes)
14
15
 
15
16
  ## Warnings
@@ -51,6 +52,7 @@ Disabling warnings is not recommended and may cause unexpected behavior.
51
52
  | ---- | ----------- | ------------ | ---------- |
52
53
  | <a id="FSTSEC001">FSTSEC001</a> | A `RegExp` Content-Type is used that may be vulnerable to a CORS bypass, because it does not anchor the essence MIME type. | Start the `RegExp` with `^` or include `;?` so the essence MIME type is detected correctly. | [#4450](https://github.com/fastify/fastify/pull/4450) |
53
54
  | <a id="FSTSEC002">FSTSEC002</a> | A headers schema references an external `$ref` (a schema registered with `addSchema`) that is not reached by header case normalization. Header names in the referenced schema keep their original case and do not match the lowercased request headers, so case-insensitive assertions such as `required` and `dependencies` may not apply. | Inline the header schema instead of referencing it with an external `$ref`. | - |
55
+ | <a id="FSTSEC003">FSTSEC003</a> | A headers schema uses a `$ref` inside `propertyNames` that is not reached by header case normalization. Header names held in the referenced schema keep their original case and do not match the lowercased request headers, so a case-insensitive deny-list or allow-list on header names may not apply. | Inline the `propertyNames` constraint instead of referencing it with a `$ref`. | - |
54
56
 
55
57
  ### Fastify Deprecation Codes
56
58
 
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '6.0.0-alpha.3'
3
+ const VERSION = '6.0.0-alpha.4'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('node:http')
@@ -140,6 +140,7 @@ ContentTypeParser.prototype.getParser = function (contentType) {
140
140
  // with that type.
141
141
  parser = this.customParsers.get(contentType.mediaType)
142
142
  if (parser !== undefined) {
143
+ this.cache.set(ct, parser)
143
144
  return parser
144
145
  }
145
146
 
@@ -203,7 +204,12 @@ ContentTypeParser.prototype.run = function (contentType, handler, request, reply
203
204
  }
204
205
 
205
206
  const resource = new AsyncResource('content-type-parser:run', request)
206
- const done = resource.bind(onDone)
207
+
208
+ // Preserve the request's async context without the per-request function
209
+ // properties and wrappers created by AsyncResource.bind().
210
+ function done (error, body) {
211
+ resource.runInAsyncScope(onDone, undefined, error, body)
212
+ }
207
213
 
208
214
  if (parser.asString === true || parser.asBuffer === true) {
209
215
  rawBody(
@@ -247,12 +253,12 @@ function rawBody (request, reply, options, parser, done) {
247
253
  }
248
254
 
249
255
  let receivedLength = 0
250
- let body = asString ? '' : []
256
+ const chunks = []
251
257
  const payload = request[kRequestPayloadStream] || request.raw
252
258
 
253
- if (asString) {
254
- payload.setEncoding('utf8')
255
- }
259
+ // Always read raw Buffers and decode once at the end: setEncoding() would
260
+ // allocate a StringDecoder per request and force a Buffer.byteLength()
261
+ // rescan of every decoded chunk just to enforce the byte limit.
256
262
 
257
263
  payload.on('data', onData)
258
264
  payload.on('end', onEnd)
@@ -260,7 +266,11 @@ function rawBody (request, reply, options, parser, done) {
260
266
  payload.resume()
261
267
 
262
268
  function onData (chunk) {
263
- receivedLength += asString ? Buffer.byteLength(chunk) : chunk.length
269
+ // A custom preParsing stream may still emit strings
270
+ if (typeof chunk === 'string') {
271
+ chunk = Buffer.from(chunk)
272
+ }
273
+ receivedLength += chunk.length
264
274
  const { receivedEncodedLength = 0 } = payload
265
275
  // The resulting body length must not exceed bodyLimit (see "zip bomb").
266
276
  // The case when encoded length is larger than received length is rather theoretical,
@@ -273,11 +283,7 @@ function rawBody (request, reply, options, parser, done) {
273
283
  return
274
284
  }
275
285
 
276
- if (asString) {
277
- body += chunk
278
- } else {
279
- body.push(chunk)
280
- }
286
+ chunks.push(chunk)
281
287
  }
282
288
 
283
289
  function onEnd (err) {
@@ -298,8 +304,17 @@ function rawBody (request, reply, options, parser, done) {
298
304
  return
299
305
  }
300
306
 
301
- if (!asString) {
302
- body = Buffer.concat(body)
307
+ let body
308
+ if (asString) {
309
+ // Decoding the whole body at once also keeps multi-byte characters
310
+ // split across chunks intact
311
+ body = chunks.length === 1
312
+ ? chunks[0].toString('utf8')
313
+ : Buffer.concat(chunks, receivedLength).toString('utf8')
314
+ } else {
315
+ // Always copy so the parser never receives a view over the shared
316
+ // socket read buffer
317
+ body = Buffer.concat(chunks, receivedLength)
303
318
  }
304
319
 
305
320
  const result = parser.fn(request, body, done)
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { LruMap: Lru } = require('toad-cache')
3
+ const { FifoMap: Fifo } = require('toad-cache')
4
4
 
5
5
  /**
6
6
  * keyValuePairsReg matches a single `parameter-name "=" parameter-value`
@@ -61,9 +61,9 @@ const subtypeNameReg = /^[\w!#$%&'*+.^`|~-]+\s*$/
61
61
 
62
62
  /**
63
63
  * Content-Type internal shared cache
64
- * @type {Lru<ContentType>}
64
+ * @type {Fifo<ContentType>}
65
65
  */
66
- const cache = new Lru(100)
66
+ const cache = new Fifo(100)
67
67
 
68
68
  /**
69
69
  * ContentType parses and represents the value of the content-type header.
@@ -82,7 +82,7 @@ class ContentType {
82
82
  /**
83
83
  * The shared cache of ContentType instances. The cache is used to avoid
84
84
  * creating multiple instances of ContentType for the same header value.
85
- * @type {Lru<ContentType>}
85
+ * @type {Fifo<ContentType>}
86
86
  */
87
87
  static get cache () { return cache }
88
88
 
package/lib/errors.js CHANGED
@@ -466,11 +466,6 @@ const codes = {
466
466
  "Method '%s' must provide a 'Content-Type' header.",
467
467
  400
468
468
  ),
469
- FST_ERR_ROUTE_MISSING_CONTENT: createError(
470
- 'FST_ERR_ROUTE_MISSING_CONTENT',
471
- "Method '%s' must provide a request body.",
472
- 400
473
- ),
474
469
 
475
470
  /**
476
471
  * again listen when close server
@@ -6,8 +6,7 @@ const { validate: validateSchema } = require('./validation')
6
6
  const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
7
7
  const {
8
8
  FST_ERR_CTP_INVALID_MEDIA_TYPE,
9
- FST_ERR_ROUTE_MISSING_CONTENT_TYPE,
10
- FST_ERR_ROUTE_MISSING_CONTENT
9
+ FST_ERR_ROUTE_MISSING_CONTENT_TYPE
11
10
  } = require('./errors')
12
11
  const { setErrorStatusCode } = require('./error-status')
13
12
  const {
@@ -51,20 +50,15 @@ function handleRequest (err, request, reply) {
51
50
  reply.status(400).send(new FST_ERR_ROUTE_MISSING_CONTENT_TYPE(method))
52
51
  return
53
52
  }
54
-
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
53
  }
65
54
 
66
55
  if (ctHeader === undefined) {
67
- if (isEmptyBody(headers)) {
56
+ const contentLength = headers['content-length']
57
+ const transferEncoding = headers['transfer-encoding']
58
+ const isEmptyBody = transferEncoding === undefined &&
59
+ (contentLength === undefined || contentLength === '0')
60
+
61
+ if (isEmptyBody) {
68
62
  // Request has no body to parse
69
63
  handler(request, reply)
70
64
  return
@@ -109,13 +103,6 @@ function handler (request, reply) {
109
103
  }
110
104
  }
111
105
 
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
-
119
106
  function preValidationCallback (err, request, reply) {
120
107
  if (reply.sent === true) return
121
108
 
package/lib/reply.js CHANGED
@@ -95,7 +95,7 @@ Object.defineProperties(Reply.prototype, {
95
95
  },
96
96
  mediaType: {
97
97
  get () {
98
- const contentTypeHeader = this[kReplyHeaders]['content-type']
98
+ const contentTypeHeader = this.getHeader('content-type')
99
99
  return ContentType.from(contentTypeHeader).mediaType
100
100
  }
101
101
  },
@@ -284,6 +284,10 @@ Reply.prototype.header = function (key, value = '') {
284
284
  } else {
285
285
  this[kReplyHeaders][key].push(value)
286
286
  }
287
+ } else if (key === 'set-cookie' && Array.isArray(value)) {
288
+ // Copy the array: later calls push into the stored one, and the caller's
289
+ // array (often a module-level constant) must not grow across requests.
290
+ this[kReplyHeaders][key] = value.slice()
287
291
  } else {
288
292
  this[kReplyHeaders][key] = value
289
293
  }
@@ -370,7 +374,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType
370
374
  return serialize
371
375
  }
372
376
 
373
- Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null, contentType = null) {
377
+ Reply.prototype.compileSerializationSchema = function (schema, httpStatus, contentType) {
374
378
  const { request } = this
375
379
  const { method, url } = request
376
380
 
@@ -606,8 +610,11 @@ function onSendEnd (reply, payload) {
606
610
  header += trailerName
607
611
  }
608
612
  if (header !== '') {
609
- // it must be chunked for trailer to work
610
- reply.header('Transfer-Encoding', 'chunked')
613
+ // HTTP/1 must use chunked encoding for trailers. HTTP/2 forbids
614
+ // the transfer-encoding header and handles trailers natively.
615
+ if (!isHttp2Reply(reply)) {
616
+ reply.header('Transfer-Encoding', 'chunked')
617
+ }
611
618
  reply.header('Trailer', header.trim())
612
619
  } else {
613
620
  reply[kReplyTrailers] = null