node-fastify 5.8.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/.borp.yaml +3 -0
- package/.markdownlint-cli2.yaml +22 -0
- package/.prettierignore +1 -0
- package/GOVERNANCE.md +4 -0
- package/LICENSE +21 -0
- package/PROJECT_CHARTER.md +126 -0
- package/README.md +423 -0
- package/SECURITY.md +220 -0
- package/SPONSORS.md +24 -0
- package/build/build-error-serializer.js +35 -0
- package/build/build-validation.js +169 -0
- package/build/sync-version.js +11 -0
- package/docs/Guides/Benchmarking.md +60 -0
- package/docs/Guides/Database.md +321 -0
- package/docs/Guides/Delay-Accepting-Requests.md +608 -0
- package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
- package/docs/Guides/Ecosystem.md +726 -0
- package/docs/Guides/Fluent-Schema.md +127 -0
- package/docs/Guides/Getting-Started.md +620 -0
- package/docs/Guides/Index.md +43 -0
- package/docs/Guides/Migration-Guide-V3.md +287 -0
- package/docs/Guides/Migration-Guide-V4.md +267 -0
- package/docs/Guides/Migration-Guide-V5.md +727 -0
- package/docs/Guides/Plugins-Guide.md +520 -0
- package/docs/Guides/Prototype-Poisoning.md +383 -0
- package/docs/Guides/Recommendations.md +378 -0
- package/docs/Guides/Serverless.md +604 -0
- package/docs/Guides/Style-Guide.md +246 -0
- package/docs/Guides/Testing.md +481 -0
- package/docs/Guides/Write-Plugin.md +103 -0
- package/docs/Guides/Write-Type-Provider.md +34 -0
- package/docs/Reference/ContentTypeParser.md +271 -0
- package/docs/Reference/Decorators.md +436 -0
- package/docs/Reference/Encapsulation.md +194 -0
- package/docs/Reference/Errors.md +377 -0
- package/docs/Reference/HTTP2.md +94 -0
- package/docs/Reference/Hooks.md +958 -0
- package/docs/Reference/Index.md +73 -0
- package/docs/Reference/LTS.md +86 -0
- package/docs/Reference/Lifecycle.md +99 -0
- package/docs/Reference/Logging.md +268 -0
- package/docs/Reference/Middleware.md +79 -0
- package/docs/Reference/Plugins.md +245 -0
- package/docs/Reference/Principles.md +73 -0
- package/docs/Reference/Reply.md +1001 -0
- package/docs/Reference/Request.md +295 -0
- package/docs/Reference/Routes.md +802 -0
- package/docs/Reference/Server.md +2389 -0
- package/docs/Reference/Type-Providers.md +256 -0
- package/docs/Reference/TypeScript.md +1729 -0
- package/docs/Reference/Validation-and-Serialization.md +1130 -0
- package/docs/Reference/Warnings.md +58 -0
- package/docs/index.md +24 -0
- package/docs/resources/encapsulation_context.drawio +1 -0
- package/docs/resources/encapsulation_context.svg +3 -0
- package/eslint.config.js +35 -0
- package/examples/asyncawait.js +38 -0
- package/examples/benchmark/body.json +3 -0
- package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
- package/examples/benchmark/hooks-benchmark.js +52 -0
- package/examples/benchmark/parser.js +47 -0
- package/examples/benchmark/simple.js +30 -0
- package/examples/benchmark/webstream.js +27 -0
- package/examples/hooks.js +91 -0
- package/examples/http2.js +39 -0
- package/examples/https.js +38 -0
- package/examples/parser.js +53 -0
- package/examples/plugin.js +12 -0
- package/examples/route-prefix.js +38 -0
- package/examples/shared-schema.js +38 -0
- package/examples/simple-stream.js +20 -0
- package/examples/simple.js +32 -0
- package/examples/simple.mjs +27 -0
- package/examples/typescript-server.ts +79 -0
- package/examples/use-plugin.js +29 -0
- package/fastify.d.ts +253 -0
- package/fastify.js +985 -0
- package/integration/server.js +29 -0
- package/integration/test.sh +23 -0
- package/lib/config-validator.js +1266 -0
- package/lib/content-type-parser.js +413 -0
- package/lib/content-type.js +160 -0
- package/lib/context.js +98 -0
- package/lib/decorate.js +152 -0
- package/lib/error-handler.js +173 -0
- package/lib/error-serializer.js +134 -0
- package/lib/error-status.js +14 -0
- package/lib/errors.js +516 -0
- package/lib/four-oh-four.js +190 -0
- package/lib/handle-request.js +195 -0
- package/lib/head-route.js +45 -0
- package/lib/hooks.js +429 -0
- package/lib/initial-config-validation.js +37 -0
- package/lib/logger-factory.js +136 -0
- package/lib/logger-pino.js +68 -0
- package/lib/noop-set.js +10 -0
- package/lib/plugin-override.js +90 -0
- package/lib/plugin-utils.js +169 -0
- package/lib/promise.js +23 -0
- package/lib/reply.js +1030 -0
- package/lib/req-id-gen-factory.js +52 -0
- package/lib/request.js +391 -0
- package/lib/route.js +686 -0
- package/lib/schema-controller.js +164 -0
- package/lib/schemas.js +207 -0
- package/lib/server.js +441 -0
- package/lib/symbols.js +71 -0
- package/lib/validation.js +280 -0
- package/lib/warnings.js +57 -0
- package/lib/wrap-thenable.js +84 -0
- package/package.json +225 -0
- package/scripts/validate-ecosystem-links.js +179 -0
- package/test/404s.test.js +2035 -0
- package/test/500s.test.js +422 -0
- package/test/allow-unsafe-regex.test.js +92 -0
- package/test/als.test.js +65 -0
- package/test/async-await.test.js +705 -0
- package/test/async-dispose.test.js +20 -0
- package/test/async_hooks.test.js +52 -0
- package/test/body-limit.test.js +224 -0
- package/test/buffer.test.js +74 -0
- package/test/build/error-serializer.test.js +36 -0
- package/test/build/version.test.js +14 -0
- package/test/build-certificate.js +109 -0
- package/test/bundler/README.md +29 -0
- package/test/bundler/esbuild/bundler-test.js +32 -0
- package/test/bundler/esbuild/package.json +10 -0
- package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
- package/test/bundler/esbuild/src/index.js +9 -0
- package/test/bundler/webpack/bundler-test.js +32 -0
- package/test/bundler/webpack/package.json +11 -0
- package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
- package/test/bundler/webpack/src/index.js +9 -0
- package/test/bundler/webpack/webpack.config.js +15 -0
- package/test/case-insensitive.test.js +102 -0
- package/test/chainable.test.js +40 -0
- package/test/child-logger-factory.test.js +128 -0
- package/test/client-timeout.test.js +38 -0
- package/test/close-pipelining.test.js +78 -0
- package/test/close.test.js +706 -0
- package/test/conditional-pino.test.js +47 -0
- package/test/connection-timeout.test.js +42 -0
- package/test/constrained-routes.test.js +1138 -0
- package/test/content-length.test.js +174 -0
- package/test/content-parser.test.js +739 -0
- package/test/content-type.test.js +181 -0
- package/test/context-config.test.js +164 -0
- package/test/custom-http-server.test.js +118 -0
- package/test/custom-parser-async.test.js +59 -0
- package/test/custom-parser.0.test.js +701 -0
- package/test/custom-parser.1.test.js +266 -0
- package/test/custom-parser.2.test.js +91 -0
- package/test/custom-parser.3.test.js +208 -0
- package/test/custom-parser.4.test.js +218 -0
- package/test/custom-parser.5.test.js +130 -0
- package/test/custom-querystring-parser.test.js +129 -0
- package/test/decorator.test.js +1330 -0
- package/test/delete.test.js +344 -0
- package/test/diagnostics-channel/404.test.js +49 -0
- package/test/diagnostics-channel/async-delay-request.test.js +65 -0
- package/test/diagnostics-channel/async-request.test.js +64 -0
- package/test/diagnostics-channel/error-before-handler.test.js +35 -0
- package/test/diagnostics-channel/error-request.test.js +53 -0
- package/test/diagnostics-channel/error-status.test.js +123 -0
- package/test/diagnostics-channel/init.test.js +50 -0
- package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
- package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
- package/test/diagnostics-channel/sync-request.test.js +54 -0
- package/test/encapsulated-child-logger-factory.test.js +69 -0
- package/test/encapsulated-error-handler.test.js +237 -0
- package/test/esm/errorCodes.test.mjs +10 -0
- package/test/esm/esm.test.mjs +13 -0
- package/test/esm/index.test.js +8 -0
- package/test/esm/named-exports.mjs +14 -0
- package/test/esm/other.mjs +8 -0
- package/test/esm/plugin.mjs +8 -0
- package/test/fastify-instance.test.js +300 -0
- package/test/find-route.test.js +152 -0
- package/test/fluent-schema.test.js +209 -0
- package/test/genReqId.test.js +426 -0
- package/test/handler-context.test.js +45 -0
- package/test/handler-timeout.test.js +367 -0
- package/test/has-route.test.js +88 -0
- package/test/header-overflow.test.js +55 -0
- package/test/helper.js +496 -0
- package/test/hooks-async.test.js +1099 -0
- package/test/hooks.on-listen.test.js +1162 -0
- package/test/hooks.on-ready.test.js +421 -0
- package/test/hooks.test.js +3578 -0
- package/test/http-methods/copy.test.js +35 -0
- package/test/http-methods/custom-http-methods.test.js +114 -0
- package/test/http-methods/get.test.js +412 -0
- package/test/http-methods/head.test.js +263 -0
- package/test/http-methods/lock.test.js +108 -0
- package/test/http-methods/mkcalendar.test.js +143 -0
- package/test/http-methods/mkcol.test.js +35 -0
- package/test/http-methods/move.test.js +42 -0
- package/test/http-methods/propfind.test.js +136 -0
- package/test/http-methods/proppatch.test.js +105 -0
- package/test/http-methods/report.test.js +142 -0
- package/test/http-methods/search.test.js +233 -0
- package/test/http-methods/trace.test.js +21 -0
- package/test/http-methods/unlock.test.js +38 -0
- package/test/http2/closing.test.js +270 -0
- package/test/http2/constraint.test.js +109 -0
- package/test/http2/head.test.js +34 -0
- package/test/http2/plain.test.js +68 -0
- package/test/http2/secure-with-fallback.test.js +113 -0
- package/test/http2/secure.test.js +67 -0
- package/test/http2/unknown-http-method.test.js +34 -0
- package/test/https/custom-https-server.test.js +58 -0
- package/test/https/https.test.js +136 -0
- package/test/imports.test.js +17 -0
- package/test/inject.test.js +502 -0
- package/test/input-validation.js +335 -0
- package/test/internals/all.test.js +38 -0
- package/test/internals/content-type-parser.test.js +111 -0
- package/test/internals/context.test.js +31 -0
- package/test/internals/decorator.test.js +156 -0
- package/test/internals/errors.test.js +982 -0
- package/test/internals/handle-request.test.js +270 -0
- package/test/internals/hook-runner.test.js +449 -0
- package/test/internals/hooks.test.js +96 -0
- package/test/internals/initial-config.test.js +383 -0
- package/test/internals/logger.test.js +163 -0
- package/test/internals/plugin.test.js +170 -0
- package/test/internals/promise.test.js +63 -0
- package/test/internals/reply-serialize.test.js +714 -0
- package/test/internals/reply.test.js +1920 -0
- package/test/internals/req-id-gen-factory.test.js +133 -0
- package/test/internals/request-validate.test.js +1402 -0
- package/test/internals/request.test.js +506 -0
- package/test/internals/schema-controller-perf.test.js +40 -0
- package/test/internals/server.test.js +91 -0
- package/test/internals/validation.test.js +352 -0
- package/test/issue-4959.test.js +118 -0
- package/test/keep-alive-timeout.test.js +42 -0
- package/test/listen.1.test.js +154 -0
- package/test/listen.2.test.js +113 -0
- package/test/listen.3.test.js +83 -0
- package/test/listen.4.test.js +168 -0
- package/test/listen.5.test.js +122 -0
- package/test/logger/instantiation.test.js +341 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +460 -0
- package/test/logger/options.test.js +579 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +183 -0
- package/test/logger/tap-parallel-not-ok +0 -0
- package/test/max-requests-per-socket.test.js +113 -0
- package/test/middleware.test.js +37 -0
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +187 -0
- package/test/options.error-handler.test.js +5 -0
- package/test/options.test.js +5 -0
- package/test/output-validation.test.js +140 -0
- package/test/patch.error-handler.test.js +5 -0
- package/test/patch.test.js +5 -0
- package/test/plugin.1.test.js +230 -0
- package/test/plugin.2.test.js +314 -0
- package/test/plugin.3.test.js +287 -0
- package/test/plugin.4.test.js +504 -0
- package/test/plugin.helper.js +8 -0
- package/test/plugin.name.display.js +10 -0
- package/test/post-empty-body.test.js +38 -0
- package/test/pretty-print.test.js +366 -0
- package/test/promises.test.js +125 -0
- package/test/proto-poisoning.test.js +145 -0
- package/test/put.error-handler.test.js +5 -0
- package/test/put.test.js +5 -0
- package/test/register.test.js +184 -0
- package/test/reply-code.test.js +148 -0
- package/test/reply-early-hints.test.js +100 -0
- package/test/reply-error.test.js +815 -0
- package/test/reply-trailers.test.js +445 -0
- package/test/reply-web-stream-locked.test.js +37 -0
- package/test/request-error.test.js +624 -0
- package/test/request-header-host.test.js +339 -0
- package/test/request-id.test.js +118 -0
- package/test/request-timeout.test.js +53 -0
- package/test/route-hooks.test.js +635 -0
- package/test/route-prefix.test.js +904 -0
- package/test/route-shorthand.test.js +48 -0
- package/test/route.1.test.js +259 -0
- package/test/route.2.test.js +100 -0
- package/test/route.3.test.js +213 -0
- package/test/route.4.test.js +127 -0
- package/test/route.5.test.js +211 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +406 -0
- package/test/route.8.test.js +225 -0
- package/test/router-options.test.js +1108 -0
- package/test/same-shape.test.js +124 -0
- package/test/schema-examples.test.js +661 -0
- package/test/schema-feature.test.js +2198 -0
- package/test/schema-serialization.test.js +1171 -0
- package/test/schema-special-usage.test.js +1348 -0
- package/test/schema-validation.test.js +1572 -0
- package/test/scripts/validate-ecosystem-links.test.js +339 -0
- package/test/serialize-response.test.js +186 -0
- package/test/server.test.js +347 -0
- package/test/set-error-handler.test.js +69 -0
- package/test/skip-reply-send.test.js +317 -0
- package/test/stream-serializers.test.js +40 -0
- package/test/stream.1.test.js +94 -0
- package/test/stream.2.test.js +129 -0
- package/test/stream.3.test.js +198 -0
- package/test/stream.4.test.js +176 -0
- package/test/stream.5.test.js +188 -0
- package/test/sync-routes.test.js +32 -0
- package/test/throw.test.js +359 -0
- package/test/toolkit.js +63 -0
- package/test/trust-proxy.test.js +162 -0
- package/test/type-provider.test.js +22 -0
- package/test/types/content-type-parser.test-d.ts +72 -0
- package/test/types/decorate-request-reply.test-d.ts +18 -0
- package/test/types/dummy-plugin.ts +9 -0
- package/test/types/errors.test-d.ts +90 -0
- package/test/types/fastify.test-d.ts +352 -0
- package/test/types/hooks.test-d.ts +550 -0
- package/test/types/import.ts +2 -0
- package/test/types/instance.test-d.ts +588 -0
- package/test/types/logger.test-d.ts +277 -0
- package/test/types/plugin.test-d.ts +97 -0
- package/test/types/register.test-d.ts +237 -0
- package/test/types/reply.test-d.ts +254 -0
- package/test/types/request.test-d.ts +188 -0
- package/test/types/route.test-d.ts +553 -0
- package/test/types/schema.test-d.ts +135 -0
- package/test/types/serverFactory.test-d.ts +37 -0
- package/test/types/type-provider.test-d.ts +1213 -0
- package/test/types/using.test-d.ts +17 -0
- package/test/upgrade.test.js +52 -0
- package/test/url-rewriting.test.js +122 -0
- package/test/use-semicolon-delimiter.test.js +168 -0
- package/test/validation-error-handling.test.js +900 -0
- package/test/versioned-routes.test.js +603 -0
- package/test/web-api.test.js +616 -0
- package/test/wrap-thenable.test.js +30 -0
- package/types/content-type-parser.d.ts +75 -0
- package/types/context.d.ts +22 -0
- package/types/errors.d.ts +92 -0
- package/types/hooks.d.ts +875 -0
- package/types/instance.d.ts +609 -0
- package/types/logger.d.ts +107 -0
- package/types/plugin.d.ts +44 -0
- package/types/register.d.ts +42 -0
- package/types/reply.d.ts +81 -0
- package/types/request.d.ts +95 -0
- package/types/route.d.ts +199 -0
- package/types/schema.d.ts +61 -0
- package/types/server-factory.d.ts +19 -0
- package/types/type-provider.d.ts +130 -0
- package/types/utils.d.ts +98 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
<h1 align="center">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
## Decorators
|
|
4
|
+
|
|
5
|
+
The decorators API customizes core Fastify objects, such as the server instance
|
|
6
|
+
and any request and reply objects used during the HTTP request lifecycle. It
|
|
7
|
+
can attach any type of property to core objects, e.g., functions, plain
|
|
8
|
+
objects, or native types.
|
|
9
|
+
|
|
10
|
+
This API is *synchronous*. Defining a decoration asynchronously could result in
|
|
11
|
+
the Fastify instance booting before the decoration completes. To register an
|
|
12
|
+
asynchronous decoration, use the `register` API with `fastify-plugin`. See the
|
|
13
|
+
[Plugins](./Plugins.md) documentation for more details.
|
|
14
|
+
|
|
15
|
+
Decorating core objects with this API allows the underlying JavaScript engine to
|
|
16
|
+
optimize the handling of server, request, and reply objects. This is
|
|
17
|
+
accomplished by defining the shape of all such object instances before they are
|
|
18
|
+
instantiated and used. As an example, the following is not recommended because
|
|
19
|
+
it will change the shape of objects during their lifecycle:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// Bad example! Continue reading.
|
|
23
|
+
|
|
24
|
+
// Attach a user property to the incoming request before the request
|
|
25
|
+
// handler is invoked.
|
|
26
|
+
fastify.addHook('preHandler', function (req, reply, done) {
|
|
27
|
+
req.user = 'Bob Dylan'
|
|
28
|
+
done()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
// Use the attached user property in the request handler.
|
|
32
|
+
fastify.get('/', function (req, reply) {
|
|
33
|
+
reply.send(`Hello, ${req.user}`)
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The above example mutates the request object after instantiation, causing the
|
|
38
|
+
JavaScript engine to deoptimize access. Using the decoration API avoids this
|
|
39
|
+
deoptimization:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
// Decorate request with a 'user' property
|
|
43
|
+
fastify.decorateRequest('user', '')
|
|
44
|
+
|
|
45
|
+
// Update our property
|
|
46
|
+
fastify.addHook('preHandler', (req, reply, done) => {
|
|
47
|
+
req.user = 'Bob Dylan'
|
|
48
|
+
done()
|
|
49
|
+
})
|
|
50
|
+
// And finally access it
|
|
51
|
+
fastify.get('/', (req, reply) => {
|
|
52
|
+
reply.send(`Hello, ${req.user}!`)
|
|
53
|
+
})
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Keep the initial shape of a decorated field close to its future dynamic value.
|
|
57
|
+
Initialize a decorator as `''` for strings and `null` for objects or functions.
|
|
58
|
+
This works only with value types; reference types will throw an error during
|
|
59
|
+
Fastify startup. See [decorateRequest](#decorate-request) and
|
|
60
|
+
[JavaScript engine fundamentals: Shapes
|
|
61
|
+
and Inline Caches](https://mathiasbynens.be/notes/shapes-ics)
|
|
62
|
+
for more information.
|
|
63
|
+
|
|
64
|
+
### Usage
|
|
65
|
+
<a id="usage"></a>
|
|
66
|
+
|
|
67
|
+
#### `decorate(name, value, [dependencies])`
|
|
68
|
+
<a id="decorate"></a>
|
|
69
|
+
|
|
70
|
+
This method customizes the Fastify [server](./Server.md) instance.
|
|
71
|
+
|
|
72
|
+
For example, to attach a new method to the server instance:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
fastify.decorate('utility', function () {
|
|
76
|
+
// Something very useful
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Non-function values can also be attached to the server instance:
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
fastify.decorate('conf', {
|
|
84
|
+
db: 'some.db',
|
|
85
|
+
port: 3000
|
|
86
|
+
})
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To access decorated properties, use the name provided to the decoration API:
|
|
90
|
+
|
|
91
|
+
```js
|
|
92
|
+
fastify.utility()
|
|
93
|
+
|
|
94
|
+
console.log(fastify.conf.db)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The decorated [Fastify server](./Server.md) is bound to `this` in
|
|
98
|
+
[route](./Routes.md) handlers:
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
fastify.decorate('db', new DbConnection())
|
|
102
|
+
|
|
103
|
+
fastify.get('/', async function (request, reply) {
|
|
104
|
+
// using return
|
|
105
|
+
return { hello: await this.db.query('world') }
|
|
106
|
+
|
|
107
|
+
// or
|
|
108
|
+
// using reply.send()
|
|
109
|
+
reply.send({ hello: await this.db.query('world') })
|
|
110
|
+
await reply
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `dependencies` parameter is an optional list of decorators that the
|
|
115
|
+
decorator being defined relies upon. This list contains the names of other
|
|
116
|
+
decorators. In the following example, the "utility" decorator depends on the
|
|
117
|
+
"greet" and "hi" decorators:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
async function greetDecorator (fastify, opts) {
|
|
121
|
+
fastify.decorate('greet', () => {
|
|
122
|
+
return 'greet message'
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function hiDecorator (fastify, opts) {
|
|
127
|
+
fastify.decorate('hi', () => {
|
|
128
|
+
return 'hi message'
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function utilityDecorator (fastify, opts) {
|
|
133
|
+
fastify.decorate('utility', () => {
|
|
134
|
+
return `${fastify.greet()} | ${fastify.hi()}`
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
fastify.register(fastifyPlugin(greetDecorator, { name: 'greet' }))
|
|
139
|
+
fastify.register(fastifyPlugin(hiDecorator, { name: 'hi' }))
|
|
140
|
+
fastify.register(fastifyPlugin(utilityDecorator, { dependencies: ['greet', 'hi'] }))
|
|
141
|
+
|
|
142
|
+
fastify.get('/', function (req, reply) {
|
|
143
|
+
// Response: {"hello":"greet message | hi message"}
|
|
144
|
+
reply.send({ hello: fastify.utility() })
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
fastify.listen({ port: 3000 }, (err, address) => {
|
|
148
|
+
if (err) throw err
|
|
149
|
+
})
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Using an arrow function breaks the binding of `this` to
|
|
153
|
+
the `FastifyInstance`.
|
|
154
|
+
|
|
155
|
+
If a dependency is not satisfied, the `decorate` method throws an exception.
|
|
156
|
+
The dependency check occurs before the server instance boots, not during
|
|
157
|
+
runtime.
|
|
158
|
+
|
|
159
|
+
#### `decorateReply(name, value, [dependencies])`
|
|
160
|
+
<a id="decorate-reply"></a>
|
|
161
|
+
|
|
162
|
+
This API adds new methods/properties to the core `Reply` object:
|
|
163
|
+
|
|
164
|
+
```js
|
|
165
|
+
fastify.decorateReply('utility', function () {
|
|
166
|
+
// Something very useful
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Using an arrow function will break the binding of `this` to the Fastify
|
|
171
|
+
`Reply` instance.
|
|
172
|
+
|
|
173
|
+
Using `decorateReply` will throw and error if used with a reference type:
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
// Don't do this
|
|
177
|
+
fastify.decorateReply('foo', { bar: 'fizz'})
|
|
178
|
+
```
|
|
179
|
+
In this example, the object reference would be shared with all requests, and
|
|
180
|
+
**any mutation will impact all requests, potentially creating security
|
|
181
|
+
vulnerabilities or memory leaks**. Fastify blocks this.
|
|
182
|
+
|
|
183
|
+
To achieve proper encapsulation across requests configure a new value for each
|
|
184
|
+
incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest).
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
const fp = require('fastify-plugin')
|
|
188
|
+
|
|
189
|
+
async function myPlugin (app) {
|
|
190
|
+
app.decorateReply('foo')
|
|
191
|
+
app.addHook('onRequest', async (req, reply) => {
|
|
192
|
+
reply.foo = { bar: 42 }
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
module.exports = fp(myPlugin)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
See [`decorate`](#decorate) for information about the `dependencies` parameter.
|
|
200
|
+
|
|
201
|
+
#### `decorateRequest(name, value, [dependencies])`
|
|
202
|
+
<a id="decorate-request"></a>
|
|
203
|
+
|
|
204
|
+
As with [`decorateReply`](#decorate-reply), this API adds new methods/properties
|
|
205
|
+
to the core `Request` object:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
fastify.decorateRequest('utility', function () {
|
|
209
|
+
// something very useful
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Using an arrow function will break the binding of `this` to the Fastify
|
|
214
|
+
`Request` instance.
|
|
215
|
+
|
|
216
|
+
Using `decorateRequest` will emit an error if used with a reference type:
|
|
217
|
+
|
|
218
|
+
```js
|
|
219
|
+
// Don't do this
|
|
220
|
+
fastify.decorateRequest('foo', { bar: 'fizz'})
|
|
221
|
+
```
|
|
222
|
+
In this example, the object reference would be shared with all requests, and
|
|
223
|
+
**any mutation will impact all requests, potentially creating security
|
|
224
|
+
vulnerabilities or memory leaks**. Fastify blocks this.
|
|
225
|
+
|
|
226
|
+
To achieve proper encapsulation across requests configure a new value for each
|
|
227
|
+
incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest).
|
|
228
|
+
|
|
229
|
+
Example:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
const fp = require('fastify-plugin')
|
|
233
|
+
|
|
234
|
+
async function myPlugin (app) {
|
|
235
|
+
app.decorateRequest('foo')
|
|
236
|
+
app.addHook('onRequest', async (req, reply) => {
|
|
237
|
+
req.foo = { bar: 42 }
|
|
238
|
+
})
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = fp(myPlugin)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The hook solution is more flexible and allows for more complex initialization
|
|
245
|
+
because more logic can be added to the `onRequest` hook.
|
|
246
|
+
|
|
247
|
+
Another approach is to use the getter/setter pattern, but it requires 2 decorators:
|
|
248
|
+
|
|
249
|
+
```js
|
|
250
|
+
fastify.decorateRequest('my_decorator_holder') // define the holder
|
|
251
|
+
fastify.decorateRequest('user', {
|
|
252
|
+
getter () {
|
|
253
|
+
this.my_decorator_holder ??= {} // initialize the holder
|
|
254
|
+
return this.my_decorator_holder
|
|
255
|
+
}
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
fastify.get('/', async function (req, reply) {
|
|
259
|
+
req.user.access = 'granted'
|
|
260
|
+
// other code
|
|
261
|
+
})
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
This ensures that the `user` property is always unique for each request.
|
|
265
|
+
|
|
266
|
+
See [`decorate`](#decorate) for information about the `dependencies` parameter.
|
|
267
|
+
|
|
268
|
+
#### `hasDecorator(name)`
|
|
269
|
+
<a id="has-decorator"></a>
|
|
270
|
+
|
|
271
|
+
Used to check for the existence of a server instance decoration:
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
fastify.hasDecorator('utility')
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
#### hasRequestDecorator
|
|
278
|
+
<a id="has-request-decorator"></a>
|
|
279
|
+
|
|
280
|
+
Used to check for the existence of a Request decoration:
|
|
281
|
+
|
|
282
|
+
```js
|
|
283
|
+
fastify.hasRequestDecorator('utility')
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
#### hasReplyDecorator
|
|
287
|
+
<a id="has-reply-decorator"></a>
|
|
288
|
+
|
|
289
|
+
Used to check for the existence of a Reply decoration:
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
fastify.hasReplyDecorator('utility')
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Decorators and Encapsulation
|
|
296
|
+
<a id="decorators-encapsulation"></a>
|
|
297
|
+
|
|
298
|
+
Defining a decorator (using `decorate`, `decorateRequest`, or `decorateReply`)
|
|
299
|
+
with the same name more than once in the same **encapsulated** context will
|
|
300
|
+
throw an exception. For example, the following will throw:
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
const server = require('fastify')()
|
|
304
|
+
|
|
305
|
+
server.decorateReply('view', function (template, args) {
|
|
306
|
+
// Amazing view rendering engine
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
server.get('/', (req, reply) => {
|
|
310
|
+
reply.view('/index.html', { hello: 'world' })
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
// Somewhere else in our codebase, we define another
|
|
314
|
+
// view decorator. This throws.
|
|
315
|
+
server.decorateReply('view', function (template, args) {
|
|
316
|
+
// Another rendering engine
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
server.listen({ port: 3000 })
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
But this will not:
|
|
324
|
+
|
|
325
|
+
```js
|
|
326
|
+
const server = require('fastify')()
|
|
327
|
+
|
|
328
|
+
server.decorateReply('view', function (template, args) {
|
|
329
|
+
// Amazing view rendering engine.
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
server.register(async function (server, opts) {
|
|
333
|
+
// We add a view decorator to the current encapsulated
|
|
334
|
+
// plugin. This will not throw as outside of this encapsulated
|
|
335
|
+
// plugin view is the old one, while inside it is the new one.
|
|
336
|
+
server.decorateReply('view', function (template, args) {
|
|
337
|
+
// Another rendering engine
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
server.get('/', (req, reply) => {
|
|
341
|
+
reply.view('/index.page', { hello: 'world' })
|
|
342
|
+
})
|
|
343
|
+
}, { prefix: '/bar' })
|
|
344
|
+
|
|
345
|
+
server.listen({ port: 3000 })
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Getters and Setters
|
|
349
|
+
<a id="getters-setters"></a>
|
|
350
|
+
|
|
351
|
+
Decorators accept special "getter/setter" objects with `getter` and optional
|
|
352
|
+
`setter` functions. This allows defining properties via decorators,
|
|
353
|
+
for example:
|
|
354
|
+
|
|
355
|
+
```js
|
|
356
|
+
fastify.decorate('foo', {
|
|
357
|
+
getter () {
|
|
358
|
+
return 'a getter'
|
|
359
|
+
}
|
|
360
|
+
})
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Will define the `foo` property on the Fastify instance:
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
console.log(fastify.foo) // 'a getter'
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### `getDecorator(name)`
|
|
370
|
+
<a id="get-decorator"></a>
|
|
371
|
+
|
|
372
|
+
Used to retrieve an existing decorator from the Fastify instance, `Request`,
|
|
373
|
+
or `Reply`.
|
|
374
|
+
If the decorator is not defined, an `FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
375
|
+
|
|
376
|
+
```js
|
|
377
|
+
// Get a decorator from the Fastify instance
|
|
378
|
+
const utility = fastify.getDecorator('utility')
|
|
379
|
+
|
|
380
|
+
// Get a decorator from the request object
|
|
381
|
+
const user = request.getDecorator('user')
|
|
382
|
+
|
|
383
|
+
// Get a decorator from the reply object
|
|
384
|
+
const helper = reply.getDecorator('helper')
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
The `getDecorator` method is useful for dependency validation - it can be used to
|
|
388
|
+
check for required decorators at registration time. If any are missing, it fails
|
|
389
|
+
at boot, ensuring dependencies are available during the request lifecycle.
|
|
390
|
+
|
|
391
|
+
```js
|
|
392
|
+
fastify.register(async function (fastify) {
|
|
393
|
+
// Verify the decorator exists before using it
|
|
394
|
+
const usersRepository = fastify.getDecorator('usersRepository')
|
|
395
|
+
|
|
396
|
+
fastify.get('/users', async function (request, reply) {
|
|
397
|
+
return usersRepository.findAll()
|
|
398
|
+
})
|
|
399
|
+
})
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
> ℹ️ Note:
|
|
403
|
+
> For TypeScript users, `getDecorator` supports generic type parameters.
|
|
404
|
+
> See the [TypeScript documentation](./TypeScript.md) for
|
|
405
|
+
> advanced typing examples.
|
|
406
|
+
|
|
407
|
+
#### `setDecorator(name, value)`
|
|
408
|
+
<a id="set-decorator"></a>
|
|
409
|
+
|
|
410
|
+
Used to safely update the value of a `Request` decorator.
|
|
411
|
+
If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error is thrown.
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
fastify.decorateRequest('user', null)
|
|
415
|
+
|
|
416
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
417
|
+
// Safely set the decorator value
|
|
418
|
+
req.setDecorator('user', 'Bob Dylan')
|
|
419
|
+
})
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
The `setDecorator` method provides runtime safety by ensuring the decorator exists
|
|
423
|
+
before setting its value, preventing errors from typos in decorator names.
|
|
424
|
+
|
|
425
|
+
```js
|
|
426
|
+
fastify.decorateRequest('account', null)
|
|
427
|
+
fastify.addHook('preHandler', async (req, reply) => {
|
|
428
|
+
// This will throw FST_ERR_DEC_UNDECLARED due to typo in decorator name
|
|
429
|
+
req.setDecorator('acount', { id: 123 })
|
|
430
|
+
})
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
> ℹ️ Note:
|
|
434
|
+
> For TypeScript users, see the
|
|
435
|
+
> [TypeScript documentation](./TypeScript.md) for advanced
|
|
436
|
+
> typing examples using `setDecorator<T>`.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<h1 align="center">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
## Encapsulation
|
|
4
|
+
<a id="encapsulation"></a>
|
|
5
|
+
|
|
6
|
+
A fundamental feature of Fastify is the "encapsulation context." It governs
|
|
7
|
+
which [decorators](./Decorators.md), registered [hooks](./Hooks.md), and
|
|
8
|
+
[plugins](./Plugins.md) are available to [routes](./Routes.md). A visual
|
|
9
|
+
representation of the encapsulation context is shown in the following figure:
|
|
10
|
+
|
|
11
|
+

|
|
12
|
+
|
|
13
|
+
In the figure above, there are several entities:
|
|
14
|
+
|
|
15
|
+
1. The _root context_
|
|
16
|
+
2. Three _root plugins_
|
|
17
|
+
3. Two _child contexts_, each with:
|
|
18
|
+
* Two _child plugins_
|
|
19
|
+
* One _grandchild context_, each with:
|
|
20
|
+
- Three _child plugins_
|
|
21
|
+
|
|
22
|
+
Every _child context_ and _grandchild context_ has access to the _root plugins_.
|
|
23
|
+
Within each _child context_, the _grandchild contexts_ have access to the
|
|
24
|
+
_child plugins_ registered within the containing _child context_, but the
|
|
25
|
+
containing _child context_ **does not** have access to the _child plugins_
|
|
26
|
+
registered within its _grandchild context_.
|
|
27
|
+
|
|
28
|
+
Given that everything in Fastify is a [plugin](./Plugins.md) except for the
|
|
29
|
+
_root context_, every "context" and "plugin" in this example is a plugin
|
|
30
|
+
that can consist of decorators, hooks, plugins, and routes. As plugins, they
|
|
31
|
+
must still signal completion either by returning a Promise (e.g., using `async`
|
|
32
|
+
functions) or by calling the `done` function if using the callback style.
|
|
33
|
+
|
|
34
|
+
To put this
|
|
35
|
+
example into concrete terms, consider a basic scenario of a REST API server
|
|
36
|
+
with three routes: the first route (`/one`) requires authentication, the
|
|
37
|
+
second route (`/two`) does not, and the third route (`/three`) has access to
|
|
38
|
+
the same context as the second route. Using [@fastify/bearer-auth][bearer] to
|
|
39
|
+
provide authentication, the code for this example is as follows:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
'use strict'
|
|
43
|
+
|
|
44
|
+
const fastify = require('fastify')()
|
|
45
|
+
|
|
46
|
+
fastify.decorateRequest('answer', 42)
|
|
47
|
+
|
|
48
|
+
fastify.register(async function authenticatedContext (childServer) {
|
|
49
|
+
childServer.register(require('@fastify/bearer-auth'), { keys: ['abc123'] })
|
|
50
|
+
|
|
51
|
+
childServer.route({
|
|
52
|
+
path: '/one',
|
|
53
|
+
method: 'GET',
|
|
54
|
+
handler (request, response) {
|
|
55
|
+
response.send({
|
|
56
|
+
answer: request.answer,
|
|
57
|
+
// request.foo will be undefined as it is only defined in publicContext
|
|
58
|
+
foo: request.foo,
|
|
59
|
+
// request.bar will be undefined as it is only defined in grandchildContext
|
|
60
|
+
bar: request.bar
|
|
61
|
+
})
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
fastify.register(async function publicContext (childServer) {
|
|
67
|
+
childServer.decorateRequest('foo', 'foo')
|
|
68
|
+
|
|
69
|
+
childServer.route({
|
|
70
|
+
path: '/two',
|
|
71
|
+
method: 'GET',
|
|
72
|
+
handler (request, response) {
|
|
73
|
+
response.send({
|
|
74
|
+
answer: request.answer,
|
|
75
|
+
foo: request.foo,
|
|
76
|
+
// request.bar will be undefined as it is only defined in grandchildContext
|
|
77
|
+
bar: request.bar
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
childServer.register(async function grandchildContext (grandchildServer) {
|
|
83
|
+
grandchildServer.decorateRequest('bar', 'bar')
|
|
84
|
+
|
|
85
|
+
grandchildServer.route({
|
|
86
|
+
path: '/three',
|
|
87
|
+
method: 'GET',
|
|
88
|
+
handler (request, response) {
|
|
89
|
+
response.send({
|
|
90
|
+
answer: request.answer,
|
|
91
|
+
foo: request.foo,
|
|
92
|
+
bar: request.bar
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
fastify.listen({ port: 8000 })
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The server example above demonstrates the encapsulation concepts from the
|
|
103
|
+
original diagram:
|
|
104
|
+
|
|
105
|
+
1. Each _child context_ (`authenticatedContext`, `publicContext`, and
|
|
106
|
+
`grandchildContext`) has access to the `answer` request decorator defined in
|
|
107
|
+
the _root context_.
|
|
108
|
+
2. Only the `authenticatedContext` has access to the `@fastify/bearer-auth`
|
|
109
|
+
plugin.
|
|
110
|
+
3. Both the `publicContext` and `grandchildContext` have access to the `foo`
|
|
111
|
+
request decorator.
|
|
112
|
+
4. Only the `grandchildContext` has access to the `bar` request decorator.
|
|
113
|
+
|
|
114
|
+
To see this, start the server and issue requests:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
# curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one
|
|
118
|
+
{"answer":42}
|
|
119
|
+
# curl http://127.0.0.1:8000/two
|
|
120
|
+
{"answer":42,"foo":"foo"}
|
|
121
|
+
# curl http://127.0.0.1:8000/three
|
|
122
|
+
{"answer":42,"foo":"foo","bar":"bar"}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
[bearer]: https://github.com/fastify/fastify-bearer-auth
|
|
126
|
+
|
|
127
|
+
## Sharing Between Contexts
|
|
128
|
+
<a id="shared-context"></a>
|
|
129
|
+
|
|
130
|
+
Each context in the prior example inherits _only_ from its parent contexts. Parent
|
|
131
|
+
contexts cannot access entities within their descendant contexts. If needed,
|
|
132
|
+
encapsulation can be broken using [fastify-plugin][fastify-plugin], making
|
|
133
|
+
anything registered in a descendant context available to the parent context.
|
|
134
|
+
|
|
135
|
+
To allow `publicContext` access to the `bar` decorator in `grandchildContext`,
|
|
136
|
+
rewrite the code as follows:
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
'use strict'
|
|
140
|
+
|
|
141
|
+
const fastify = require('fastify')()
|
|
142
|
+
const fastifyPlugin = require('fastify-plugin')
|
|
143
|
+
|
|
144
|
+
fastify.decorateRequest('answer', 42)
|
|
145
|
+
|
|
146
|
+
// `authenticatedContext` omitted for clarity
|
|
147
|
+
|
|
148
|
+
fastify.register(async function publicContext (childServer) {
|
|
149
|
+
childServer.decorateRequest('foo', 'foo')
|
|
150
|
+
|
|
151
|
+
childServer.route({
|
|
152
|
+
path: '/two',
|
|
153
|
+
method: 'GET',
|
|
154
|
+
handler (request, response) {
|
|
155
|
+
response.send({
|
|
156
|
+
answer: request.answer,
|
|
157
|
+
foo: request.foo,
|
|
158
|
+
bar: request.bar
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
childServer.register(fastifyPlugin(grandchildContext))
|
|
164
|
+
|
|
165
|
+
async function grandchildContext (grandchildServer) {
|
|
166
|
+
grandchildServer.decorateRequest('bar', 'bar')
|
|
167
|
+
|
|
168
|
+
grandchildServer.route({
|
|
169
|
+
path: '/three',
|
|
170
|
+
method: 'GET',
|
|
171
|
+
handler (request, response) {
|
|
172
|
+
response.send({
|
|
173
|
+
answer: request.answer,
|
|
174
|
+
foo: request.foo,
|
|
175
|
+
bar: request.bar
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
fastify.listen({ port: 8000 })
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Restarting the server and re-issuing the requests for `/two` and `/three`:
|
|
186
|
+
|
|
187
|
+
```sh
|
|
188
|
+
# curl http://127.0.0.1:8000/two
|
|
189
|
+
{"answer":42,"foo":"foo","bar":"bar"}
|
|
190
|
+
# curl http://127.0.0.1:8000/three
|
|
191
|
+
{"answer":42,"foo":"foo","bar":"bar"}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
[fastify-plugin]: https://github.com/fastify/fastify-plugin
|