fastify 4.15.0 → 4.16.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 (42) hide show
  1. package/README.md +3 -3
  2. package/docs/Guides/Database.md +7 -8
  3. package/docs/Guides/Ecosystem.md +16 -7
  4. package/docs/Guides/Getting-Started.md +1 -1
  5. package/docs/Guides/Migration-Guide-V4.md +21 -0
  6. package/docs/Guides/Plugins-Guide.md +1 -1
  7. package/docs/Guides/Prototype-Poisoning.md +31 -39
  8. package/docs/Guides/Recommendations.md +1 -1
  9. package/docs/Guides/Write-Type-Provider.md +3 -3
  10. package/docs/Reference/Hooks.md +42 -9
  11. package/docs/Reference/Reply.md +2 -2
  12. package/docs/Reference/Routes.md +13 -2
  13. package/docs/Reference/Server.md +19 -3
  14. package/docs/Reference/Type-Providers.md +1 -1
  15. package/docs/Reference/TypeScript.md +3 -3
  16. package/docs/index.md +2 -2
  17. package/examples/benchmark/parser.js +47 -0
  18. package/fastify.js +26 -23
  19. package/lib/error-serializer.js +9 -162
  20. package/lib/hooks.js +3 -0
  21. package/lib/server.js +9 -4
  22. package/lib/validation.js +10 -8
  23. package/lib/warnings.js +2 -0
  24. package/package.json +8 -7
  25. package/test/close.test.js +91 -0
  26. package/test/route-hooks.test.js +29 -0
  27. package/test/route.test.js +1 -1
  28. package/test/schema-feature.test.js +128 -35
  29. package/test/serial/logger.0.test.js +861 -0
  30. package/test/serial/logger.1.test.js +862 -0
  31. package/test/serial/tap-parallel-not-ok +0 -0
  32. package/test/server.test.js +10 -0
  33. package/test/types/hooks.test-d.ts +66 -11
  34. package/test/types/import.js +1 -1
  35. package/test/types/instance.test-d.ts +2 -0
  36. package/test/types/route.test-d.ts +106 -5
  37. package/test/types/type-provider.test-d.ts +77 -10
  38. package/types/hooks.d.ts +28 -0
  39. package/types/instance.d.ts +20 -1
  40. package/types/logger.d.ts +1 -1
  41. package/types/route.d.ts +41 -11
  42. package/test/logger.test.js +0 -1721
package/README.md CHANGED
@@ -9,11 +9,11 @@
9
9
 
10
10
  <div align="center">
11
11
 
