fastify 4.0.0 → 4.0.3
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.
- package/.markdownlint-cli2.yaml +22 -0
- package/GOVERNANCE.md +30 -20
- package/PROJECT_CHARTER.md +48 -17
- package/README.md +165 -78
- package/SECURITY.md +55 -44
- package/build/build-error-serializer.js +12 -7
- package/docs/Guides/Benchmarking.md +2 -0
- package/docs/Guides/Delay-Accepting-Requests.md +98 -90
- package/docs/Guides/Ecosystem.md +43 -30
- package/docs/Guides/Index.md +2 -0
- package/docs/Guides/Migration-Guide-V4.md +55 -0
- package/docs/Guides/Serverless.md +13 -12
- package/docs/Reference/ContentTypeParser.md +17 -13
- package/docs/Reference/Errors.md +6 -5
- package/docs/Reference/LTS.md +2 -2
- package/docs/Reference/Plugins.md +8 -6
- package/docs/Reference/Reply.md +30 -16
- package/docs/Reference/Request.md +3 -3
- package/docs/Reference/Routes.md +112 -37
- package/docs/Reference/Server.md +109 -72
- package/docs/Reference/Type-Providers.md +30 -9
- package/docs/Reference/TypeScript.md +12 -6
- package/docs/Reference/Validation-and-Serialization.md +39 -37
- package/fastify.js +1 -1
- package/lib/error-serializer.js +171 -175
- package/lib/pluginUtils.js +10 -0
- package/lib/reply.js +1 -1
- package/lib/server.js +9 -1
- package/lib/wrapThenable.js +8 -3
- package/package.json +8 -6
- package/test/build/error-serializer.test.js +28 -0
- package/test/{internals → build}/version.test.js +1 -1
- package/test/listen.test.js +16 -2
- package/test/plugin.test.js +32 -0
- package/test/reply-error.test.js +7 -1
- package/test/stream.test.js +73 -0
- package/docs/Migration-Guide-V4.md +0 -12
package/SECURITY.md
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
1
|
# Security Policy
|
|
2
2
|
|
|
3
|
-
This document describes the management of vulnerabilities for the
|
|
4
|
-
|
|
3
|
+
This document describes the management of vulnerabilities for the Fastify
|
|
4
|
+
project and its official plugins.
|
|
5
5
|
|
|
6
6
|
## Reporting vulnerabilities
|
|
7
7
|
|
|
8
|
-
Individuals who find potential vulnerabilities in Fastify are invited
|
|
9
|
-
|
|
8
|
+
Individuals who find potential vulnerabilities in Fastify are invited to
|
|
9
|
+
complete a vulnerability report via the dedicated HackerOne page:
|
|
10
10
|
[https://hackerone.com/fastify](https://hackerone.com/fastify).
|
|
11
11
|
|
|
12
12
|
### Strict measures when reporting vulnerabilities
|
|
13
13
|
|
|
14
14
|
It is of the utmost importance that you read carefully and follow these
|
|
15
|
-
guidelines to ensure the ecosystem as a whole isn't disrupted due to
|
|
16
|
-
|
|
15
|
+
guidelines to ensure the ecosystem as a whole isn't disrupted due to improperly
|
|
16
|
+
reported vulnerabilities:
|
|
17
17
|
|
|
18
18
|
* Avoid creating new "informative" reports on HackerOne. Only create new
|
|
19
|
-
HackerOne reports on a vulnerability if you are absolutely sure this
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* HackerOne reports should never be created and triaged by the same person.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
HackerOne reports on a vulnerability if you are absolutely sure this should be
|
|
20
|
+
tagged as an actual vulnerability. Third-party vendors and individuals are
|
|
21
|
+
tracking any new vulnerabilities reported in HackerOne and will flag them as
|
|
22
|
+
such for their customers (think about snyk, npm audit, ...).
|
|
23
|
+
* HackerOne reports should never be created and triaged by the same person. If
|
|
24
|
+
you are creating a HackerOne report for a vulnerability that you found, or on
|
|
25
|
+
behalf of someone else, there should always be a 2nd Security Team member who
|
|
26
|
+
triages it. If in doubt, invite more Fastify Collaborators to help triage the
|
|
27
|
+
validity of the report. In any case, the report should follow the same process
|
|
28
|
+
as outlined below of inviting the maintainers to review and accept the
|
|
29
|
+
vulnerability.
|
|
30
30
|
|
|
31
31
|
### Vulnerabilities found outside this process
|
|
32
32
|
|
|
33
|
-
⚠ The Fastify project does not support any reporting outside the HackerOne
|
|
33
|
+
⚠ The Fastify project does not support any reporting outside the HackerOne
|
|
34
|
+
process.
|
|
34
35
|
|
|
35
36
|
## Handling vulnerability reports
|
|
36
37
|
|
|
@@ -40,37 +41,40 @@ When a potential vulnerability is reported, the following actions are taken:
|
|
|
40
41
|
|
|
41
42
|
**Delay:** 4 business days
|
|
42
43
|
|
|
43
|
-
Within 4 business days, a member of the security team provides a first answer to
|
|
44
|
-
individual who submitted the potential vulnerability. The possible responses
|
|
44
|
+
Within 4 business days, a member of the security team provides a first answer to
|
|
45
|
+
the individual who submitted the potential vulnerability. The possible responses
|
|
45
46
|
can be:
|
|
46
47
|
|
|
47
48
|
* Acceptance: what was reported is considered as a new vulnerability
|
|
48
49
|
* Rejection: what was reported is not considered as a new vulnerability
|
|
49
|
-
* Need more information: the security team needs more information in order to
|
|
50
|
+
* Need more information: the security team needs more information in order to
|
|
51
|
+
evaluate what was reported.
|
|
50
52
|
|
|
51
53
|
Triaging should include updating issue fields:
|
|
52
54
|
* Asset - set/create the module affected by the report
|
|
53
55
|
* Severity - TBD, currently left empty
|
|
54
56
|
|
|
55
|
-
Reference: [HackerOne: Submitting
|
|
57
|
+
Reference: [HackerOne: Submitting
|
|
58
|
+
Reports](https://docs.hackerone.com/hackers/submitting-reports.html)
|
|
56
59
|
|
|
57
60
|
### Correction follow-up
|
|
58
61
|
|
|
59
62
|
**Delay:** 90 days
|
|
60
63
|
|
|
61
|
-
When a vulnerability is confirmed, a member of the security team volunteers to
|
|
62
|
-
up on this report.
|
|
64
|
+
When a vulnerability is confirmed, a member of the security team volunteers to
|
|
65
|
+
follow up on this report.
|
|
63
66
|
|
|
64
|
-
With the help of the individual who reported the vulnerability, they contact
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
With the help of the individual who reported the vulnerability, they contact the
|
|
68
|
+
maintainers of the vulnerable package to make them aware of the vulnerability.
|
|
69
|
+
The maintainers can be invited as participants to the reported issue.
|
|
67
70
|
|
|
68
|
-
With the package maintainer, they define a release date for the publication
|
|
69
|
-
|
|
70
|
-
|
|
71
|
+
With the package maintainer, they define a release date for the publication of
|
|
72
|
+
the vulnerability. Ideally, this release date should not happen before the
|
|
73
|
+
package has been patched.
|
|
71
74
|
|
|
72
75
|
The report's vulnerable versions upper limit should be set to:
|
|
73
|
-
* `*` if there is no fixed version available by the time of publishing the
|
|
76
|
+
* `*` if there is no fixed version available by the time of publishing the
|
|
77
|
+
report.
|
|
74
78
|
* the last vulnerable version. For example: `<=1.2.3` if a fix exists in `1.2.4`
|
|
75
79
|
|
|
76
80
|
### Publication
|
|
@@ -79,34 +83,41 @@ The report's vulnerable versions upper limit should be set to:
|
|
|
79
83
|
|
|
80
84
|
Within 90 days after the triage date, the vulnerability must be made public.
|
|
81
85
|
|
|
82
|
-
**Severity**: Vulnerability severity is assessed using [CVSS
|
|
83
|
-
More information can be found on
|
|
86
|
+
**Severity**: Vulnerability severity is assessed using [CVSS
|
|
87
|
+
v.3](https://www.first.org/cvss/user-guide). More information can be found on
|
|
88
|
+
[HackerOne documentation](https://docs.hackerone.com/hackers/severity.html)
|
|
84
89
|
|
|
85
90
|
If the package maintainer is actively developing a patch, an additional delay
|
|
86
91
|
can be added with the approval of the security team and the individual who
|
|
87
|
-
reported the vulnerability.
|
|
92
|
+
reported the vulnerability.
|
|
88
93
|
|
|
89
94
|
At this point, a CVE should be requested through the HackerOne platform through
|
|
90
95
|
the UI, which should include the Report ID and a summary.
|
|
91
96
|
|
|
92
97
|
Within HackerOne, this is handled through a "public disclosure request".
|
|
93
98
|
|
|
94
|
-
Reference: [HackerOne:
|
|
99
|
+
Reference: [HackerOne:
|
|
100
|
+
Disclosure](https://docs.hackerone.com/hackers/disclosure.html)
|
|
95
101
|
|
|
96
102
|
## The Fastify Security team
|
|
97
103
|
|
|
98
|
-
The core team is responsible for the management of HackerOne program and this
|
|
104
|
+
The core team is responsible for the management of HackerOne program and this
|
|
105
|
+
policy and process.
|
|
99
106
|
|
|
100
|
-
Members of this team are expected to keep all information that they have
|
|
101
|
-
on the team completely private to the team. This
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
Members of this team are expected to keep all information that they have
|
|
108
|
+
privileged access to by being on the team completely private to the team. This
|
|
109
|
+
includes agreeing to not notify anyone outside the team of issues that have not
|
|
110
|
+
yet been disclosed publicly, including the existence of issues, expectations of
|
|
111
|
+
upcoming releases, and patching of any issues other than in the process of their
|
|
112
|
+
work as a member of the Fastify Core team.
|
|
105
113
|
|
|
106
114
|
### Members
|
|
107
115
|
|
|
108
|
-
* [__Matteo Collina__](https://github.com/mcollina),
|
|
109
|
-
|
|
116
|
+
* [__Matteo Collina__](https://github.com/mcollina),
|
|
117
|
+
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
|
|
118
|
+
* [__Tomas Della Vedova__](https://github.com/delvedor),
|
|
119
|
+
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
|
|
110
120
|
* [__Vincent Le Goff__](https://github.com/zekth)
|
|
111
121
|
* [__KaKa Ng__](https://github.com/climba03003)
|
|
112
|
-
* [__James Sumners__](https://github.com/jsumners),
|
|
122
|
+
* [__James Sumners__](https://github.com/jsumners),
|
|
123
|
+
<https://twitter.com/jsumners79>, <https://www.npmjs.com/~jsumners>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
/* istanbul ignore file */
|
|
1
2
|
'use strict'
|
|
2
3
|
|
|
3
4
|
const FJS = require('fast-json-stringify')
|
|
4
5
|
const path = require('path')
|
|
5
6
|
const fs = require('fs')
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
+
const code = FJS({
|
|
8
9
|
type: 'object',
|
|
9
10
|
properties: {
|
|
10
11
|
statusCode: { type: 'number' },
|
|
@@ -12,16 +13,20 @@ const debugCompiled = FJS({
|
|
|
12
13
|
error: { type: 'string' },
|
|
13
14
|
message: { type: 'string' }
|
|
14
15
|
}
|
|
15
|
-
}, {
|
|
16
|
+
}, { mode: 'standalone' })
|
|
16
17
|
|
|
17
18
|
const file = path.join(__dirname, '..', 'lib', 'error-serializer.js')
|
|
18
|
-
const rawString = debugCompiled.toString()
|
|
19
19
|
|
|
20
20
|
const moduleCode = `// This file is autogenerated by build/build-error-serializer.js, do not edit
|
|
21
21
|
/* istanbul ignore file */
|
|
22
|
-
|
|
23
|
-
${rawString.slice(5)}
|
|
22
|
+
${code}
|
|
24
23
|
`
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
if (require.main === module) {
|
|
26
|
+
fs.writeFileSync(file, moduleCode)
|
|
27
|
+
console.log(`Saved ${file} file successfully`)
|
|
28
|
+
} else {
|
|
29
|
+
module.exports = {
|
|
30
|
+
code: moduleCode
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -53,6 +53,8 @@ npm run bench
|
|
|
53
53
|
|
|
54
54
|
### Run different examples
|
|
55
55
|
|
|
56
|
+
<!-- markdownlint-disable -->
|
|
56
57
|
```sh
|
|
57
58
|
branchcmp --rounds 2 -s "node ./node_modules/concurrently -k -s first \"node ./examples/asyncawait.js\" \"node ./node_modules/autocannon -c 100 -d 5 -p 10 localhost:3000/\""
|
|
58
59
|
```
|
|
60
|
+
<!-- markdownlint-enable -->
|
|
@@ -5,15 +5,16 @@
|
|
|
5
5
|
## Introduction
|
|
6
6
|
|
|
7
7
|
Fastify provides several [hooks](../Reference/Hooks.md) useful for a variety of
|
|
8
|
-
situations. One of them is the [`onReady`](../Reference/Hooks.md#onready) hook,
|
|
9
|
-
useful for executing tasks *right before* the server starts accepting
|
|
10
|
-
requests. There isn't, though, a direct mechanism to handle scenarios in
|
|
11
|
-
you'd like the server to start accepting **specific** requests and denying
|
|
12
|
-
others, at least up to some point.
|
|
13
|
-
|
|
14
|
-
Say, for instance, your server needs to authenticate with an OAuth provider
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
situations. One of them is the [`onReady`](../Reference/Hooks.md#onready) hook,
|
|
9
|
+
which is useful for executing tasks *right before* the server starts accepting
|
|
10
|
+
new requests. There isn't, though, a direct mechanism to handle scenarios in
|
|
11
|
+
which you'd like the server to start accepting **specific** requests and denying
|
|
12
|
+
all others, at least up to some point.
|
|
13
|
+
|
|
14
|
+
Say, for instance, your server needs to authenticate with an OAuth provider to
|
|
15
|
+
start serving requests. To do that it'd need to engage in the [OAuth
|
|
16
|
+
Authorization Code
|
|
17
|
+
Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow),
|
|
17
18
|
which would require it to listen to two requests from the authentication
|
|
18
19
|
provider:
|
|
19
20
|
|
|
@@ -31,14 +32,13 @@ rolling asap!
|
|
|
31
32
|
|
|
32
33
|
### Overview
|
|
33
34
|
|
|
34
|
-
The proposed solution is one of many possible ways of dealing with this
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
The proposed solution is one of many possible ways of dealing with this scenario
|
|
36
|
+
and many similar to it. It relies solely on Fastify, so no fancy infrastructure
|
|
37
|
+
tricks or third-party libraries will be necessary.
|
|
37
38
|
|
|
38
|
-
To simplify things we won't be dealing with a precise OAuth flow but,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
an external provider.
|
|
39
|
+
To simplify things we won't be dealing with a precise OAuth flow but, instead,
|
|
40
|
+
simulate a scenario in which some key is needed to serve a request and that key
|
|
41
|
+
can only be retrieved in runtime by authenticating with an external provider.
|
|
42
42
|
|
|
43
43
|
The main goal here is to deny requests that would otherwise fail **as early as
|
|
44
44
|
possible** and with some **meaningful context**. That's both useful for the
|
|
@@ -97,7 +97,7 @@ server.get('/v1*', async function (request, reply) {
|
|
|
97
97
|
error,
|
|
98
98
|
message: 'Failed at fetching sensitive data from provider',
|
|
99
99
|
})
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
reply.statusCode = 500
|
|
102
102
|
return { customer: null, error: true }
|
|
103
103
|
}
|
|
@@ -122,13 +122,13 @@ server.listen({ port: '1234' }, () => {
|
|
|
122
122
|
|
|
123
123
|
Our code is simply setting up a Fastify server with a few routes:
|
|
124
124
|
|
|
125
|
-
- a `/ping` route that specifies whether the service is ready or
|
|
126
|
-
|
|
127
|
-
- a `/webhook` endpoint for our provider to reach back to us when
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
- a catchall `/v1*` route to simulate what would have been
|
|
131
|
-
|
|
125
|
+
- a `/ping` route that specifies whether the service is ready or not to serve
|
|
126
|
+
requests by checking if the `magicKey` has been set up
|
|
127
|
+
- a `/webhook` endpoint for our provider to reach back to us when they're ready
|
|
128
|
+
to share the `magicKey`. The `magicKey` is, then, saved into the previously set
|
|
129
|
+
decorator on the `fastify` object
|
|
130
|
+
- a catchall `/v1*` route to simulate what would have been customer-initiated
|
|
131
|
+
requests. These requests rely on us having a valid `magicKey`
|
|
132
132
|
|
|
133
133
|
The `provider.js` file, simulating actions of an external provider, is as
|
|
134
134
|
follows:
|
|
@@ -166,11 +166,11 @@ exports.fetchSensitiveData = async (key) => {
|
|
|
166
166
|
// Simulate processing delay
|
|
167
167
|
await delay(700)
|
|
168
168
|
const data = { sensitive: true }
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
if (key === MAGIC_KEY) {
|
|
171
171
|
return data
|
|
172
172
|
}
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
throw new Error('Invalid key')
|
|
175
175
|
}
|
|
176
176
|
```
|
|
@@ -184,16 +184,16 @@ our `magicKey` set up. Until we receive the webhook request from our external
|
|
|
184
184
|
provider (in this example we're simulating a 5 second delay) all our requests
|
|
185
185
|
under the `/v1*` path (customer requests) will fail. Worse than that: they'll
|
|
186
186
|
fail after we've reached out to our provider with an invalid key and got an
|
|
187
|
-
error from them. That wasted time and resources for us and our
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
error from them. That wasted time and resources for us and our customers.
|
|
188
|
+
Depending on the kind of application we're running and on the request rate we're
|
|
189
|
+
expecting this delay is not acceptable or, at least, very annoying.
|
|
190
190
|
|
|
191
191
|
Of course, that could be simply mitigated by checking whether or not the
|
|
192
192
|
`magicKey` has been set up before hitting the provider in the `/v1*` handler.
|
|
193
193
|
Sure, but that would lead to bloat in the code. And imagine we have dozens of
|
|
194
|
-
different routes, with different controllers, that require that key. Should
|
|
195
|
-
|
|
196
|
-
|
|
194
|
+
different routes, with different controllers, that require that key. Should we
|
|
195
|
+
repeatedly add that check to all of them? That's error-prone and there are more
|
|
196
|
+
elegant solutions.
|
|
197
197
|
|
|
198
198
|
What we'll do to improve this setup overall is create a
|
|
199
199
|
[`Plugin`](../Reference/Plugins.md) that'll be solely responsible for making
|
|
@@ -280,11 +280,11 @@ exports.fetchSensitiveData = async (key) => {
|
|
|
280
280
|
// Simulate processing delay
|
|
281
281
|
await delay(700)
|
|
282
282
|
const data = { sensitive: true }
|
|
283
|
-
|
|
283
|
+
|
|
284
284
|
if (key === MAGIC_KEY) {
|
|
285
285
|
return data
|
|
286
286
|
}
|
|
287
|
-
|
|
287
|
+
|
|
288
288
|
throw new Error('Invalid key')
|
|
289
289
|
}
|
|
290
290
|
```
|
|
@@ -381,9 +381,9 @@ There is a very specific change on the previously existing files that is worth
|
|
|
381
381
|
mentioning: Beforehand we were using the `server.listen` callback to start the
|
|
382
382
|
authentication process with the external provider and we were decorating the
|
|
383
383
|
`server` object right before initializing the server. That was bloating our
|
|
384
|
-
server initialization setup with unnecessary code and didn't have much to
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
server initialization setup with unnecessary code and didn't have much to do
|
|
385
|
+
with starting the Fastify server. It was a business logic that didn't have its
|
|
386
|
+
specific place in the code base.
|
|
387
387
|
|
|
388
388
|
Now we've implemented the `delayIncomingRequests` plugin in the
|
|
389
389
|
`delay-incoming-requests.js` file. That's, in truth, a module split into two
|
|
@@ -399,11 +399,11 @@ asap and store the `magicKey` somewhere available to all our handlers.
|
|
|
399
399
|
fastify.server.on('listening', doMagic)
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
-
As soon as the server starts listening (very similar behavior to adding a
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
We use that to reach out to
|
|
406
|
-
function.
|
|
402
|
+
As soon as the server starts listening (very similar behavior to adding a piece
|
|
403
|
+
of code to the `server.listen`'s callback function) a `listening` event is
|
|
404
|
+
emitted (for more info refer to
|
|
405
|
+
https://nodejs.org/api/net.html#event-listening). We use that to reach out to
|
|
406
|
+
our provider as soon as possible, with the `doMagic` function.
|
|
407
407
|
|
|
408
408
|
```js
|
|
409
409
|
fastify.decorate('magicKey', null)
|
|
@@ -414,10 +414,10 @@ a placeholder, waiting for the valid value to be retrieved.
|
|
|
414
414
|
|
|
415
415
|
##### delay
|
|
416
416
|
|
|
417
|
-
`delay` is not a plugin itself. It's actually a plugin *factory*. It expects
|
|
418
|
-
|
|
419
|
-
enveloping those routes with an `onRequest` hook that will make sure no
|
|
420
|
-
|
|
417
|
+
`delay` is not a plugin itself. It's actually a plugin *factory*. It expects a
|
|
418
|
+
Fastify plugin with `routes` and exports the actual plugin that'll handle
|
|
419
|
+
enveloping those routes with an `onRequest` hook that will make sure no requests
|
|
420
|
+
are handled until we're ready for them.
|
|
421
421
|
|
|
422
422
|
```js
|
|
423
423
|
const delay = (routes) =>
|
|
@@ -441,33 +441,34 @@ const delay = (routes) =>
|
|
|
441
441
|
}
|
|
442
442
|
```
|
|
443
443
|
|
|
444
|
-
Instead of updating every single controller that might use the
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
444
|
+
Instead of updating every single controller that might use the `magicKey`, we
|
|
445
|
+
simply make sure that no route that's related to customer requests will be
|
|
446
|
+
served until we have everything ready. And there's more: we fail **FAST** and
|
|
447
|
+
have the possibility of giving the customer meaningful information, like how
|
|
448
|
+
long they should wait before retrying the request. Going even further, by
|
|
449
|
+
issuing a [`503` status
|
|
450
|
+
code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) we're
|
|
451
|
+
signaling to our infrastructure components (namely load balancers) we're still
|
|
452
|
+
not ready to take incoming requests and they should redirect traffic to other
|
|
453
|
+
instances, if available, besides in how long we estimate that will be solved.
|
|
454
|
+
All of that in a few simple lines!
|
|
455
455
|
|
|
456
456
|
It's noteworthy that we didn't use the `fastify-plugin` wrapper in the `delay`
|
|
457
457
|
factory. That's because we wanted the `onRequest` hook to only be set within
|
|
458
|
-
that specific scope and not to the scope that called it (in our case, the
|
|
459
|
-
|
|
460
|
-
`skip-override` hidden property, which has a practical effect of making
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
Let's see how that behaves in action. If we fired our server up with
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
458
|
+
that specific scope and not to the scope that called it (in our case, the main
|
|
459
|
+
`server` object defined in `index.js`). `fastify-plugin` sets the
|
|
460
|
+
`skip-override` hidden property, which has a practical effect of making whatever
|
|
461
|
+
changes we make to our `fastify` object available to the upper scope. That's
|
|
462
|
+
also why we used it with the `customerRoutes` plugin: we wanted those routes to
|
|
463
|
+
be available to its calling scope, the `delay` plugin. For more info on that
|
|
464
|
+
subject refer to [Plugins](../Reference/Plugins.md#handle-the-scope).
|
|
465
|
+
|
|
466
|
+
Let's see how that behaves in action. If we fired our server up with `node
|
|
467
|
+
index.js` and made a few requests to test things out. These were the logs we'd
|
|
468
|
+
see (some bloat was removed to ease things up):
|
|
469
|
+
|
|
470
|
+
<!-- markdownlint-disable -->
|
|
471
|
+
```sh
|
|
471
472
|
{"time":1650063793316,"msg":"Doing magic!"}
|
|
472
473
|
{"time":1650063793316,"msg":"Server listening at http://127.0.0.1:1234"}
|
|
473
474
|
{"time":1650063795030,"reqId":"req-1","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51928},"msg":"incoming request"}
|
|
@@ -480,10 +481,11 @@ we'd see (some bloat was removed to ease things up):
|
|
|
480
481
|
{"time":1650063799858,"reqId":"req-4","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51934},"msg":"incoming request"}
|
|
481
482
|
{"time":1650063800561,"reqId":"req-4","res":{"statusCode":200},"responseTime":702.4662979990244,"msg":"request completed"}
|
|
482
483
|
```
|
|
484
|
+
<!-- markdownlint-enable -->
|
|
483
485
|
|
|
484
486
|
Let's focus on a few parts:
|
|
485
487
|
|
|
486
|
-
```
|
|
488
|
+
```sh
|
|
487
489
|
{"time":1650063793316,"msg":"Doing magic!"}
|
|
488
490
|
{"time":1650063793316,"msg":"Server listening at http://127.0.0.1:1234"}
|
|
489
491
|
```
|
|
@@ -494,18 +496,20 @@ couldn't do that before the server was ready to receive connections).
|
|
|
494
496
|
|
|
495
497
|
While the server is still not ready, a few requests are attempted:
|
|
496
498
|
|
|
497
|
-
|
|
499
|
+
<!-- markdownlint-disable -->
|
|
500
|
+
```sh
|
|
498
501
|
{"time":1650063795030,"reqId":"req-1","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51928},"msg":"incoming request"}
|
|
499
502
|
{"time":1650063795033,"reqId":"req-1","res":{"statusCode":503},"responseTime":2.5721680000424385,"msg":"request completed"}
|
|
500
503
|
{"time":1650063796248,"reqId":"req-2","req":{"method":"GET","url":"/ping","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51930},"msg":"incoming request"}
|
|
501
504
|
{"time":1650063796248,"reqId":"req-2","res":{"statusCode":200},"responseTime":0.4802369996905327,"msg":"request completed"}
|
|
502
505
|
```
|
|
506
|
+
<!-- markdownlint-enable -->
|
|
503
507
|
|
|
504
508
|
The first one (`req-1`) was a `GET /v1`, that failed (**FAST** - `responseTime`
|
|
505
509
|
is in `ms`) with our `503` status code and the meaningful information in the
|
|
506
510
|
response. Below is the response for that request:
|
|
507
511
|
|
|
508
|
-
```
|
|
512
|
+
```sh
|
|
509
513
|
HTTP/1.1 503 Service Unavailable
|
|
510
514
|
Connection: keep-alive
|
|
511
515
|
Content-Length: 31
|
|
@@ -524,12 +528,12 @@ Then we attempt a new request (`req-2`), which was a `GET /ping`. As expected,
|
|
|
524
528
|
since that was not one of the requests we asked our plugin to filter, it
|
|
525
529
|
succeeded. That could also be used as means of informing an interested party
|
|
526
530
|
whether or not we were ready to serve requests (although `/ping` is more
|
|
527
|
-
commonly associated with
|
|
528
|
-
of a
|
|
529
|
-
[here](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes))
|
|
530
|
-
Below is the response for that request:
|
|
531
|
+
commonly associated with *liveness* checks and that would be the responsibility
|
|
532
|
+
of a *readiness* check -- the curious reader can get more info on these terms
|
|
533
|
+
[here](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes))
|
|
534
|
+
with the `ready` field. Below is the response for that request:
|
|
531
535
|
|
|
532
|
-
```
|
|
536
|
+
```sh
|
|
533
537
|
HTTP/1.1 200 OK
|
|
534
538
|
Connection: keep-alive
|
|
535
539
|
Content-Length: 29
|
|
@@ -545,26 +549,30 @@ Keep-Alive: timeout=5
|
|
|
545
549
|
|
|
546
550
|
After that there were more interesting log messages:
|
|
547
551
|
|
|
548
|
-
|
|
552
|
+
<!-- markdownlint-disable -->
|
|
553
|
+
```sh
|
|
549
554
|
{"time":1650063798377,"reqId":"req-3","req":{"method":"POST","url":"/webhook","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51932},"msg":"incoming request"}
|
|
550
555
|
{"time":1650063798379,"reqId":"req-3","msg":"Ready for customer requests!"}
|
|
551
556
|
{"time":1650063798379,"reqId":"req-3","res":{"statusCode":200},"responseTime":1.3567829988896847,"msg":"request completed"}
|
|
552
557
|
```
|
|
558
|
+
<!-- markdownlint-enable -->
|
|
553
559
|
|
|
554
560
|
This time it was our simulated external provider hitting us to let us know
|
|
555
561
|
authentication had gone well and telling us what our `magicKey` was. We saved
|
|
556
562
|
that into our `magicKey` decorator and celebrated with a log message saying we
|
|
557
563
|
were now ready for customers to hit us!
|
|
558
564
|
|
|
559
|
-
|
|
565
|
+
<!-- markdownlint-disable -->
|
|
566
|
+
```sh
|
|
560
567
|
{"time":1650063799858,"reqId":"req-4","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51934},"msg":"incoming request"}
|
|
561
568
|
{"time":1650063800561,"reqId":"req-4","res":{"statusCode":200},"responseTime":702.4662979990244,"msg":"request completed"}
|
|
562
569
|
```
|
|
570
|
+
<!-- markdownlint-enable -->
|
|
563
571
|
|
|
564
572
|
Finally, a final `GET /v1` request was made and, this time, it succeeded. Its
|
|
565
573
|
response was the following:
|
|
566
574
|
|
|
567
|
-
```
|
|
575
|
+
```sh
|
|
568
576
|
HTTP/1.1 200 OK
|
|
569
577
|
Connection: keep-alive
|
|
570
578
|
Content-Length: 31
|
|
@@ -580,18 +588,18 @@ Keep-Alive: timeout=5
|
|
|
580
588
|
|
|
581
589
|
## Conclusion
|
|
582
590
|
|
|
583
|
-
Specifics of the implementation will vary
|
|
584
|
-
|
|
585
|
-
|
|
591
|
+
Specifics of the implementation will vary from one problem to another, but the
|
|
592
|
+
main goal of this guide was to show a very specific use case of an issue that
|
|
593
|
+
could be solved within Fastify's ecosystem.
|
|
586
594
|
|
|
587
595
|
This guide is a tutorial on the use of plugins, decorators, and hooks to solve
|
|
588
596
|
the problem of delaying serving specific requests on our application. It's not
|
|
589
597
|
production-ready, as it keeps local state (the `magicKey`) and it's not
|
|
590
|
-
horizontally scalable (we don't want to flood our provider, right?). One way
|
|
591
|
-
|
|
592
|
-
|
|
598
|
+
horizontally scalable (we don't want to flood our provider, right?). One way of
|
|
599
|
+
improving it would be storing the `magicKey` somewhere else (perhaps a cache
|
|
600
|
+
database?).
|
|
593
601
|
|
|
594
602
|
The keywords here were [Decorators](../Reference/Decorators.md),
|
|
595
|
-
[Hooks](../Reference/Hooks.md), and [Plugins](../Reference/Plugins.md).
|
|
596
|
-
what Fastify has to offer can lead to very ingenious and creative
|
|
597
|
-
wide variety of problems. Let's be creative! :)
|
|
603
|
+
[Hooks](../Reference/Hooks.md), and [Plugins](../Reference/Plugins.md).
|
|
604
|
+
Combining what Fastify has to offer can lead to very ingenious and creative
|
|
605
|
+
solutions to a wide variety of problems. Let's be creative! :)
|