fastify 5.11.2 → 6.0.0-alpha.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 (64) hide show
  1. package/README.md +9 -9
  2. package/SPONSORS.md +1 -0
  3. package/build/build-validation.js +20 -34
  4. package/docs/Guides/Getting-Started.md +2 -2
  5. package/docs/Guides/Serverless.md +34 -2
  6. package/docs/Reference/Encapsulation.md +31 -2
  7. package/docs/Reference/Errors.md +81 -6
  8. package/docs/Reference/Hooks.md +0 -4
  9. package/docs/Reference/LTS.md +6 -5
  10. package/docs/Reference/Request.md +18 -6
  11. package/docs/Reference/Routes.md +13 -0
  12. package/docs/Reference/Server.md +8 -88
  13. package/docs/Reference/TypeScript.md +3 -49
  14. package/docs/Reference/Warnings.md +4 -11
  15. package/fastify.d.ts +10 -24
  16. package/fastify.js +17 -34
  17. package/lib/config-validator.js +368 -475
  18. package/lib/content-type-parser.js +4 -0
  19. package/lib/decorate.js +1 -1
  20. package/lib/errors.js +9 -3
  21. package/lib/log-controller.js +3 -5
  22. package/lib/logger-factory.js +1 -1
  23. package/lib/reply.js +7 -3
  24. package/lib/route.js +4 -9
  25. package/lib/server.js +2 -3
  26. package/lib/warnings.js +2 -46
  27. package/package.json +2 -3
  28. package/test/404s.test.js +0 -49
  29. package/test/allow-unsafe-regex.test.js +12 -4
  30. package/test/case-insensitive.test.js +12 -4
  31. package/test/content-parser.test.js +32 -0
  32. package/test/custom-querystring-parser.test.js +0 -16
  33. package/test/decorator-instance-properties.test.js +49 -0
  34. package/test/hooks.test.js +6 -1
  35. package/test/http-methods/custom-http-methods.test.js +12 -17
  36. package/test/http-methods/trace.test.js +1 -1
  37. package/test/https/custom-https-server.test.js +1 -1
  38. package/test/internals/errors.test.js +15 -5
  39. package/test/internals/initial-config.test.js +56 -38
  40. package/test/internals/logger.test.js +17 -4
  41. package/test/internals/reply.test.js +1 -1
  42. package/test/internals/request.test.js +8 -24
  43. package/test/logger/logging.test.js +2 -121
  44. package/test/logger/request.test.js +1 -36
  45. package/test/plugin.4.test.js +5 -4
  46. package/test/reply-trailers.test.js +92 -4
  47. package/test/route-prefix.test.js +46 -18
  48. package/test/route.1.test.js +2 -2
  49. package/test/router-options.test.js +25 -523
  50. package/test/set-error-handler.test.js +7 -9
  51. package/test/types/errors.tst.ts +2 -1
  52. package/test/types/fastify.tst.ts +44 -48
  53. package/test/types/instance.tst.ts +4 -5
  54. package/test/types/logger.tst.ts +3 -7
  55. package/test/use-semicolon-delimiter.test.js +0 -60
  56. package/types/context.d.ts +0 -20
  57. package/types/errors.d.ts +2 -1
  58. package/types/instance.d.ts +0 -4
  59. package/types/logger.d.ts +2 -17
  60. package/types/plugin.d.ts +0 -9
  61. package/types/request.d.ts +0 -4
  62. package/.pi/bash-confirm-whitelist.json +0 -180
  63. package/lib/promise.js +0 -23
  64. package/test/internals/promise.test.js +0 -63
package/README.md CHANGED
@@ -46,8 +46,8 @@ developer experience with the least overhead and a powerful plugin architecture.
46
46
  It is inspired by Hapi and Express and as far as we know, it is one of the
47
47
  fastest web frameworks in town.
48
48
 