12
- [![CI](https://github.com/fastify/fastify/workflows/ci/badge.svg)](https://github.com/fastify/fastify/actions/workflows/ci.yml)
12
+ [![CI](https://github.com/fastify/fastify/workflows/ci/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/ci.yml)
13
13
  [![Package Manager
14
- CI](https://github.com/fastify/fastify/workflows/package-manager-ci/badge.svg)](https://github.com/fastify/fastify/actions/workflows/package-manager-ci.yml)
14
+ CI](https://github.com/fastify/fastify/workflows/package-manager-ci/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/package-manager-ci.yml)
15
15
  [![Web
16
- SIte](https://github.com/fastify/fastify/workflows/website/badge.svg)](https://github.com/fastify/fastify/actions/workflows/website.yml)
16
+ SIte](https://github.com/fastify/fastify/workflows/website/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/website.yml)
17
17
  [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
18
18
 
19
19
  </div>
@@ -130,19 +130,18 @@ fastify.register(require('@fastify/mongodb'), {
130
130
  url: 'mongodb://mongo/mydb'
131
131
  })
132
132
 
133
- fastify.get('/user/:id', function (req, reply) {
133
+ fastify.get('/user/:id', async function (req, reply) {
134
134
  // Or this.mongo.client.db('mydb').collection('users')
135
135
  const users = this.mongo.db.collection('users')
136
136
 
137
137
  // if the id is an ObjectId format, you need to create a new ObjectId
138
138
  const id = this.mongo.ObjectId(req.params.id)
139
- users.findOne({ id }, (err, user) => {
140
- if (err) {
141
- reply.send(err)
142
- return
143
- }
144
- reply.send(user)
145
- })
139
+ try {
140
+ const user = await users.findOne({ id })
141
+ return user
142
+ } catch (err) {
143
+ return err
144
+ }
146
145
  })
147
146
 
148
147
  fastify.listen({ port: 3000 }, err => {
@@ -11,7 +11,7 @@ section.
11
11
  - [`@fastify/accepts`](https://github.com/fastify/fastify-accepts) to have
12
12
  [accepts](https://www.npmjs.com/package/accepts) in your request object.
13
13
  - [`@fastify/accepts-serializer`](https://github.com/fastify/fastify-accepts-serializer)
14
- to serialize to output according to `Accept` header.
14
+ to serialize to output according to the `Accept` header.
15
15
  - [`@fastify/any-schema`](https://github.com/fastify/any-schema-you-like) Save
16
16
  multiple schemas and decide which one to use to serialize the payload
17
17
  - [`@fastify/auth`](https://github.com/fastify/fastify-auth) Run multiple auth
@@ -151,7 +151,7 @@ section.
151
151
  - [`@eropple/fastify-openapi3`](https://github.com/eropple/fastify-openapi3) Provides
152
152
  easy, developer-friendly OpenAPI 3.1 specs + doc explorer based on your routes.
153
153
  - [`@ethicdevs/fastify-custom-session`](https://github.com/EthicDevs/fastify-custom-session)
154
- A plugin that let you use session and decide only where to load/save from/to. Has
154
+ A plugin lets you use session and decide only where to load/save from/to. Has
155
155
  great TypeScript support + built-in adapters for common ORMs/databases (Firebase,
156
156
  Prisma Client, Postgres (wip), InMemory) and you can easily make your own adapter!
157
157
  - [`@ethicdevs/fastify-git-server`](https://github.com/EthicDevs/fastify-git-server)
@@ -174,7 +174,6 @@ section.
174
174
  - [`@immobiliarelabs/fastify-sentry`](https://github.com/immobiliare/fastify-sentry)
175
175
  Sentry errors handler that just works! Install, add your DSN and you're good
176
176
  to go!
177
- - [`@mateonunez/fastify-lyra`](https://github.com/mateonunez/fastify-lyra)
178
177
  A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
179
178
  on Fastify
180
179
  - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
@@ -391,9 +390,11 @@ section.
391
390
  - [`fastify-keycloak-adapter`](https://github.com/yubinTW/fastify-keycloak-adapter)
392
391
  A keycloak adapter for a Fastify app.
393
392
  - [`fastify-knexjs`](https://github.com/chapuletta/fastify-knexjs) Fastify
394
- plugin for support KnexJS Query Builder.
393
+ plugin for supporting KnexJS Query Builder.
395
394
  - [`fastify-knexjs-mock`](https://github.com/chapuletta/fastify-knexjs-mock)
396
395
  Fastify Mock KnexJS for testing support.
396
+ - [`fastify-koa`](https://github.com/rozzilla/fastify-koa) Convert Koa
397
+ middlewares into Fastify plugins
397
398
  - [`fastify-kubernetes`](https://github.com/greguz/fastify-kubernetes) Fastify
398
399
  Kubernetes client plugin.
399
400
  - [`fastify-language-parser`](https://github.com/lependu/fastify-language-parser)
@@ -401,13 +402,14 @@ section.
401
402
  - [`fastify-lcache`](https://github.com/denbon05/fastify-lcache)
402
403
  Lightweight cache plugin
403
404
  - [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
404
- A simple plugin for Fastify list all available routes.
405
+ A simple plugin for Fastify to list all available routes.
405
406
  - [`fastify-loader`](https://github.com/TheNoim/fastify-loader) Load routes from
406
407
  a directory and inject the Fastify instance in each file.
408
+ - [`fastify-log-controller`](https://github.com/Eomm/fastify-log-controller/)
409
+ changes the log level of your Fastify server at runtime.
407
410
  - [`fastify-lured`](https://github.com/lependu/fastify-lured) Plugin to load lua
408
411
  scripts with [fastify-redis](https://github.com/fastify/fastify-redis) and
409
412
  [lured](https://github.com/enobufs/lured).
410
- - [`fastify-lyra`](https://github.com/mateonunez/fastify-lyra)
411
413
  A plugin to implement [Lyra](https://github.com/LyraSearch/lyra) search engine
412
414
  on Fastify.
413
415
  - [`fastify-mailer`](https://github.com/coopflow/fastify-mailer) Plugin to
@@ -475,6 +477,7 @@ section.
475
477
  - [`fastify-oracle`](https://github.com/cemremengu/fastify-oracle) Attaches an
476
478
  [`oracledb`](https://github.com/oracle/node-oracledb) connection pool to a
477
479
  Fastify server instance.
480
+ - [`fastify-orama`](https://github.com/mateonunez/fastify-orama)
478
481
  - [`fastify-orientdb`](https://github.com/mahmed8003/fastify-orientdb) Fastify
479
482
  OrientDB connection plugin, with which you can share the OrientDB connection
480
483
  across every part of your server.
@@ -518,6 +521,8 @@ section.
518
521
  - [`fastify-redis-channels`](https://github.com/hearit-io/fastify-redis-channels)
519
522
  A plugin for fast, reliable, and scalable channels implementation based on
520
523
  Redis streams.
524
+ - [`fastify-redis-session`](https://github.com/mohammadraufzahed/fastify-redis-session)
525
+ Redis Session plugin for fastify.
521
526
  - [`fastify-register-routes`](https://github.com/israeleriston/fastify-register-routes)
522
527
  Plugin to automatically load routes from a specified path and optionally limit
523
528
  loaded file names by a regular expression.
@@ -586,6 +591,10 @@ section.
586
591
  TOTP (e.g. for 2FA).
587
592
  - [`fastify-twitch-ebs-tools`](https://github.com/lukemnet/fastify-twitch-ebs-tools)
588
593
  Useful functions for Twitch Extension Backend Services (EBS).
594
+ - [`fastify-type-provider-effect-schema`](https://github.com/daotl/fastify-type-provider-effect-schema)
595
+ Fastify
596
+ [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
597
+ for [@effect/schema](https://github.com/effect-ts/schema).
589
598
  - [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
590
599
  Fastify
591
600
  [type provider](https://www.fastify.io/docs/latest/Reference/Type-Providers/)
@@ -637,7 +646,7 @@ section.
637
646
  - [`openapi-validator-middleware`](https://github.com/PayU/openapi-validator-middleware#fastify)
638
647
  Swagger and OpenAPI 3.0 spec-based request validation middleware that supports
639
648
  Fastify.
640
- - [`pubsub-http-handler`](https://github.com/cobraz/pubsub-http-handler) A Fastify
649
+ - [`pubsub-http-handler`](https://github.com/simenandre/pubsub-http-handler) A Fastify
641
650
  plugin to easily create Google Cloud PubSub endpoints.
642
651
  - [`sequelize-fastify`](https://github.com/hsynlms/sequelize-fastify) A simple
643
652
  and lightweight Sequelize plugin for Fastify.
@@ -538,7 +538,7 @@ an amazing [ecosystem](./Ecosystem.md)!
538
538
  <a id="test-server"></a>
539
539
 
540
540
  Fastify does not offer a testing framework, but we do recommend a way to write
541
- your tests that uses the features and architecture of Fastify.
541
+ your tests that use the features and architecture of Fastify.
542
542
 
543
543
  Read the [testing](./Testing.md) documentation to learn more!
544
544
 
@@ -130,6 +130,27 @@ As a result, if you specify an `onRoute` hook in a plugin you should now either:
130
130
  });
131
131
  ```
132
132
 
133
+ ### Optional URL parameters
134
+
135
+ If you've already used any implicitly optional parameters, you'll get a 404
136
+ error when trying to access the route. You will now need to declare the
137
+ optional parameters explicitly.
138
+
139
+ For example, if you have the same route for listing and showing a post,
140
+ refactor this:
141
+ ```js
142
+ fastify.get('/posts/:id', (request, reply) => {
143
+ const { id } = request.params;
144
+ });
145
+ ```
146
+
147
+ Into this:
148
+ ```js
149
+ fastify.get('/posts/:id?', (request, reply) => {
150
+ const { id } = request.params;
151
+ });
152
+ ```
153
+
133
154
  ## Non-Breaking Changes
134
155
 
135
156
  ### Deprecation of variadic `.listen()` signature
@@ -427,7 +427,7 @@ variables that were injected by preceding plugins in the order of declaration.
427
427
 
428
428
  ESM is supported as well from [Node.js
429
429
  `v13.3.0`](https://nodejs.org/api/esm.html) and above! Just export your plugin
430
- as ESM module and you are good to go!
430
+ as an ESM module and you are good to go!
431
431
 
432
432
  ```js
433
433
  // plugin.mjs
@@ -4,29 +4,24 @@
4
4
  > but otherwise remains the same. The original HTML can be retrieved from the
5
5
  > above permission link.
6
6
 
7
- ## A Tale of (prototype) Poisoning
7
+ ## History behind prototype poisoning
8
8
  <a id="pp"></a>
9
9
 
10
- This story is a behind-the-scenes look at the process and drama created by a
11
- particularity interesting web security issue. It is also a perfect illustration
12
- of the efforts required to maintain popular pieces of open source software and
13
- the limitations of existing communication channels.
14
-
15
- But first, if you use a JavaScript framework to process incoming JSON data, take
16
- a moment to read up on [Prototype
17
- Poisoning](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)
18
- in general, and the specific [technical
19
- details](https://github.com/hapijs/hapi/issues/3916) of this issue. I'll explain
20
- it all in a bit, but since this could be a critical issue, you might want to
21
- verify your own code first. While this story is focused on a specific framework,
22
- any solution that uses `JSON.parse()` to process external data is potentially at
23
- risk.
10
+ Based on the article by Eran Hammer,the issue is created by a web security bug.
11
+ It is also a perfect illustration of the efforts required to maintain
12
+ open-source software and the limitations of existing communication channels.
13
+
14
+ But first, if we use a JavaScript framework to process incoming JSON data, take
15
+ a moment to read up on [Prototype Poisoning](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)
16
+ in general, and the specific [technical details]
17
+ (https://github.com/hapijs/hapi/issues/3916) of this issue.
18
+ This could be a critical issue so, we might need to verify your own code first.
19
+ It focuses on specific framework however, any solution that uses `JSON.parse()`
20
+ to process external data is potentially at risk.
24
21
 
25
22
  ### BOOM
26
23
  <a id="pp-boom"></a>
27
24
 
28
- Our story begins with a bang.
29
-
30
25
  The engineering team at Lob (long time generous supporters of my work!) reported
31
26
  a critical security vulnerability they identified in our data validation
32
27
  module — [joi](https://github.com/hapijs/joi). They provided some technical
@@ -34,21 +29,22 @@ details and a proposed solution.
34
29
 
35
30
  The main purpose of a data validation library is to ensure the output fully
36
31
  complies with the rules defined. If it doesn't, validation fails. If it passes,
37
- your can blindly trust that the data you are working with is safe. In fact, most
32
+ we can blindly trust that the data you are working with is safe. In fact, most
38
33
  developers treat validated input as completely safe from a system integrity
39
- perspective. This is crucial.
34
+ perspective which is crucial!
40
35
 
41
- In our case, the Lob team provided an example where some data was able to sneak
36
+ In our case, the Lob team provided an example where some data was able to escape
42
37
  by the validation logic and pass through undetected. This is the worst possible
43
38
  defect a validation library can have.
44
39
 
45
40
  ### Prototype in a nutshell
46
41
  <a id="pp-nutshell"></a>
47
42
 
48
- To understand this story, you need to understand how JavaScript works a bit.
43
+ To understand this, we need to understand how JavaScript works a bit.
49
44
  Every object in JavaScript can have a prototype. It is a set of methods and
50
- properties it "inherits" from another object. I put inherits in quotes because
51
- JavaScript isn't really an object oriented language.
45
+ properties it "inherits" from another object. I have put inherits in quotes
46
+ because JavaScript isn't really an object-oriented language.It is prototype-
47
+ based object-oriented language.
52
48
 
53
49
  A long time ago, for a bunch of irrelevant reasons, someone decided that it
54
50
  would be a good idea to use the special property name `__proto__` to access (and
@@ -68,22 +64,21 @@ To demonstrate:
68
64
  { b: 5 }
69
65
  ```
70
66
 
71
- As you can see, the object doesn't have a `c` property, but its prototype does.
67
+ The object doesn't have a `c` property, but its prototype does.
72
68
  When validating the object, the validation library ignores the prototype and
73
69
  only validates the object's own properties. This allows `c` to sneak in via the
74
70
  prototype.
75
71
 
76
- Another important part of this story is the way `JSON.parse()` — a utility
77
- provided by the language to convert JSON formatted text into objects  —  handles
78
- this magic `__proto__` property name.
72
+ Another important part is the way `JSON.parse()` — a utility
73
+ provided by the language to convert JSON formatted text into
74
+ objects  —  handles this magic `__proto__` property name.
79
75
 
80
76
  ```
81
- > const text = '{ "b": 5, "__proto__": { "c": 6 } }';
77
+ > const text = '{"b": 5, "__proto__": { "c": 6 }}';
82
78
  > const a = JSON.parse(text);
83
79
  > a;
84
- { b: 5, __proto__: { c: 6 } }
80
+ {b: 5, __proto__: { c: 6 }}
85
81
  ```
86
-
87
82
  Notice how `a` has a `__proto__` property. This is not a prototype reference. It
88
83
  is a simple object property key, just like `b`. As we've seen from the first
89
84
  example, we can't actually create this key through assignment as that invokes
@@ -111,17 +106,17 @@ level properties of `a` into the provided empty `{}` object), the magic
111
106
 
112
107
  Surprise!
113
108
 
114
- Put together, if you get some external text input, parse it with `JSON.parse()`
115
- then perform some simple manipulation of that object (say, shallow clone and add
116
- an `id` ), and then pass it to our validation library, anything passed through
117
- via `__proto__` would sneak in undetected.
109
+ If you get some external text input and parse it with `JSON.parse()`
110
+ then perform some simple manipulation of that object (e.g shallow clone and add
111
+ an `id` ), and pass it to our validation library, it would sneak in undetected
112
+ via `__proto__`.
118
113
 
119
114
  ### Oh joi!
120
115
  <a id="pp-oh-joi"></a>
121
116
 
122
117
  The first question is, of course, why does the validation module **joi** ignore
123
118
  the prototype and let potentially harmful data through? We asked ourselves the
124
- same question and our instant thought was "it was an oversight". A bug. A really
119
+ same question and our instant thought was "it was an oversight". A bug - a really
125
120
  big mistake. The joi module should not have allowed this to happen. But…
126
121
 
127
122
  While joi is used primarily for validating web input data, it also has a
@@ -166,7 +161,6 @@ will share with the world how to exploit this vulnerability while also making it
166
161
  more time consuming for systems to upgrade (breaking changes never get applied
167
162
  automatically by build tools).
168
163
 
169
- Lose — Lose.
170
164
 
171
165
  ### A detour
172
166
  <a id="pp-detour"></a>
@@ -386,6 +380,4 @@ plan](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-
386
380
  coming in March. You can read more about it
387
381
  [here](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898).
388
382
 
389
- Of all the time consuming things, security is at the very top. I hope this story
390
- successfully conveyed not just the technical details, but also the human drama and
391
- what it takes to keep the web secure.
383
+
@@ -326,7 +326,7 @@ frequently. Also, the main thread won't have to stop to let the GC run.
326
326
 
327
327
  * To optimize for throughput (handling the largest possible amount of
328
328
  requests per second per vCPU available), consider using a smaller amount of vCPUs
329
- per app instance. It is totally fine to run Node.js application with 1 vCPU.
329
+ per app instance. It is totally fine to run Node.js applications with 1 vCPU.
330
330
 
331
331
  * You may experiment with an even smaller amount of vCPU, which may provide
332
332
  even better throughput in certain use-cases. There are reports of API gateway
@@ -13,11 +13,11 @@ up to `unknown`.
13
13
  The reasoning is that certain methods of `FastifyInstance` are
14
14
  contravariant on `TypeProvider`, which can lead to TypeScript surfacing
15
15
  assignability issues unless the custom type provider interface is
16
- substitutible with `FastifyTypeProviderDefault`.
16
+ substitutable with `FastifyTypeProviderDefault`.
17
17
 
18
18
  For example, `FastifyTypeProviderDefault` will not be assignable to the following:
19
19
  ```ts
20
- export interface NotSubstitutibleTypeProvider extends FastifyTypeProvider {
20
+ export interface NotSubstitutableTypeProvider extends FastifyTypeProvider {
21
21
  // bad, nothing is assignable to `never` (except for itself)
22
22
  output: this['input'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
23
23
  }
@@ -25,7 +25,7 @@ export interface NotSubstitutibleTypeProvider extends FastifyTypeProvider {
25
25
 
26
26
  Unless changed to:
27
27
  ```ts
28
- export interface SubstitutibleTypeProvider extends FastifyTypeProvider {
28
+ export interface SubstitutableTypeProvider extends FastifyTypeProvider {
29
29
  // good, anything can be assigned to `unknown`
30
30
  output: this['input'] extends /** custom check here**/ ? /** narrowed type here **/ : unknown;
31
31
  }
@@ -25,6 +25,7 @@ are Request/Reply hooks and application hooks:
25
25
  - [Application Hooks](#application-hooks)
26
26
  - [onReady](#onready)
27
27
  - [onClose](#onclose)
28
+ - [preClose](#preclose)
28
29
  - [onRoute](#onroute)
29
30
  - [onRegister](#onregister)
30
31
  - [Scope](#scope)
@@ -266,19 +267,19 @@ fastify.addHook('onTimeout', async (request, reply) => {
266
267
  `onTimeout` is useful if you need to monitor the request timed out in your
267
268
  service (if the `connectionTimeout` property is set on the Fastify instance).
268
269
  The `onTimeout` hook is executed when a request is timed out and the HTTP socket
269
- has been hanged up. Therefore, you will not be able to send data to the client.
270
+ has been hung up. Therefore, you will not be able to send data to the client.
270
271
 
271
272
  ### onRequestAbort
272
273
 
273
274
  ```js
274
- fastify.addHook('onRequestAbort', (request, reply, done) => {
275
+ fastify.addHook('onRequestAbort', (request, done) => {
275
276
  // Some code
276
277
  done()
277
278
  })
278
279
  ```
279
280
  Or `async/await`:
280
281
  ```js
281
- fastify.addHook('onRequestAbort', async (request, reply) => {
282
+ fastify.addHook('onRequestAbort', async (request) => {
282
283
  // Some code
283
284
  await asyncMethod()
284
285
  })
@@ -385,6 +386,7 @@ You can hook into the application-lifecycle as well.
385
386
 
386
387
  - [onReady](#onready)
387
388
  - [onClose](#onclose)
389
+ - [preClose](#preclose)
388
390
  - [onRoute](#onroute)
389
391
  - [onRegister](#onregister)
390
392
 
@@ -414,9 +416,10 @@ fastify.addHook('onReady', async function () {
414
416
  ### onClose
415
417
  <a id="on-close"></a>
416
418
 
417
- Triggered when `fastify.close()` is invoked to stop the server. It is useful
418
- when [plugins](./Plugins.md) need a "shutdown" event, for example, to close an
419
- open connection to a database.
419
+ Triggered when `fastify.close()` is invoked to stop the server, after all in-flight
420
+ HTTP requests have been completed.
421
+ It is useful when [plugins](./Plugins.md) need a "shutdown" event, for example,
422
+ to close an open connection to a database.
420
423
 
421
424
  The hook function takes the Fastify instance as a first argument,
422
425
  and a `done` callback for synchronous hook functions.
@@ -434,10 +437,34 @@ fastify.addHook('onClose', async (instance) => {
434
437
  })
435
438
  ```
436
439
 
440
+ ### preClose
441
+ <a id="pre-close"></a>
442
+
443
+ Triggered when `fastify.close()` is invoked to stop the server, before all in-flight
444
+ HTTP requests have been completed.
445
+ It is useful when [plugins](./Plugins.md) have set up some state attached
446
+ to the HTTP server that would prevent the server to close.
447
+ _It is unlikely you will need to use this hook_,
448
+ use the [`onClose`](#onclose) for the most common case.
449
+
450
+ ```js
451
+ // callback style
452
+ fastify.addHook('preClose', (done) => {
453
+ // Some code
454
+ done()
455
+ })
456
+
457
+ // or async/await style
458
+ fastify.addHook('preClose', async () => {
459
+ // Some async code
460
+ await removeSomeServerState()
461
+ })
462
+ ```
463
+
437
464
  ### onRoute
438
465
  <a id="on-route"></a>
439
466
 
440
- Triggered when a new route is registered. Listeners are passed a `routeOptions`
467
+ Triggered when a new route is registered. Listeners are passed a [`routeOptions`](./Routes.md#routes-options)
441
468
  object as the sole parameter. The interface is synchronous, and, as such, the
442
469
  listeners are not passed a callback. This hook is encapsulated.
443
470
 
@@ -655,6 +682,12 @@ fastify.route({
655
682
  // This hook will always be executed after the shared `onRequest` hooks
656
683
  done()
657
684
  },
685
+ // // Example with an async hook. All hooks support this syntax
686
+ //
687
+ // onRequest: async function (request, reply) {
688
+ // // This hook will always be executed after the shared `onRequest` hooks
689
+ // await ...
690
+ // }
658
691
  onResponse: function (request, reply, done) {
659
692
  // this hook will always be executed after the shared `onResponse` hooks
660
693
  done()
@@ -728,7 +761,7 @@ fastify.get('/me/is-admin', async function (req, reply) {
728
761
  ```
729
762
 
730
763
  Note that `.authenticatedUser` could actually be any property name
731
- choosen by yourself. Using your own custom property prevents you
764
+ chosen by yourself. Using your own custom property prevents you
732
765
  from mutating existing properties, which
733
766
  would be a dangerous and destructive operation. So be careful and
734
767
  make sure your property is entirely new, also using this approach
@@ -774,7 +807,7 @@ initialization of the tracking package, in the typical "require instrumentation
774
807
  tools first" fashion.
775
808
 
776
809
  ```js
777
- const tracer = /* retrieved from elsehwere in the package */
810
+ const tracer = /* retrieved from elsewhere in the package */
778
811
  const dc = require('diagnostics_channel')
779
812
  const channel = dc.channel('fastify.initialization')
780
813
  const spans = new WeakMap()
@@ -243,7 +243,7 @@ reply.trailer('server-timing', function() {
243
243
  })
244
244
 
245
245
  const { createHash } = require('crypto')
246
- // trailer function also recieve two argument
246
+ // trailer function also receive two argument
247
247
  // @param {object} reply fastify reply
248
248
  // @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
249
249
  // @param {function} done callback to set trailer value
@@ -396,7 +396,7 @@ The function returned (a.k.a. _serialization function_) returned is compiled
396
396
  by using the provided `SerializerCompiler`. Also this is cached by using
397
397
  a `WeakMap` for reducing compilation calls.
398
398
 
399
- The optional paramaters `httpStatus` and `contentType`, if provided,
399
+ The optional parameters `httpStatus` and `contentType`, if provided,
400
400
  are forwarded directly to the `SerializerCompiler`, so it can be used
401
401
  to compile the serialization function if a custom `SerializerCompiler` is used.
402
402
 
@@ -78,7 +78,7 @@ fastify.route(options)
78
78
  when a response has been sent, so you will not be able to send more data to
79
79
  the client. It could also be an array of functions.
80
80
  * `onTimeout(request, reply, done)`: a [function](./Hooks.md#ontimeout) called
81
- when a request is timed out and the HTTP socket has been hanged up.
81
+ when a request is timed out and the HTTP socket has been hung up.
82
82
  * `onError(request, reply, error, done)`: a [function](./Hooks.md#onerror)
83
83
  called when an Error is thrown or sent to the client by the route handler.
84
84
  * `handler(request, reply)`: the function that will handle this request. The
@@ -219,7 +219,7 @@ fastify.get('/', opts)
219
219
  ```
220
220
 
221
221
  > Note: if the handler is specified in both the `options` and as the third
222
- > parameter to the shortcut method then throws duplicate `handler` error.
222
+ > parameter to the shortcut method then throws a duplicate `handler` error.
223
223
 
224
224
  ### Url building
225
225
  <a id="url-building"></a>
@@ -290,6 +290,17 @@ fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, r
290
290
  In this case as parameter separator it is possible to use whatever character is
291
291
  not matched by the regular expression.
292
292
 
293
+ The last parameter can be made optional if you add a question mark ("?") to the
294
+ end of the parameters name.
295
+ ```js
296
+ fastify.get('/example/posts/:id?', function (request, reply) {
297
+ const { id } = request.params;
298
+ // your code here
299
+ })
300
+ ```
301
+ In this case you can request `/example/posts` as well as `/example/posts/1`.
302
+ The optional param will be undefined if not specified.
303
+
293
304
  Having a route with multiple parameters may negatively affect performance, so
294
305
  prefer a single parameter approach whenever possible, especially on routes that
295
306
  are on the hot path of your application. If you are interested in how we handle
@@ -916,9 +916,25 @@ fastify.ready().then(() => {
916
916
 
917
917
  Starts the server and internally waits for the `.ready()` event. The signature
918
918
  is `.listen([options][, callback])`. Both the `options` object and the
919
- `callback` parameters follow the [Node.js
920
- core](https://nodejs.org/api/net.html#serverlistenoptions-callback) parameter
921
- definitions.
919
+ `callback` parameters extend the [Node.js
920
+ core](https://nodejs.org/api/net.html#serverlistenoptions-callback) options
921
+ object. Thus, all core options are available with the following additional
922
+ Fastify specific options:
923
+
924
+ ### `listenTextResolver`
925
+ <a id="listen-text-resolver"></a>
926
+
927
+ Set an optional resolver for the text to log after server has been successfully
928
+ started.
929
+ It is possible to override the default `Server listening at [address]` log
930
+ entry using this option.
931
+
932
+ ```js
933
+ server.listen({
934
+ port: 9080,
935
+ listenTextResolver: (address) => { return `Prometheus metrics server is listening at ${address}` }
936
+ })
937
+ ```
922
938
 
923
939
  By default, the server will listen on the address(es) resolved by `localhost`
924
940
  when no specific host is provided. If listening on any available interface is
@@ -170,7 +170,7 @@ function plugin1(fastify: FastifyInstance, _opts, done): void {
170
170
  })
171
171
  }
172
172
  }, (req) => {
173
- // it doesn't works! in a new scope needs to call `withTypeProvider` again
173
+ // it doesn't work! in a new scope needs to call `withTypeProvider` again
174
174
  const { x, y, z } = req.body
175
175
  });
176
176
  done()
@@ -186,7 +186,7 @@ Serialization](./Validation-and-Serialization.md) documentation for more info.
186
186
  Also it has the advantage to use the defined type within your handlers
187
187
  (including pre-validation, etc.).
188
188
 
189
- Here are some options how to achieve this.
189
+ Here are some options on how to achieve this.
190
190
 
191
191
  #### Fastify Type Providers
192
192
 
@@ -1307,10 +1307,10 @@ types defined in this section are used under-the-hood by the Fastify instance
1307
1307
  [src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#L105)
1308
1308
 
1309
1309
  A type declaration for the route handler methods. Has two arguments, `request`
1310
- and `reply` which are typed by `FastifyRequest` and `FastifyReply` respectfully.
1310
+ and `reply` which are typed by `FastifyRequest` and `FastifyReply` respectively.
1311
1311
  The generics parameters are passed through to these arguments. The method
1312
1312
  returns either `void` or `Promise<any>` for synchronous and asynchronous
1313
- handlers respectfully.
1313
+ handlers respectively.
1314
1314
 
1315
1315
  ##### fastify.RouteOptions<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
1316
1316
 
package/docs/index.md CHANGED
@@ -7,8 +7,8 @@ The documentation for Fastify is split into two categories:
7
7
 
8
8
  The reference documentation utilizes a very formal style in an effort to document
9
9
  Fastify's API and implementation details thoroughly for the developer who needs
10
- such. The guides category utilizes an informal, educational, style as a means to
11
- introduce newcomers to core, and advanced, Fastify concepts.
10
+ such. The guides category utilizes an informal educational style as a means to
11
+ introduce newcomers to core and advanced Fastify concepts.
12
12
 
13
13
  ## Where To Start
14
14
 
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ const fastify = require('../../fastify')({
4
+ logger: false
5
+ })
6
+
7
+ const jsonParser = require('fast-json-body')
8
+ const querystring = require('querystring')
9
+
10
+ // Handled by fastify
11
+ // curl -X POST -d '{"hello":"world"}' -H'Content-type: application/json' http://localhost:3000/
12
+
13
+ // curl -X POST -d '{"hello":"world"}' -H'Content-type: application/jsoff' http://localhost:3000/
14
+ fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
15
+ jsonParser(payload, function (err, body) {
16
+ done(err, body)
17
+ })
18
+ })
19
+
20
+ // curl -X POST -d 'hello=world' -H'Content-type: application/x-www-form-urlencoded' http://localhost:3000/
21
+ fastify.addContentTypeParser('application/x-www-form-urlencoded', function (request, payload, done) {
22
+ let body = ''
23
+ payload.on('data', function (data) {
24
+ body += data
25
+ })
26
+ payload.on('end', function () {
27
+ try {
28
+ const parsed = querystring.parse(body)
29
+ done(null, parsed)
30
+ } catch (e) {
31
+ done(e)
32
+ }
33
+ })
34
+ payload.on('error', done)
35
+ })
36
+
37
+ // curl -X POST -d '{"hello":"world"}' -H'Content-type: application/vnd.custom+json' http://localhost:3000/
38
+ fastify.addContentTypeParser(/^application\/.+\+json$/, { parseAs: 'string' }, fastify.getDefaultJsonParser('error', 'ignore'))
39
+
40
+ fastify
41
+ .post('/', function (req, reply) {
42
+ reply.send(req.body)
43
+ })
44
+
45
+ fastify.listen({ port: 3000 }, (err, address) => {
46
+ if (err) throw err
47
+ })