fastify 4.18.0 → 4.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/docs/Guides/Ecosystem.md +5 -0
  2. package/docs/Guides/Serverless.md +43 -1
  3. package/docs/Guides/Testing.md +135 -2
  4. package/docs/Guides/Write-Plugin.md +3 -1
  5. package/docs/Reference/Principles.md +78 -0
  6. package/docs/Reference/Request.md +2 -0
  7. package/docs/Reference/Routes.md +5 -0
  8. package/docs/Reference/Server.md +5 -2
  9. package/docs/Reference/Type-Providers.md +9 -2
  10. package/docs/Reference/TypeScript.md +21 -6
  11. package/fastify.d.ts +1 -1
  12. package/fastify.js +23 -8
  13. package/lib/contentTypeParser.js +1 -1
  14. package/lib/reply.js +2 -2
  15. package/lib/request.js +18 -8
  16. package/lib/route.js +0 -1
  17. package/lib/schema-controller.js +6 -6
  18. package/lib/schemas.js +2 -2
  19. package/lib/server.js +56 -30
  20. package/lib/symbols.js +1 -0
  21. package/package.json +9 -8
  22. package/test/buffer.test.js +51 -0
  23. package/test/close-pipelining.test.js +4 -1
  24. package/test/close.test.js +148 -6
  25. package/test/custom-http-server.test.js +13 -2
  26. package/test/genReqId.test.js +42 -0
  27. package/test/header-overflow.test.js +65 -0
  28. package/test/https/custom-https-server.test.js +2 -2
  29. package/test/inject.test.js +30 -0
  30. package/test/internals/reply.test.js +2 -2
  31. package/test/internals/request.test.js +60 -0
  32. package/test/router-options.test.js +221 -0
  33. package/test/types/fastify.test-d.ts +7 -1
  34. package/test/types/instance.test-d.ts +70 -0
  35. package/test/types/reply.test-d.ts +37 -4
  36. package/test/types/request.test-d.ts +1 -0
  37. package/test/types/route.test-d.ts +79 -3
  38. package/test/url-rewriting.test.js +35 -0
  39. package/test/versioned-routes.test.js +1 -1
  40. package/types/context.d.ts +2 -1
  41. package/types/instance.d.ts +24 -20
  42. package/types/reply.d.ts +11 -6
  43. package/types/request.d.ts +1 -0
  44. package/types/route.d.ts +5 -0
  45. package/types/utils.d.ts +24 -0
@@ -248,6 +248,8 @@ section.
248
248
  development servers that require Babel transformations of JavaScript sources.