49
- The `main` branch refers to the Fastify `v5` release.
50
- Check out the [`4.x` branch](https://github.com/fastify/fastify/tree/4.x) for `v4`.
49
+ The `main` branch refers to the Fastify `v6` release.
50
+ Check out the [`5.x` branch](https://github.com/fastify/fastify/tree/5.x) for `v5`.
51
51
 
52
52
  ### Table of Contents
53
53
 
@@ -73,7 +73,7 @@ mkdir my-app
73
73
  cd my-app
74
74
  ```
75
75
 
76
- Generate a fastify project with `npm init`:
76
+ Generate a Fastify project with `npm init`:
77
77
 
78
78
  ```sh
79
79
  npm init fastify
@@ -121,7 +121,7 @@ import Fastify from 'fastify'
121
121
  const fastify = Fastify({
122
122
  logger: true
123
123
  })
124
- // CommonJs
124
+ // CommonJS
125
125
  const fastify = require('fastify')({
126
126
  logger: true
127
127
  })
@@ -147,7 +147,7 @@ import Fastify from 'fastify'
147
147
  const fastify = Fastify({
148
148
  logger: true
149
149
  })
150
- // CommonJs
150
+ // CommonJS
151
151
  const fastify = require('fastify')({
152
152
  logger: true
153
153
  })
@@ -181,7 +181,7 @@ If you learn best by reading code, explore the official [demo](https://github.co
181
181
  ### Core features
182
182
 
183
183
  - **Highly performant:** as far as we know, Fastify is one of the fastest web
184
- frameworks in town, depending on the code complexity we can serve up to 76+
184
+ frameworks in town, depending on the code complexity we can serve more than 76
185
185
  thousand requests per second.
186
186
  - **Extensible:** Fastify is fully extensible via its hooks, plugins, and
187
187
  decorators.
@@ -189,7 +189,7 @@ If you learn best by reading code, explore the official [demo](https://github.co
189
189
  Schema](https://json-schema.org/) to validate your routes and serialize your
190
190
  outputs. Internally Fastify compiles the schema in a highly performant
191
191
  function.
192
- - **Logging:** logs are extremely important but are costly; we chose the best
192
+ - **Logging:** logs are extremely important, but are costly; we chose the best
193
193
  logger to almost remove this cost, [Pino](https://github.com/pinojs/pino)!
194
194
  - **Developer friendly:** the framework is built to be very expressive and help
195
195
  developers in their daily use without sacrificing performance and
@@ -212,8 +212,8 @@ second average
212
212
  | - | | | |
213
213
  | `http.Server` | 16.14.2 | ✗ | 74,513 |
214
214
 
215
- These benchmarks taken using https://github.com/fastify/benchmarks. This is a
216
- synthetic "hello world" benchmark that aims to evaluate the framework overhead.
215
+ These benchmarks were taken using https://github.com/fastify/benchmarks. This is
216
+ a synthetic "hello world" benchmark that aims to evaluate the framework overhead.
217
217
  The overhead that each framework has on your application depends on your
218
218
  application. You should __always__ benchmark if performance matters to you.
219
219
 
package/SPONSORS.md CHANGED
@@ -19,6 +19,7 @@ or [GitHub Sponsors](https://github.com/sponsors/fastify)!
19
19
  - [atagon GmbH](https://github.com/atagon-GmbH)
20
20
  - [Photon](https://github.com/photon-hq)
21
21
  - [N-iX](https://www.n-ix.com/)
22
+ - [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=fastify)
22
23
 
23
24
  ## Tier 2
24
25
 
@@ -30,26 +30,19 @@ const defaultInitOptions = {
30
30
  requestTimeout: 0, // no limit
31
31
  handlerTimeout: 0, // no timeout (disabled by default)
32
32
  bodyLimit: 1024 * 1024, // 1 MiB
33
- caseSensitive: true,
34
- allowUnsafeRegex: false,
35
- disableRequestLogging: false, // TODO: remove it in v6
36
- ignoreTrailingSlash: false,
37
- ignoreDuplicateSlashes: false,
38
- maxParamLength: 100,
39
33
  onProtoPoisoning: 'error',
40
34
  onConstructorPoisoning: 'error',
41
35
  pluginTimeout: 10000,
42
36
  requestIdHeader: false,
43
- requestIdLogLabel: 'reqId', // TODO: remove it in v6
44
37
  http2SessionTimeout: 72000, // 72 seconds
45
38
  exposeHeadRoutes: true,
46
- useSemicolonDelimiter: false,
47
- allowErrorHandlerOverride: true, // TODO: set to false in v6
39
+ allowErrorHandlerOverride: false,
48
40
  routerOptions: {
41
+ allowUnsafeRegex: false,
42
+ caseSensitive: true,
49
43
  ignoreTrailingSlash: false,
50
44
  ignoreDuplicateSlashes: false,
51
45
  maxParamLength: 100,
52
- allowUnsafeRegex: false,
53
46
  useSemicolonDelimiter: false
54
47
  }
55
48
  }
@@ -75,8 +68,6 @@ const schema = {
75
68
  requestTimeout: { type: 'integer', default: defaultInitOptions.requestTimeout },
76
69
  handlerTimeout: { type: 'integer', default: defaultInitOptions.handlerTimeout },
77
70
  bodyLimit: { type: 'integer', default: defaultInitOptions.bodyLimit },
78
- caseSensitive: { type: 'boolean', default: defaultInitOptions.caseSensitive },
79
- allowUnsafeRegex: { type: 'boolean', default: defaultInitOptions.allowUnsafeRegex },
80
71
  http2: { type: 'boolean' },
81
72
  https: {
82
73
  if: {
@@ -99,42 +90,37 @@ const schema = {
99
90
  },
100
91
  ignoreTrailingSlash: { type: 'boolean', default: defaultInitOptions.ignoreTrailingSlash },
101
92
  ignoreDuplicateSlashes: { type: 'boolean', default: defaultInitOptions.ignoreDuplicateSlashes },
102
- disableRequestLogging: {
103
- default: false
104
- },
105
- maxParamLength: { type: 'integer', default: defaultInitOptions.maxParamLength },
106
93
  onProtoPoisoning: { type: 'string', default: defaultInitOptions.onProtoPoisoning },
107
94
  onConstructorPoisoning: { type: 'string', default: defaultInitOptions.onConstructorPoisoning },
108
95
  pluginTimeout: { type: 'integer', default: defaultInitOptions.pluginTimeout },
109
96
  requestIdHeader: { anyOf: [{ type: 'boolean' }, { type: 'string' }], default: defaultInitOptions.requestIdHeader },
110
- requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel },
111
97
  http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
112
98
  exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
113
- useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.useSemicolonDelimiter },
114
99
  routerOptions: {
115
100
  type: 'object',
116
- additionalProperties: true,
101
+ additionalProperties: false,
117
102
  properties: {
103
+ allowUnsafeRegex: { type: 'boolean', default: defaultInitOptions.routerOptions.allowUnsafeRegex },
104
+ caseSensitive: { type: 'boolean', default: defaultInitOptions.routerOptions.caseSensitive },
105
+ constraints: {
106
+ type: 'object',
107
+ additionalProperties: {
108
+ type: 'object',
109
+ required: ['name', 'storage', 'validate', 'deriveConstraint'],
110
+ additionalProperties: true,
111
+ properties: {
112
+ name: { type: 'string' },
113
+ storage: {},
114
+ validate: {},
115
+ deriveConstraint: {}
116
+ }
117
+ }
118
+ },
118
119
  ignoreTrailingSlash: { type: 'boolean', default: defaultInitOptions.routerOptions.ignoreTrailingSlash },
119
120
  ignoreDuplicateSlashes: { type: 'boolean', default: defaultInitOptions.routerOptions.ignoreDuplicateSlashes },
120
121
  maxParamLength: { type: 'integer', default: defaultInitOptions.routerOptions.maxParamLength },
121
- allowUnsafeRegex: { type: 'boolean', default: defaultInitOptions.routerOptions.allowUnsafeRegex },
122
122
  useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.routerOptions.useSemicolonDelimiter }
123
123
  }
124
- },
125
- constraints: {
126
- type: 'object',
127
- additionalProperties: {
128
- type: 'object',
129
- required: ['name', 'storage', 'validate', 'deriveConstraint'],
130
- additionalProperties: true,
131
- properties: {
132
- name: { type: 'string' },
133
- storage: {},
134
- validate: {},
135
- deriveConstraint: {}
136
- }
137
- }
138
124
  }
139
125
  }
140
126
  }
@@ -303,7 +303,7 @@ export default fastifyPlugin(dbConnector)
303
303
  ```js
304
304
  // CommonJs
305
305
  /**
306
- * @type {import('fastify-plugin').FastifyPlugin}
306
+ * @type {import('fastify-plugin').FastifyPluginAsync}
307
307
  */
308
308
  const fastifyPlugin = require('fastify-plugin')
309
309
 
@@ -616,5 +616,5 @@ npm start
616
616
  speed](https://www.youtube.com/watch?v=5z46jJZNe8k) by
617
617
  [@mcollina](https://github.com/mcollina)
618
618
  - [What if I told you that HTTP can be
619
- fast](https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/)
619
+ fast](https://web.archive.org/web/20251006142253/https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/)
620
620
  by [@delvedor](https://github.com/delvedor)
@@ -256,9 +256,41 @@ curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \
256
256
  {"message":"Hello Fastify!"}
257
257
  ```
258
258
 
259
+ #### Per-route logging
260
+
261
+ Google Cloud Functions exposes one entry point for your Fastify instance, so
262
+ the dashboard may show all HTTP traffic under that function. Instead of
263
+ deploying one function per endpoint, keep the single Fastify instance and emit
264
+ a structured log line after each request with the matched route:
265
+
266
+ ```js
267
+ fastify.addHook('onResponse', async (request, reply) => {
268
+ request.log.info({
269
+ route: request.routeOptions.url,
270
+ method: request.method,
271
+ statusCode: reply.statusCode,
272
+ responseTime: reply.elapsedTime
273
+ }, 'request completed')
274
+ })
275
+ ```
276
+
277
+ `request.routeOptions.url` is the route pattern, so `/users/123` and
278
+ `/users/456` are grouped under `/users/:id`. With Fastify's logger enabled, the
279
+ fields are emitted as structured log fields, as described in the [Cloud
280
+ Logging structured logging documentation](https://cloud.google.com/logging/docs/structured-logging)
281
+ and the [Cloud Functions logging guide](https://cloud.google.com/functions/docs/monitoring/logging).
282
+ In Cloud Logging, create a [log-based
283
+ metric](https://cloud.google.com/logging/docs/logs-based-metrics) or filter on
284
+ `jsonPayload.route` to get per-route request counts, latency, and error rates.
285
+
286
+ The same hook works when deploying through Firebase Functions with `onRequest`.
287
+
259
288
  ### References
260
289
  - [Google Cloud Functions - Node.js Quickstart
261
- ](https://docs.cloud.google.com/run/docs/quickstarts/functions/deploy-functions-gcloud)
290
+ ](https://cloud.google.com/run/docs/quickstarts/functions/deploy-functions-gcloud)
291
+ - [Cloud Logging - Structured Logging](https://cloud.google.com/logging/docs/structured-logging)
292
+ - [Cloud Logging - Log-based Metrics](https://cloud.google.com/logging/docs/logs-based-metrics)
293
+ - [Cloud Functions - Logging](https://cloud.google.com/functions/docs/monitoring/logging)
262
294
 
263
295
  ## Google Firebase Functions
264
296
 
@@ -594,4 +626,4 @@ https://vercel.com/templates/backend/fastify-on-vercel).
594
626
  [Fluid compute](https://vercel.com/docs/fluid-compute) currently
595
627
  requires an explicit opt-in. Learn more about enabling Fluid compute
596
628
  [here](
597
- https://vercel.com/docs/fluid-compute#enabling-fluid-compute).
629
+ https://vercel.com/docs/fluid-compute#enabling-fluid-compute).
@@ -140,7 +140,7 @@ update the code as follows:
140
140
  'use strict'
141
141
 
142
142
  const fastify = require('fastify')()
143
- const fastifyPlugin = require('fastify-plugin')
143
+ const fp = require('fastify-plugin')
144
144
 
145
145
  fastify.decorateRequest('answer', 42)
146
146
 
@@ -161,7 +161,7 @@ fastify.register(async function publicContext (childServer) {
161
161
  }
162
162
  })
163
163
 
164
- childServer.register(fastifyPlugin(grandchildContext))
164
+ childServer.register(fp(grandchildContext))
165
165
 
166
166
  async function grandchildContext (grandchildServer) {
167
167
  grandchildServer.decorateRequest('bar', 'bar')
@@ -192,4 +192,33 @@ curl http://127.0.0.1:8000/three
192
192
  # {"answer":42,"foo":"foo","bar":"bar"}
193
193
  ```
194
194
 
195
+ `fastify-plugin` breaks encapsulation only for the plugin it wraps. Plugins
196
+ registered inside it without `fastify-plugin` still create new encapsulated
197
+ contexts:
198
+
199
+ ```js
200
+ 'use strict'
201
+
202
+ const fastify = require('fastify')()
203
+ const fp = require('fastify-plugin')
204
+
205
+ fastify.register(fp(async function sharedContext (childServer) {
206
+ childServer.decorate('foo', 'foo')
207
+
208
+ childServer.register(async function encapsulatedContext (grandchildServer) {
209
+ grandchildServer.decorate('bar', 'bar')
210
+ })
211
+ }))
212
+
213
+ await fastify.ready()
214
+
215
+ console.log(fastify.foo) // 'foo'
216
+ console.log(fastify.bar) // undefined
217
+ ```
218
+
219
+ The `foo` decorator is available in the root context because it is added
220
+ directly by the plugin wrapped with `fastify-plugin`. The nested `register`
221
+ call still creates a grandchild context, so the `bar` decorator remains
222
+ available only in that context and its children.
223
+
195
224
  [fastify-plugin]: https://github.com/fastify/fastify-plugin
@@ -11,6 +11,7 @@
11
11
  - [Errors In Fastify](#errors-in-fastify)
12
12
  - [Errors In Input Data](#errors-in-input-data)
13
13
  - [Catching Uncaught Errors In Fastify](#catching-uncaught-errors-in-fastify)
14
+ - [What The Default Error Handler Sends](#what-the-default-error-handler-sends)
14
15
  - [Errors In Fastify Lifecycle Hooks And A Custom Error Handler](#errors-in-fastify-lifecycle-hooks-and-a-custom-error-handler)
15
16
  - [Fastify Error Codes](#fastify-error-codes)
16
17
  - [FST_ERR_NOT_FOUND](#fst_err_not_found)
@@ -31,7 +32,7 @@
31
32
  - [FST_ERR_CTP_EMPTY_JSON_BODY](#fst_err_ctp_empty_json_body)
32
33
  - [FST_ERR_CTP_INVALID_JSON_BODY](#fst_err_ctp_invalid_json_body)
33
34
  - [FST_ERR_CTP_INSTANCE_ALREADY_STARTED](#fst_err_ctp_instance_already_started)
34
- - [FST_ERR_INSTANCE_ALREADY_LISTENING](#fst_err_instance_already_listening)
35
+ - [FST_ERR_INSTANCE_ALREADY_STARTED](#fst_err_instance_already_started)
35
36
  - [FST_ERR_DEC_ALREADY_PRESENT](#fst_err_dec_already_present)
36
37
  - [FST_ERR_DEC_DEPENDENCY_INVALID_TYPE](#fst_err_dec_dependency_invalid_type)
37
38
  - [FST_ERR_DEC_MISSING_DEPENDENCY](#fst_err_dec_missing_dependency)
@@ -83,6 +84,7 @@
83
84
  - [FST_ERR_ROUTE_HANDLER_NOT_FN](#fst_err_route_handler_not_fn)
84
85
  - [FST_ERR_ROUTE_MISSING_HANDLER](#fst_err_route_missing_handler)
85
86
  - [FST_ERR_ROUTE_METHOD_INVALID](#fst_err_route_method_invalid)
87
+ - [FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED](#fst_err_route_method_already_supported)
86
88
  - [FST_ERR_ROUTE_METHOD_NOT_SUPPORTED](#fst_err_route_method_not_supported)
87
89
  - [FST_ERR_ROUTE_LOG_LEVEL_INVALID](#fst_err_route_log_level_invalid)
88
90
  - [FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED](#fst_err_route_body_validation_schema_not_supported)
@@ -142,10 +144,82 @@ performance. This includes:
142
144
  })`
143
145
 
144
146
  In both cases, the error will be caught safely and routed to Fastify's default
145
- error handler, resulting in a generic `500 Internal Server Error` response.
147
+ error handler, resulting in a `500 Internal Server Error` response.
146
148
 
147
- To customize this behavior, use
148
- [`setErrorHandler`](./Server.md#seterrorhandler).
149
+ #### What The Default Error Handler Sends
150
+ The default error handler serializes the error into a JSON body with the
151
+ `statusCode`, `error`, and `message` properties, plus `code` when the error
152
+ carries one:
153
+
154
+ ```js
155
+ app.get('/', async () => { throw new Error('kaboom') })
156
+ ```
157
+
158
+ ```json
159
+ {
160
+ "statusCode": 500,
161
+ "error": "Internal Server Error",
162
+ "message": "kaboom"
163
+ }
164
+ ```
165
+
166
+ The `error` property is the generic HTTP status text, but **`message` is
167
+ `error.message` verbatim**. This applies to every status code, including `500`.
168
+ Fastify's built-in error serializer emits only these four properties, so the
169
+ stack trace is not part of the default payload — but a route-level response
170
+ schema replaces that serializer, and one that declares a `stack` property will
171
+ serialize it.
172
+
173
+ > Security:
174
+ > Because `message` and `code` are forwarded as-is, errors thrown by libraries
175
+ > deeper in your application are exposed to the client. A database driver
176
+ > error, for example, can leak schema details and query text:
177
+ >
178
+ > ```json
179
+ > {
180
+ > "statusCode": 500,
181
+ > "code": "ER_BAD_FIELD_ERROR",
182
+ > "error": "Internal Server Error",
183
+ > "message": "Unknown column 'username' in 'field list'"
184
+ > }
185
+ > ```
186
+ >
187
+ > Fastify does not distinguish between development and production here. If
188
+ > unexpected errors must not reach the client, handle this explicitly.
189
+
190
+ Register a [`setErrorHandler`](./Server.md#seterrorhandler) to replace the
191
+ message for errors you did not raise deliberately, while letting the ones you
192
+ did pass through:
193
+
194
+ ```js
195
+ app.setErrorHandler(function (error, request, reply) {
196
+ // Errors with a statusCode below 500 were raised deliberately by this
197
+ // application, as were validation errors. A status code below 500 is not on
198
+ // its own a guarantee that the message is safe to expose — narrow this
199
+ // condition if any of yours are not.
200
+ if (error.validation || (error.statusCode && error.statusCode < 500)) {
201
+ return reply.send(error)
202
+ }
203
+
204
+ // Anything else is unexpected: log it, but do not describe it to the client.
205
+ this.log.error({ err: error }, 'unhandled error')
206
+ reply.status(500).send({
207
+ statusCode: 500,
208
+ error: 'Internal Server Error',
209
+ message: 'Internal Server Error'
210
+ })
211
+ })
212
+ ```
213
+
214
+ The handler above is registered on the root instance, so it applies to every
215
+ route. Error handlers are encapsulated; for how they resolve across plugin
216
+ contexts, see
217
+ [the next section](#errors-in-fastify-lifecycle-hooks-and-a-custom-error-handler).
218
+
219
+ Note that a route-level response schema is still applied to whatever the error
220
+ handler sends, and will reshape the payload accordingly — including properties
221
+ the built-in error serializer would have omitted, such as `stack`. See
222
+ [Serialization](./Validation-and-Serialization.md#serialization).
149
223
 
150
224
  ### Errors In Fastify Lifecycle Hooks And A Custom Error Handler
151
225
 
@@ -312,7 +386,7 @@ Below is a table with all the error codes used by Fastify.
312
386
  | <a id="fst_err_ctp_empty_json_body">FST_ERR_CTP_EMPTY_JSON_BODY</a> | Body is not valid JSON but content-type is set to <code>application/json</code>. | Check if the request body is valid JSON. | [#5925](https://github.com/fastify/fastify/pull/5925) |
313
387
  | <a id="fst_err_ctp_invalid_json_body">FST_ERR_CTP_INVALID_JSON_BODY</a> | Body cannot be empty when content-type is set to <code>application/json</code>. | Check the request body. | [#1253](https://github.com/fastify/fastify/pull/1253) |
314
388
  | <a id="fst_err_ctp_instance_already_started">FST_ERR_CTP_INSTANCE_ALREADY_STARTED</a> | Fastify is already started. | - | [#4554](https://github.com/fastify/fastify/pull/4554) |
315
- | <a id="fst_err_instance_already_listening">FST_ERR_INSTANCE_ALREADY_LISTENING</a> | Fastify instance is already listening. | - | [#4554](https://github.com/fastify/fastify/pull/4554) |
389
+ | <a id="fst_err_instance_already_started">FST_ERR_INSTANCE_ALREADY_STARTED</a> | Fastify instance is already started. | - | [#6098](https://github.com/fastify/fastify/pull/6098) |
316
390
  | <a id="fst_err_dec_already_present">FST_ERR_DEC_ALREADY_PRESENT</a> | A decorator with the same name is already registered. | Use a different decorator name. | [#1168](https://github.com/fastify/fastify/pull/1168) |
317
391
  | <a id="fst_err_dec_dependency_invalid_type">FST_ERR_DEC_DEPENDENCY_INVALID_TYPE</a> | The dependencies of decorator must be of type `Array`. | Use an array for the dependencies. | [#3090](https://github.com/fastify/fastify/pull/3090) |
318
392
  | <a id="fst_err_dec_missing_dependency">FST_ERR_DEC_MISSING_DEPENDENCY</a> | The decorator cannot be registered due to a missing dependency. | Register the missing dependency. | [#1168](https://github.com/fastify/fastify/pull/1168) |
@@ -364,6 +438,7 @@ Below is a table with all the error codes used by Fastify.
364
438
  | <a id="fst_err_route_handler_not_fn">FST_ERR_ROUTE_HANDLER_NOT_FN</a> | Handler for the route must be a function. | Use a function for the handler. | [#4554](https://github.com/fastify/fastify/pull/4554) |
365
439
  | <a id="fst_err_route_missing_handler">FST_ERR_ROUTE_MISSING_HANDLER</a> | Missing handler function for the route. | Add a handler function. | [#4554](https://github.com/fastify/fastify/pull/4554) |
366
440
  | <a id="fst_err_route_method_invalid">FST_ERR_ROUTE_METHOD_INVALID</a> | Method is not a valid value. | Use a valid value for the method. | [#4750](https://github.com/fastify/fastify/pull/4750) |
441
+ | <a id="fst_err_route_method_already_supported">FST_ERR_ROUTE_METHOD_ALREADY_SUPPORTED</a> | The HTTP method is already supported. | Pass `{ overrideExisting: true }` to explicitly override it. | [#6909](https://github.com/fastify/fastify/pull/6909) |
367
442
  | <a id="fst_err_route_method_not_supported">FST_ERR_ROUTE_METHOD_NOT_SUPPORTED</a> | Method is not supported for the route. | Use a supported method. | [#4554](https://github.com/fastify/fastify/pull/4554) |
368
443
  | <a id="fst_err_route_log_level_invalid">FST_ERR_ROUTE_LOG_LEVEL_INVALID</a> | `logLevel` must match a configured logger level. | Use one of the configured logger levels for the route. | [#6523](https://github.com/fastify/fastify/pull/6523) |
369
444
  | <a id="fst_err_route_body_validation_schema_not_supported">FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED</a> | Body validation schema route is not supported. | Use a different different method for the route. | [#4554](https://github.com/fastify/fastify/pull/4554) |
@@ -374,7 +449,7 @@ Below is a table with all the error codes used by Fastify.
374
449
  | <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
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) |
376
451
  | <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) |
377
- | <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already listening. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
452
+ | <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already started. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
378
453
  | <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) |
379
454
  | <a id="fst_err_plugin_callback_not_fn">FST_ERR_PLUGIN_CALLBACK_NOT_FN</a> | Callback for a hook is not a function. | Use a function for the callback. | [#3106](https://github.com/fastify/fastify/pull/3106) |
380
455
  | <a id="fst_err_plugin_not_valid">FST_ERR_PLUGIN_NOT_VALID</a> | Plugin must be a function or a promise. | Use a function or a promise for the plugin. | [#3106](https://github.com/fastify/fastify/pull/3106) |
@@ -260,10 +260,6 @@ The `onResponse` hook is executed when a response has been sent, so you will not
260
260
  be able to send more data to the client. It can however be useful for sending
261
261
  data to external services, for example, to gather statistics.
262
262
 
263
- > ℹ️ Note:
264
- > Setting `disableRequestLogging` to `true` will disable any error log
265
- > inside the `onResponse` hook. In this case use `try - catch` to log errors.
266
-
267
263
  ### onTimeout
268
264
 
269
265
  ```js
@@ -60,7 +60,8 @@ For more information, see the [Never Ending Support][hd-link] service offered.
60
60
  | 2.0.0 | 2019-02-25 | 2021-01-31 | 6, 8, 10, 12, 14 | |
61
61
  | 3.0.0 | 2020-07-07 | 2023-06-30 | 10, 12, 14, 16, 18 | v5(18) |
62
62
  | 4.0.0 | 2022-06-08 | 2025-06-30 | 14, 16, 18, 20, 22 | v5(18), v5(20) |
63
- | 5.0.0 | 2024-09-17 | TBD | 20, 22 | v5(20) |
63
+ | 5.0.0 | 2024-09-17 | TBD | 20, 22, 24, 26 | v5(20) |
64
+ | 6.0.0 | TDB | TBD | 24, 26 | v6(24) |
64
65
 
65
66
  ### CI Tested Operating Systems
66
67
  <a id="supported-os"></a>
@@ -73,10 +74,10 @@ YAML workflow labels below:
73
74
 
74
75
  | OS | YAML Workflow Label | Package Manager | Node.js | Nsolid(Node) |
75
76
  | ------- | ------------------- | --------------- | ----------- | ------------- |
76
- | Linux | `ubuntu-latest` | npm | 20 | v5(20) |
77
- | Linux | `ubuntu-latest` | yarn,pnpm | 20 | v5(20) |
78
- | Windows | `windows-latest` | npm | 20 | v5(20) |
79
- | MacOS | `macos-latest` | npm | 20 | v5(20) |
77
+ | Linux | `ubuntu-latest` | npm | 24 | v6(24) |
78
+ | Linux | `ubuntu-latest` | yarn,pnpm | 24 | v6(24) |
79
+ | Windows | `windows-latest` | npm | 24 | v6(24) |
80
+ | MacOS | `macos-latest` | npm | 24 | v6(24) |
80
81
 
81
82
  When using [yarn](https://yarnpkg.com/), the `--ignore-engines` flag may be required.
82
83
 
@@ -9,7 +9,11 @@ Request is a core Fastify object containing the following fields:
9
9
  - `body` - The request payload, see [Content-Type Parser](./ContentTypeParser.md)
10
10
  for details on what request payloads Fastify natively parses and how to support
11
11
  other content types.
12
- - `params` - The params matching the URL.
12
+ - `params` - The params matching the URL. Values are percent-decoded
13
+ (for example `%20` becomes a space, and `%2f` becomes `/`). Decoded
14
+ values may contain `.`, `..`, `/`, or other characters; treat them as
15
+ untrusted input. See the security note below and
16
+ [Routes - Url building](./Routes.md#url-building).
13
17
  - [`headers`](#headers) - The headers getter and setter.
14
18
  - `raw` - The incoming HTTP request from Node core.
15
19
  - `server` - The Fastify server instance, scoped to the current
@@ -35,13 +39,21 @@ Request is a core Fastify object containing the following fields:
35
39
  [`trustProxy`](./Server.md#factory-trust-proxy) is enabled).
36
40
 
37
41
  > ⚠️ Security:
42
+ > `request.params`, `request.query`, `request.headers`, and `request.body`
43
+ > are untrusted network input. Route parameter values are percent-decoded
44
+ > before your handler runs, so a segment like `..%2ffile` becomes `../file`
45
+ > in `request.params`. Do not use parameter values as filesystem paths,
46
+ > template names, or redirect targets without validating or containing
47
+ > them. Prefer [`@fastify/static`](https://github.com/fastify/fastify-static)
48
+ > (or `reply.sendFile`) when serving files from a root directory.
49
+ >
38
50
  > `request.ip`, `request.ips`, `request.host`, `request.hostname`,
39
51
  > `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).
52
+ > (socket and/or forwarding headers) and should also be treated as
53
+ > untrusted input. Fastify does not perform security validation for
54
+ > business logic. If these values are used in security-sensitive decisions,
55
+ > they must be validated explicitly (for example: trusted proxy
56
+ > configuration, allow-lists, strict parsing, and normalization).
45
57
 
46
58
  - `method` - The method of the incoming request.
47
59
  - `url` - The URL of the incoming request.
@@ -328,6 +328,19 @@ Prefer a single parameter approach, especially on routes that are on the hot
328
328
  path of your application. For more details, see
329
329
  [find-my-way](https://github.com/delvedor/find-my-way).
330
330
 
331
+ > ⚠️ Security:
332
+ > Fastify (via find-my-way) percent-decodes route parameters and wildcards
333
+ > before they reach your handler. Encoded separators in a segment are
334
+ > decoded in the parameter value: for a route `/download/:file`, a request
335
+ > to `/download/..%2fsecret.txt` yields
336
+ > `request.params.file === '../secret.txt'`. Parameters are untrusted
337
+ > input. Do not pass them to `path.join`, `fs` APIs, template engines, or
338
+ > redirects without validation or path containment. To serve files from a
339
+ > directory root, use
340
+ > [`@fastify/static`](https://github.com/fastify/fastify-static) instead of
341
+ > joining `request.params` into a filesystem path yourself. See also
342
+ > [Request](./Request.md).
343
+
331
344
  To include a colon in a path without declaring a parameter, use a double colon.
332
345
  For example:
333
346
  ```js