249
249
  - [`fastify-bcrypt`](https://github.com/beliven-it/fastify-bcrypt) A Bcrypt hash
250
250
  generator & checker.
251
+ - [`fastify-better-sqlite3`](https://github.com/punkish/fastify-better-sqlite3)
252
+ Plugin for better-sqlite3.
251
253
  - [`fastify-blipp`](https://github.com/PavelPolyakov/fastify-blipp) Prints your
252
254
  routes to the console, so you definitely know which endpoints are available.
253
255
  - [`fastify-bookshelf`](https://github.com/butlerx/fastify-bookshelfjs) Fastify
@@ -258,6 +260,9 @@ section.
258
260
  to add [bree](https://github.com/breejs/bree) support.
259
261
  - [`fastify-bugsnag`](https://github.com/ZigaStrgar/fastify-bugsnag) Fastify plugin
260
262
  to add support for [Bugsnag](https://www.bugsnag.com/) error reporting.
263
+ - [`fastify-cacheman`](https://gitlab.com/aalfiann/fastify-cacheman)
264
+ Small and efficient cache provider for Node.JS with In-memory, File, Redis
265
+ and MongoDB engines for Fastify
261
266
  - [`fastify-casbin`](https://github.com/nearform/fastify-casbin) Casbin support
262
267
  for Fastify.
263
268
  - [`fastify-casbin-rest`](https://github.com/nearform/fastify-casbin-rest)
@@ -28,6 +28,7 @@ snippet of code.
28
28
  - [Google Cloud Functions](#google-cloud-functions)
29
29
  - [Google Cloud Run](#google-cloud-run)
30
30
  - [Netlify Lambda](#netlify-lambda)
31
+ - [Platformatic Cloud](#platformatic-cloud)
31
32
  - [Vercel](#vercel)
32
33
 
33
34
  ## AWS
@@ -453,6 +454,47 @@ Add this command to your `package.json` *scripts*
453
454
 
454
455
  Then it should work fine
455
456
 
457
+ ## Platformatic Cloud
458
+
459
+ [Platformatic](https://platformatic.dev) provides zero-configuration deployment
460
+ for Node.js applications.
461
+ To use it now, you should wrap your existing Fastify application inside a
462
+ [Platformatic Service](https://oss.platformatic.dev/docs/reference/service/introduction),
463
+ by running the following:
464
+
465
+
466
+ ```bash
467
+ npm create platformatic@latest -- service
468
+ ```
469
+
470
+ The wizard would ask you to fill in a few answers:
471
+
472
+ ```
473
+ ? Where would you like to create your project? .
474
+ ? Do you want to run npm install? yes
475
+ ? Do you want to use TypeScript? no
476
+ ? What port do you want to use? 3042
477
+ [13:04:14] INFO: Configuration file platformatic.service.json successfully created.
478
+ [13:04:14] INFO: Environment file .env successfully created.
479
+ [13:04:14] INFO: Plugins folder "plugins" successfully created.
480
+ [13:04:14] INFO: Routes folder "routes" successfully created.
481
+ ? Do you want to create the github action to deploy this application to Platformatic Cloud dynamic workspace? no
482
+ ? Do you want to create the github action to deploy this application to Platformatic Cloud static workspace? no
483
+ ```
484
+
485
+ Then, head to [Platformatic Cloud](https://platformatic.cloud) and sign in
486
+ with your GitHub account.
487
+ Create your first application and a static workspace: be careful to download the
488
+ API key as an env file, e.g. `yourworkspace.txt`.
489
+
490
+ Then, you can easily deploy your application with the following commmand:
491
+
492
+ ```bash
493
+ platformatic deploy --keys `yuourworkspace.txt`
494
+ ```
495
+
496
+ Check out the [Full Guide](https://blog.platformatic.dev/how-to-migrate-a-fastify-app-to-platformatic-service)
497
+ on how to wrap Fastify application in Platformatic.
456
498
 
457
499
  ## Vercel
458
500
 
@@ -506,4 +548,4 @@ async function routes (fastify, options) {
506
548
  }
507
549
 
508
550
  export default routes;
509
- ```
551
+ ```
@@ -1,12 +1,15 @@
1
- <h1 align="center">Fastify</h1>
1
+ <h1 style="text-align: center;">Fastify</h1>
2
2
 
3
- ## Testing
3
+ # Testing
4
+ <a id="testing"></a>
4
5
 
5
6
  Testing is one of the most important parts of developing an application. Fastify
6
7
  is very flexible when it comes to testing and is compatible with most testing
7
8
  frameworks (such as [Tap](https://www.npmjs.com/package/tap), which is used in
8
9
  the examples below).
9
10
 
11
+ ## Application
12
+
10
13
  Let's `cd` into a fresh directory called 'testing-example' and type `npm init
11
14
  -y` in our terminal.
12
15
 
@@ -345,3 +348,133 @@ test('should ...', {only: true}, t => ...)
345
348
 
346
349
  Now you should be able to step through your test file (and the rest of
347
350
  `Fastify`) in your code editor.
351
+
352
+
353
+
354
+ ## Plugins
355
+ Let's `cd` into a fresh directory called 'testing-plugin-example' and type `npm init
356
+ -y` in our terminal.
357
+
358
+ Run `npm i fastify fastify-plugin && npm i tap -D`
359
+
360
+ **plugin/myFirstPlugin.js**:
361
+
362
+ ```js
363
+ const fP = require("fastify-plugin")
364
+
365
+ async function myPlugin(fastify, options) {
366
+ fastify.decorateRequest("helloRequest", "Hello World")
367
+ fastify.decorate("helloInstance", "Hello Fastify Instance")
368
+ }
369
+
370
+ module.exports = fP(myPlugin)
371
+ ```
372
+
373
+ A basic example of a Plugin. See [Plugin Guide](./Plugins-Guide.md)
374
+
375
+ **test/myFirstPlugin.test.js**:
376
+
377
+ ```js
378
+ const Fastify = require("fastify");
379
+ const tap = require("tap");
380
+ const myPlugin = require("../plugin/myFirstPlugin");
381
+
382
+ tap.test("Test the Plugin Route", async t => {
383
+ // Create a mock fastify application to test the plugin
384
+ const fastify = Fastify()
385
+
386
+ fastify.register(myPlugin)
387
+
388
+ // Add an endpoint of your choice
389
+ fastify.get("/", async (request, reply) => {
390
+ return ({ message: request.helloRequest })
391
+ })
392
+
393
+ // Use fastify.inject to fake a HTTP Request
394
+ const fastifyResponse = await fastify.inject({
395
+ method: "GET",
396
+ url: "/"
397
+ })
398
+
399
+ console.log('status code: ', fastifyResponse.statusCode)
400
+ console.log('body: ', fastifyResponse.body)
401
+ })
402
+ ```
403
+ Learn more about [```fastify.inject()```](#benefits-of-using-fastifyinject).
404
+ Run the test file in your terminal `node test/myFirstPlugin.test.js`
405
+
406
+ ```sh
407
+ status code: 200
408
+ body: {"message":"Hello World"}
409
+ ```
410
+
411
+ Now we can replace our `console.log` calls with actual tests!
412
+
413
+ In your `package.json` change the "test" script to:
414
+
415
+ `"test": "tap --reporter=list --watch"`
416
+
417
+ Create the tap test for the endpoint.
418
+
419
+ **test/myFirstPlugin.test.js**:
420
+
421
+ ```js
422
+ const Fastify = require("fastify");
423
+ const tap = require("tap");
424
+ const myPlugin = require("../plugin/myFirstPlugin");
425
+
426
+ tap.test("Test the Plugin Route", async t => {
427
+ // Specifies the number of test
428
+ t.plan(2)
429
+
430
+ const fastify = Fastify()
431
+
432
+ fastify.register(myPlugin)
433
+
434
+ fastify.get("/", async (request, reply) => {
435
+ return ({ message: request.helloRequest })
436
+ })
437
+
438
+ const fastifyResponse = await fastify.inject({
439
+ method: "GET",
440
+ url: "/"
441
+ })
442
+
443
+ t.equal(fastifyResponse.statusCode, 200)
444
+ t.same(JSON.parse(fastifyResponse.body), { message: "Hello World" })
445
+ })
446
+ ```
447
+
448
+ Finally, run `npm test` in the terminal and see your test results!
449
+
450
+ Test the ```.decorate()``` and ```.decorateRequest()```.
451
+
452
+ **test/myFirstPlugin.test.js**:
453
+
454
+ ```js
455
+ const Fastify = require("fastify");
456
+ const tap = require("tap");
457
+ const myPlugin = require("../plugin/myFirstPlugin");
458
+
459
+ tap.test("Test the Plugin Route", async t => {
460
+ t.plan(5)
461
+ const fastify = Fastify()
462
+
463
+ fastify.register(myPlugin)
464
+
465
+ fastify.get("/", async (request, reply) => {
466
+ // Testing the fastify decorators
467
+ t.not(request.helloRequest, null)
468
+ t.ok(request.helloRequest, "Hello World")
469
+ t.ok(fastify.helloInstance, "Hello Fastify Instance")
470
+ return ({ message: request.helloRequest })
471
+ })
472
+
473
+ const fastifyResponse = await fastify.inject({
474
+ method: "GET",
475
+ url: "/"
476
+ })
477
+ t.equal(fastifyResponse.statusCode, 200)
478
+ t.same(JSON.parse(fastifyResponse.body), { message: "Hello World" })
479
+ })
480
+ ```
@@ -1,4 +1,4 @@
1
- <h1 align="center">Fastify</h1>
1
+ <h1 style="text-align: center;">Fastify</h1>
2
2
 
3
3
  # How to write a good plugin
4
4
  First, thank you for deciding to write a plugin for Fastify. Fastify is a
@@ -63,6 +63,8 @@ among different versions of its dependencies.
63
63
  We do not enforce any testing library. We use [`tap`](https://www.node-tap.org/)
64
64
  since it offers out-of-the-box parallel testing and code coverage, but it is up
65
65
  to you to choose your library of preference.
66
+ We highly recommend you read the [Plugin Testing](./Testing.md#plugins) to
67
+ learn about how to test your plugins.
66
68
 
67
69
  ## Code Linter
68
70
  It is not mandatory, but we highly recommend you use a code linter in your
@@ -0,0 +1,78 @@
1
+ # Technical Principles
2
+
3
+ Every decision in the Fastify framework and its official plugins is guided by
4
+ the following technical principles:
5
+
6
+ 1. “Zero” overhead in production
7
+ 2. “Good” developer experience
8
+ 3. Works great for small & big projects alike
9
+ 4. Easy to migrate to microservices (or even serverless) and back
10
+ 5. Security & data validation
11
+ 6. If something could be a plugin, it likely should be
12
+ 7. Easily testable
13
+ 8. Do not monkeypatch core
14
+ 9. Semantic versioning & Long Term Support
15
+ 10. Specification adherence
16
+
17
+ ## "Zero" Overhead in Production
18
+
19
+ Fastify aims to implement its features by adding as minimal overhead to your
20
+ application as possible.
21
+ This is usually delivered by implementing fast algorithms and data structures,
22
+ as well as JavaScript-specific features.
23
+
24
+ Given that JavaScript does not offer zero-overhead data structures, this principle
25
+ is at odds with providing a great developer experience and providing more features,
26
+ as usually those cost some overhead.
27
+
28
+ ## "Good" Developer Experience
29
+
30
+ Fastify aims to provide the best developer experience at the performance point
31
+ it is operating.
32
+ It provides a great out-of-the-box experience that is flexible enough to be
33
+ adapted to a variety of situations.
34
+
35
+ As an example, this means that binary addons are forbidden because most JavaScript
36
+ developers would not
37
+ have access to a compiler.
38
+
39
+ ## Works great for small and big projects alike
40
+
41
+ We recognize that most applications start small and become more complex over time.
42
+ Fastify aims to grow with
43
+ the complexity of your application, providing advanced features to structure
44
+ your codebase.
45
+
46
+ ## Easy to migrate to microservices (or even serverless) and back
47
+
48
+ How you deploy your routes should not matter. The framework should "just work".
49
+
50
+ ## Security and Data Validation
51
+
52
+ Your web framework is the first point of contact with untrusted data, and it
53
+ needs to act as the first line of defense for your system.
54
+
55
+ ## If something could be a plugin, it likely should
56
+
57
+ We recognize that there are an infinite amount of use cases for an HTTP framework
58
+ for Node.js. Catering to them in a single module would make the codebase unmaintainable.
59
+ Therefore we provide hooks and options to allow you to customize the framework
60
+ as you please.
61
+
62
+ ## Easily testable
63
+
64
+ Testing Fastify applications should be a first-class concern.
65
+
66
+ ## Do not monkeypatch core
67
+
68
+ Moonkeypatch Node.js APIs or installing globals that alter the behavior of the
69
+ runtime makes building modular applications harder, and limit the use cases of Fastify.
70
+ Other frameworks do this and we do not.
71
+
72
+ ## Semantic Versioning and Long Term Support
73
+
74
+ We provide a clear Long Term Support strategy so developers can know when to upgrade.
75
+
76
+ ## Specification adherence
77
+
78
+ In doubt, we chose the strict behavior as defined by the relevant Specifications.
@@ -27,6 +27,8 @@ Request is a core Fastify object containing the following fields:
27
27
  - `protocol` - the protocol of the incoming request (`https` or `http`)
28
28
  - `method` - the method of the incoming request
29
29
  - `url` - the URL of the incoming request
30
+ - `originalUrl` - similar to `url`, this allows you to access the
31
+ original `url` in case of internal re-routing
30
32
  - `routerMethod` - the method defined for the router that is handling the
31
33
  request
32
34
  - `routerPath` - the path pattern defined for the router that is handling the
@@ -112,6 +112,11 @@ fastify.route(options)
112
112
  * `config`: object used to store custom configuration.
113
113
  * `version`: a [semver](https://semver.org/) compatible string that defined the
114
114
  version of the endpoint. [Example](#version-constraints).
115
+ * `constraints`: defines route restrictions based on request properties or
116
+ values, enabling customized matching using
117
+ [find-my-way](https://github.com/delvedor/find-my-way) constraints. Includes
118
+ built-in `version` and `host` constraints, with support for custom constraint
119
+ strategies.
115
120
  * `prefixTrailingSlash`: string used to determine how to handle passing `/` as a
116
121
  route with a prefix.
117
122
  * `both` (default): Will register both `/prefix` and `/prefix/`.
@@ -527,8 +527,8 @@ Defines the label used for the request identifier when logging the request.
527
527
  ### `genReqId`
528
528
  <a id="factory-gen-request-id"></a>
529
529
 
530
- Function for generating the request-id. It will receive the incoming request as
531
- a parameter. This function is expected to be error-free.
530
+ Function for generating the request-id. It will receive the _raw_ incoming
531
+ request as a parameter. This function is expected to be error-free.
532
532
 
533
533
  + Default: `value of 'request-id' header if provided or monotonically increasing
534
534
  integers`
@@ -856,6 +856,9 @@ is an instance-wide configuration.
856
856
  [server](https://nodejs.org/api/http.html#http_class_http_server) object as
857
857
  returned by the [**`Fastify factory function`**](#factory).
858
858
 
859
+ >__Warning__: If utilized improperly, certain Fastify features could be disrupted.
860
+ >It is recommended to only use it for attaching listeners.
861
+
859
862
  #### after
860
863
  <a id="after"></a>
861
864
 
@@ -11,15 +11,16 @@ keep associated types for each schema defined in your project.
11
11
 
12
12
  Type Providers are offered as additional packages you will need to install into
13
13
  your project. Each provider uses a different inference library under the hood;
14
- allowing you to select the library most appropriate for your needs. Type
14
+ allowing you to select the library most appropriate for your needs. Official Type
15
15
  Provider packages follow a `@fastify/type-provider-{provider-name}` naming
16
- convention.
16
+ convention, and there are several community ones available as well.
17
17
 
18
18
  The following inference packages are supported:
19
19
 
20
20
  - `json-schema-to-ts` -
21
21
  [github](https://github.com/ThomasAribart/json-schema-to-ts)
22
22
  - `typebox` - [github](https://github.com/sinclairzx81/typebox)
23
+ - `zod` - [github](https://github.com/colinhacks/zod)
23
24
 
24
25
  ### Json Schema to Ts
25
26
 
@@ -91,6 +92,12 @@ See also the [TypeBox
91
92
  documentation](https://github.com/sinclairzx81/typebox#validation) on how to set
92
93
  up AJV to work with TypeBox.
93
94
 
95
+ ### Zod
96
+
97
+ See [official documentation](https://github.com/turkerdev/fastify-type-provider-zod)
98
+ for Zod type provider instructions.
99
+
100
+
94
101
  ### Scoped Type-Provider
95
102
 
96
103
  The provider types don't propagate globally. In encapsulated usage, one can
@@ -111,7 +111,7 @@ generic types for route schemas and the dynamic properties located on the
111
111
  route-level `request` object.
112
112
 
113
113
  1. If you did not complete the previous example, follow steps 1-4 to get set up.
114
- 2. Inside `index.ts`, define two interfaces `IQuerystring` and `IHeaders`:
114
+ 2. Inside `index.ts`, define three interfaces `IQuerystring`,`IHeaders` and `IReply`:
115
115
  ```typescript
116
116
  interface IQuerystring {
117
117
  username: string;
@@ -121,8 +121,14 @@ route-level `request` object.
121
121
  interface IHeaders {
122
122
  'h-Custom': string;
123
123
  }
124
+
125
+ interface IReply {
126
+ 200: { success: boolean };
127
+ 302: { url: string };
128
+ '4xx': { error: string };
129
+ }
124
130
  ```
125
- 3. Using the two interfaces, define a new API route and pass them as generics.
131
+ 3. Using the three interfaces, define a new API route and pass them as generics.
126
132
  The shorthand route methods (i.e. `.get`) accept a generic object
127
133
  `RouteGenericInterface` containing five named properties: `Body`,
128
134
  `Querystring`, `Params`, `Headers` and `Reply`. The interfaces `Body`,
@@ -132,12 +138,20 @@ route-level `request` object.
132
138
  ```typescript
133
139
  server.get<{
134
140
  Querystring: IQuerystring,
135
- Headers: IHeaders
141
+ Headers: IHeaders,
142
+ Reply: IReply
136
143
  }>('/auth', async (request, reply) => {
137
144
  const { username, password } = request.query
138
145
  const customerHeader = request.headers['h-Custom']
139
146
  // do something with request data
140
147
 
148
+ // chaining .statusCode/.code calls with .send allows type narrowing. For example:
149
+ // this works
150
+ reply.code(200).send({ success: true });
151
+ // but this gives a type error
152
+ reply.code(200).send('uh-oh');
153
+ // it even works for wildcards
154
+ reply.code(404).send({ error: 'Not found' });
141
155
  return `logged in!`
142
156
  })
143
157
  ```
@@ -154,7 +168,8 @@ route-level `request` object.
154
168
  ```typescript
155
169
  server.get<{
156
170
  Querystring: IQuerystring,
157
- Headers: IHeaders
171
+ Headers: IHeaders,
172
+ Reply: IReply
158
173
  }>('/auth', {
159
174
  preValidation: (request, reply, done) => {
160
175
  const { username, password } = request.query
@@ -172,7 +187,7 @@ route-level `request` object.
172
187
  admin"}`
173
188
 
174
189
  🎉 Good work, now you can define interfaces for each route and have strictly
175
- typed request and reply instances. Other parts of the Fastify type system rely
190
+ typed request and reply instances. Other parts of the Fastify type system rely
176
191
  on generic properties. Make sure to reference the detailed type system
177
192
  documentation below to learn more about what is available.
178
193
 
@@ -493,7 +508,7 @@ Fastify Plugin in a TypeScript Project.
493
508
  `"compilerOptions"` object.
494
509
  ```json
495
510
  {
496
- "compileOptions": {
511
+ "compilerOptions": {
497
512
  "declaration": true
498
513
  }
499
514
  }
package/fastify.d.ts CHANGED
@@ -109,7 +109,7 @@ declare namespace fastify {
109
109
  requestIdHeader?: string | false,
110
110
  requestIdLogLabel?: string;
111
111
  jsonShorthand?: boolean;
112
- genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>) => string,
112
+ genReqId?: (req: RawRequestDefaultExpression<RawServer>) => string,
113
113
  trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
114
114
  querystringParser?: (str: string) => { [key: string]: unknown },
115
115
  /**
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.18.0'
3
+ const VERSION = '4.19.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -426,10 +426,13 @@ function fastify (options) {
426
426
  router.closeRoutes()
427
427
 
428
428
  hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
429
- if (fastify[kState].listening) {
430
- // No new TCP connections are accepted
431
- instance.server.close(done)
429
+ // No new TCP connections are accepted.
430
+ // We must call close on the server even if we are not listening
431
+ // otherwise memory will be leaked.
432
+ // https://github.com/nodejs/node/issues/48604
433
+ instance.server.close(done)
432
434
 
435
+ if (fastify[kState].listening) {
433
436
  /* istanbul ignore next: Cannot test this without Node.js core support */
434
437
  if (forceCloseConnections === 'idle') {
435
438
  // Not needed in Node 19
@@ -653,6 +656,11 @@ function fastify (options) {
653
656
  errorStatus = http.STATUS_CODES[errorCode]
654
657
  body = `{"error":"${errorStatus}","message":"Client Timeout","statusCode":408}`
655
658
  errorLabel = 'timeout'
659
+ } else if (err.code === 'HPE_HEADER_OVERFLOW') {
660
+ errorCode = '431'
661
+ errorStatus = http.STATUS_CODES[errorCode]
662
+ body = `{"error":"${errorStatus}","message":"Exceeded maximum allowed HTTP header size","statusCode":431}`
663
+ errorLabel = 'header_overflow'
656
664
  } else {
657
665
  errorCode = '400'
658
666
  errorStatus = http.STATUS_CODES[errorCode]
@@ -692,10 +700,13 @@ function fastify (options) {
692
700
  const id = genReqId(req)
693
701
  const childLogger = logger.child({ reqId: id })
694
702
 
695
- childLogger.info({ req }, 'incoming request')
696
-
697
703
  const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
698
704
  const reply = new Reply(res, request, childLogger)
705
+
706
+ if (disableRequestLogging === false) {
707
+ childLogger.info({ req: request }, 'incoming request')
708
+ }
709
+
699
710
  return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
700
711
  }
701
712
  const body = `{"error":"Bad Request","code":"FST_ERR_BAD_URL","message":"'${path}' is not a valid url component","statusCode":400}`
@@ -714,10 +725,13 @@ function fastify (options) {
714
725
  const id = genReqId(req)
715
726
  const childLogger = logger.child({ reqId: id })
716
727
 
717
- childLogger.info({ req }, 'incoming request')
718
-
719
728
  const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
720
729
  const reply = new Reply(res, request, childLogger)
730
+
731
+ if (disableRequestLogging === false) {
732
+ childLogger.info({ req: request }, 'incoming request')
733
+ }
734
+
721
735
  return frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)
722
736
  }
723
737
  const body = '{"error":"Internal Server Error","message":"Unexpected error from async constraint","statusCode":500}'
@@ -793,6 +807,7 @@ function fastify (options) {
793
807
  // only call isAsyncConstraint once
794
808
  if (isAsync === undefined) isAsync = router.isAsyncConstraint()
795
809
  if (rewriteUrl) {
810
+ req.originalUrl = req.url
796
811
  const url = rewriteUrl.call(fastify, req)
797
812
  if (typeof url === 'string') {
798
813
  req.url = url
@@ -281,7 +281,7 @@ function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
281
281
  return defaultJsonParser
282
282
 
283
283
  function defaultJsonParser (req, body, done) {
284
- if (body === '' || body == null) {
284
+ if (body === '' || body == null || (Buffer.isBuffer(body) && body.length === 0)) {
285
285
  return done(new FST_ERR_CTP_EMPTY_JSON_BODY(), undefined)
286
286
  }
287
287
  let json
package/lib/reply.js CHANGED
@@ -357,7 +357,7 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
357
357
  })
358
358
 
359
359
  // We create a WeakMap to compile the schema only once
360
- // Its done leazily to avoid add overhead by creating the WeakMap
360
+ // Its done lazily to avoid add overhead by creating the WeakMap
361
361
  // if it is not used
362
362
  // TODO: Explore a central cache for all the schemas shared across
363
363
  // encapsulated contexts
@@ -859,7 +859,7 @@ function notFound (reply) {
859
859
  * @param {object} context the request context
860
860
  * @param {object} data the JSON payload to serialize
861
861
  * @param {number} statusCode the http status code
862
- * @param {string} contentType the reply content type
862
+ * @param {string} [contentType] the reply content type
863
863
  * @returns {string} the serialized payload
864
864
  */
865
865
  function serialize (context, data, statusCode, contentType) {
package/lib/request.js CHANGED
@@ -13,7 +13,8 @@ const {
13
13
  kOptions,
14
14
  kRequestCacheValidateFns,
15
15
  kRouteContext,
16
- kPublicRouteContext
16
+ kPublicRouteContext,
17
+ kRequestOriginalUrl
17
18
  } = require('./symbols')
18
19
  const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION } = require('./errors')
19
20
 
@@ -149,6 +150,15 @@ Object.defineProperties(Request.prototype, {
149
150
  return this.raw.url
150
151
  }
151
152
  },
153
+ originalUrl: {
154
+ get () {
155
+ /* istanbul ignore else */
156
+ if (!this[kRequestOriginalUrl]) {
157
+ this[kRequestOriginalUrl] = this.raw.originalUrl || this.raw.url
158
+ }
159
+ return this[kRequestOriginalUrl]
160
+ }
161
+ },
152
162
  method: {
153
163
  get () {
154
164
  return this.raw.method
@@ -162,7 +172,7 @@ Object.defineProperties(Request.prototype, {
162
172
  },
163
173
  routerPath: {
164
174
  get () {
165
- return this[kRouteContext].config.url
175
+ return this[kRouteContext].config?.url
166
176
  }
167
177
  },
168
178
  routeOptions: {
@@ -172,8 +182,8 @@ Object.defineProperties(Request.prototype, {
172
182
  const serverLimit = context.server.initialConfig.bodyLimit
173
183
  const version = context.server.hasConstraintStrategy('version') ? this.raw.headers['accept-version'] : undefined
174
184
  const options = {
175
- method: context.config.method,
176
- url: context.config.url,
185
+ method: context.config?.method,
186
+ url: context.config?.url,
177
187
  bodyLimit: (routeLimit || serverLimit),
178
188
  attachValidation: context.attachValidation,
179
189
  logLevel: context.logLevel,
@@ -186,12 +196,12 @@ Object.defineProperties(Request.prototype, {
186
196
  },
187
197
  routerMethod: {
188
198
  get () {
189
- return this[kRouteContext].config.method
199
+ return this[kRouteContext].config?.method
190
200
  }
191
201
  },
192
202
  routeConfig: {
193
203
  get () {
194
- return this[kRouteContext][kPublicRouteContext].config
204
+ return this[kRouteContext][kPublicRouteContext]?.config
195
205
  }
196
206
  },
197
207
  routeSchema: {
@@ -201,7 +211,7 @@ Object.defineProperties(Request.prototype, {
201
211
  },
202
212
  is404: {
203
213
  get () {
204
- return this[kRouteContext].config.url === undefined
214
+ return this[kRouteContext].config?.url === undefined
205
215
  }
206
216
  },
207
217
  connection: {
@@ -283,7 +293,7 @@ Object.defineProperties(Request.prototype, {
283
293
  })
284
294
 
285
295
  // We create a WeakMap to compile the schema only once
286
- // Its done leazily to avoid add overhead by creating the WeakMap
296
+ // Its done lazily to avoid add overhead by creating the WeakMap
287
297
  // if it is not used
288
298
  // TODO: Explore a central cache for all the schemas shared across
289
299
  // encapsulated contexts
package/lib/route.js CHANGED
@@ -468,7 +468,6 @@ function buildRouting (options) {
468
468
 
469
469
  const request = new context.Request(id, params, req, query, childLogger, context)
470
470
  const reply = new context.Reply(res, request, childLogger)
471
-
472
471
  if (disableRequestLogging === false) {
473
472
  childLogger.info({ req: request }, 'incoming request')
474
473
  }