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,103 @@
|
|
|
1
|
+
<h1 style="text-align: center;">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
# How to write a good plugin
|
|
4
|
+
First, thank you for deciding to write a plugin for Fastify. Fastify is a
|
|
5
|
+
minimal framework and plugins are its strength, so thank you.
|
|
6
|
+
|
|
7
|
+
The core principles of Fastify are performance, low overhead, and providing a
|
|
8
|
+
good experience to our users. When writing a plugin, it is important to keep
|
|
9
|
+
these principles in mind. Therefore, in this document, we will analyze what
|
|
10
|
+
characterizes a quality plugin.
|
|
11
|
+
|
|
12
|
+
*Need some inspiration? You can use the label ["plugin
|
|
13
|
+
suggestion"](https://github.com/fastify/fastify/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+suggestion%22)
|
|
14
|
+
in our issue tracker!*
|
|
15
|
+
|
|
16
|
+
## Code
|
|
17
|
+
Fastify uses different techniques to optimize its code, many of which are
|
|
18
|
+
documented in our Guides. We highly recommend you read [the hitchhiker's guide
|
|
19
|
+
to plugins](./Plugins-Guide.md) to discover all the APIs you can use to build
|
|
20
|
+
your plugin and learn how to use them.
|
|
21
|
+
|
|
22
|
+
Do you have a question or need some advice? We are more than happy to help you!
|
|
23
|
+
Just open an issue in our [help repository](https://github.com/fastify/help).
|
|
24
|
+
|
|
25
|
+
Once you submit a plugin to our [ecosystem list](./Ecosystem.md), we will review
|
|
26
|
+
your code and help you improve it if necessary.
|
|
27
|
+
|
|
28
|
+
## Documentation
|
|
29
|
+
Documentation is extremely important. If your plugin is not well documented we
|
|
30
|
+
will not accept it to the ecosystem list. Lack of quality documentation makes it
|
|
31
|
+
more difficult for people to use your plugin, and will likely result in it going
|
|
32
|
+
unused.
|
|
33
|
+
|
|
34
|
+
If you want to see some good examples of how to document a plugin take a look
|
|
35
|
+
at:
|
|
36
|
+
- [`@fastify/caching`](https://github.com/fastify/fastify-caching)
|
|
37
|
+
- [`@fastify/compress`](https://github.com/fastify/fastify-compress)
|
|
38
|
+
- [`@fastify/cookie`](https://github.com/fastify/fastify-cookie)
|
|
39
|
+
- [`@fastify/under-pressure`](https://github.com/fastify/under-pressure)
|
|
40
|
+
- [`@fastify/view`](https://github.com/fastify/point-of-view)
|
|
41
|
+
|
|
42
|
+
## License
|
|
43
|
+
You can license your plugin as you prefer, we do not enforce any kind of
|
|
44
|
+
license.
|
|
45
|
+
|
|
46
|
+
We prefer the [MIT license](https://choosealicense.com/licenses/mit/) because we
|
|
47
|
+
think it allows more people to use the code freely. For a list of alternative
|
|
48
|
+
licenses see the [OSI list](https://opensource.org/licenses) or GitHub's
|
|
49
|
+
[choosealicense.com](https://choosealicense.com/).
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
Always put an example file in your repository. Examples are very helpful for
|
|
53
|
+
users and give a very fast way to test your plugin. Your users will be grateful.
|
|
54
|
+
|
|
55
|
+
## Test
|
|
56
|
+
A plugin **must** be thoroughly tested to verify that is working properly.
|
|
57
|
+
|
|
58
|
+
A plugin without tests will not be accepted to the ecosystem list. A lack of
|
|
59
|
+
tests does not inspire trust nor guarantee that the code will continue to work
|
|
60
|
+
among different versions of its dependencies.
|
|
61
|
+
|
|
62
|
+
We do not enforce any testing library. We use [`node:test`](https://nodejs.org/api/test.html)
|
|
63
|
+
since it offers out-of-the-box parallel testing and code coverage, but it is up
|
|
64
|
+
to you to choose your library of preference.
|
|
65
|
+
We highly recommend you read the [Plugin Testing](./Testing.md#plugins) to
|
|
66
|
+
learn about how to test your plugins.
|
|
67
|
+
|
|
68
|
+
## Code Linter
|
|
69
|
+
It is not mandatory, but we highly recommend you use a code linter in your
|
|
70
|
+
plugin. It will ensure a consistent code style and help you to avoid many
|
|
71
|
+
errors.
|
|
72
|
+
|
|
73
|
+
We use [`standard`](https://standardjs.com/) since it works without the need to
|
|
74
|
+
configure it and is very easy to integrate into a test suite.
|
|
75
|
+
|
|
76
|
+
## Continuous Integration
|
|
77
|
+
It is not mandatory, but if you release your code as open source, it helps to
|
|
78
|
+
use Continuous Integration to ensure contributions do not break your plugin and
|
|
79
|
+
to show that the plugin works as intended. Both
|
|
80
|
+
[CircleCI](https://circleci.com/) and [GitHub
|
|
81
|
+
Actions](https://github.com/features/actions) are free for open source projects
|
|
82
|
+
and easy to set up.
|
|
83
|
+
|
|
84
|
+
In addition, you can enable services like [Dependabot](https://github.com/dependabot),
|
|
85
|
+
which will help you keep your dependencies up to date and discover if a new
|
|
86
|
+
release of Fastify has some issues with your plugin.
|
|
87
|
+
|
|
88
|
+
## Let's start!
|
|
89
|
+
Awesome, now you know everything you need to know about how to write a good
|
|
90
|
+
plugin for Fastify! After you have built one (or more!) let us know! We will add
|
|
91
|
+
it to the [ecosystem](https://github.com/fastify/fastify#ecosystem) section of
|
|
92
|
+
our documentation!
|
|
93
|
+
|
|
94
|
+
If you want to see some real world examples, check out:
|
|
95
|
+
- [`@fastify/view`](https://github.com/fastify/point-of-view) Templates
|
|
96
|
+
rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify.
|
|
97
|
+
- [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify
|
|
98
|
+
MongoDB connection plugin, with this you can share the same MongoDB connection
|
|
99
|
+
pool in every part of your server.
|
|
100
|
+
- [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart
|
|
101
|
+
support for Fastify.
|
|
102
|
+
- [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important
|
|
103
|
+
security headers for Fastify.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<h1 align="center">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
## How to write your own type provider
|
|
4
|
+
|
|
5
|
+
Things to keep in mind when implementing a custom [type provider](../Reference/Type-Providers.md):
|
|
6
|
+
|
|
7
|
+
### Type Contravariance
|
|
8
|
+
|
|
9
|
+
Whereas exhaustive type narrowing checks normally rely on `never` to represent
|
|
10
|
+
an unreachable state, reduction in type provider interfaces should only be done
|
|
11
|
+
up to `unknown`.
|
|
12
|
+
|
|
13
|
+
The reasoning is that certain methods of `FastifyInstance` are
|
|
14
|
+
contravariant on `TypeProvider`, which can lead to TypeScript surfacing
|
|
15
|
+
assignability issues unless the custom type provider interface is
|
|
16
|
+
substitutable with `FastifyTypeProviderDefault`.
|
|
17
|
+
|
|
18
|
+
For example, `FastifyTypeProviderDefault` will not be assignable to the following:
|
|
19
|
+
```ts
|
|
20
|
+
export interface NotSubstitutableTypeProvider extends FastifyTypeProvider {
|
|
21
|
+
// bad, nothing is assignable to `never` (except for itself)
|
|
22
|
+
validator: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
|
|
23
|
+
serializer: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Unless changed to:
|
|
28
|
+
```ts
|
|
29
|
+
export interface SubstitutableTypeProvider extends FastifyTypeProvider {
|
|
30
|
+
// good, anything can be assigned to `unknown`
|
|
31
|
+
validator: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : unknown;
|
|
32
|
+
serializer: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : unknown;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
<h1 align="center">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
## `Content-Type` Parser
|
|
4
|
+
Fastify natively supports `'application/json'` and `'text/plain'` content types
|
|
5
|
+
with a default charset of `utf-8`. These default parsers can be changed or
|
|
6
|
+
removed.
|
|
7
|
+
|
|
8
|
+
Unsupported content types will throw an `FST_ERR_CTP_INVALID_MEDIA_TYPE` error.
|
|
9
|
+
|
|
10
|
+
To support other content types, use the `addContentTypeParser` API or an
|
|
11
|
+
existing [plugin](https://fastify.dev/ecosystem/).
|
|
12
|
+
|
|
13
|
+
As with other APIs, `addContentTypeParser` is encapsulated in the scope in which
|
|
14
|
+
it is declared. If declared in the root scope, it is available everywhere; if
|
|
15
|
+
declared in a plugin, it is available only in that scope and its children.
|
|
16
|
+
|
|
17
|
+
Fastify automatically adds the parsed request payload to the [Fastify
|
|
18
|
+
request](./Request.md) object, accessible via `request.body`.
|
|
19
|
+
|
|
20
|
+
> **Important:** When using a body schema with the
|
|
21
|
+
> [`content`](./Validation-and-Serialization.md#body-content-type-validation)
|
|
22
|
+
> property to validate per content type, only content types listed in the schema
|
|
23
|
+
> will be validated. If you add a custom content type parser but do not include
|
|
24
|
+
> its content type in the body schema's `content` property, the incoming data
|
|
25
|
+
> will be parsed but **not validated**.
|
|
26
|
+
|
|
27
|
+
Note that for `GET` and `HEAD` requests, the payload is never parsed. For
|
|
28
|
+
`OPTIONS` and `DELETE` requests, the payload is parsed only if a valid
|
|
29
|
+
`content-type` header is provided. Unlike `POST`, `PUT`, and `PATCH`, the
|
|
30
|
+
[catch-all](#catch-all) parser is not executed, and the payload is simply not
|
|
31
|
+
parsed.
|
|
32
|
+
|
|
33
|
+
> ⚠ Warning:
|
|
34
|
+
> When using regular expressions to detect `Content-Type`, it is important to
|
|
35
|
+
> ensure proper detection. For example, to match `application/*`, use
|
|
36
|
+
> `/^application\/([\w-]+);?/` to match the
|
|
37
|
+
> [essence MIME type](https://mimesniff.spec.whatwg.org/#mime-type-miscellaneous)
|
|
38
|
+
> only.
|
|
39
|
+
>
|
|
40
|
+
> Additionally, if the route uses per-content-type body validation via
|
|
41
|
+
> `schema.body.content`, the schema is selected by an **exact match** on the
|
|
42
|
+
> essence MIME type, not by the parser's regex. A regex parser that accepts
|
|
43
|
+
> content types with no matching key in the `content` schema map will result
|
|
44
|
+
> in those requests **not being validated**. Ensure every content type matched
|
|
45
|
+
> by the regex has a corresponding entry in the schema's `content` map. See
|
|
46
|
+
> [Validation and Serialization](./Validation-and-Serialization.md) for details.
|
|
47
|
+
|
|
48
|
+
### Usage
|
|
49
|
+
```js
|
|
50
|
+
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
|
|
51
|
+
jsoffParser(payload, function (err, body) {
|
|
52
|
+
done(err, body)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
// Handle multiple content types with the same function
|
|
57
|
+
fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) {
|
|
58
|
+
xmlParser(payload, function (err, body) {
|
|
59
|
+
done(err, body)
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// Async is also supported in Node versions >= 8.0.0
|
|
64
|
+
fastify.addContentTypeParser('application/jsoff', async function (request, payload) {
|
|
65
|
+
const res = await jsoffParserAsync(payload)
|
|
66
|
+
|
|
67
|
+
return res
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
// Handle all content types that matches RegExp
|
|
71
|
+
fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) {
|
|
72
|
+
imageParser(payload, function (err, body) {
|
|
73
|
+
done(err, body)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
// Can use default JSON/Text parser for different content Types
|
|
78
|
+
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Fastify first tries to match a content-type parser with a `string` value before
|
|
82
|
+
trying to find a matching `RegExp`. For overlapping content types, it starts
|
|
83
|
+
with the last one configured and ends with the first (last in, first out).
|
|
84
|
+
To specify a general content type more precisely, first specify the general
|
|
85
|
+
type, then the specific one, as shown below.
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
// Here only the second content type parser is called because its value also matches the first one
|
|
89
|
+
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
|
|
90
|
+
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
|
|
91
|
+
|
|
92
|
+
// Here the desired behavior is achieved because fastify first tries to match the
|
|
93
|
+
// `application/vnd.custom+xml` content type parser
|
|
94
|
+
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
|
|
95
|
+
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Using addContentTypeParser with fastify.register
|
|
99
|
+
When using `addContentTypeParser` with `fastify.register`, avoid `await`
|
|
100
|
+
when registering routes. Using `await` makes route registration asynchronous,
|
|
101
|
+
potentially registering routes before `addContentTypeParser` is set.
|
|
102
|
+
|
|
103
|
+
#### Correct Usage
|
|
104
|
+
```js
|
|
105
|
+
const fastify = require('fastify')();
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
fastify.register((fastify, opts) => {
|
|
109
|
+
fastify.addContentTypeParser('application/json', function (request, payload, done) {
|
|
110
|
+
jsonParser(payload, function (err, body) {
|
|
111
|
+
done(err, body)
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
fastify.get('/hello', async (req, res) => {});
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
In addition to `addContentTypeParser`, the `hasContentTypeParser`,
|
|
120
|
+
`removeContentTypeParser`, and `removeAllContentTypeParsers` APIs are available.
|
|
121
|
+
|
|
122
|
+
#### hasContentTypeParser
|
|
123
|
+
|
|
124
|
+
Use the `hasContentTypeParser` API to check if a specific content type parser
|
|
125
|
+
exists.
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
if (!fastify.hasContentTypeParser('application/jsoff')){
|
|
129
|
+
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
|
|
130
|
+
jsoffParser(payload, function (err, body) {
|
|
131
|
+
done(err, body)
|
|
132
|
+
})
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### removeContentTypeParser
|
|
138
|
+
|
|
139
|
+
`removeContentTypeParser` can remove a single content type or an array of
|
|
140
|
+
content types, supporting both `string` and `RegExp`.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
|
|
144
|
+
xmlParser(payload, function (err, body) {
|
|
145
|
+
done(err, body)
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
// Removes the both built-in content type parsers so that only the content type parser for text/html is available
|
|
150
|
+
fastify.removeContentTypeParser(['application/json', 'text/plain'])
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### removeAllContentTypeParsers
|
|
154
|
+
The `removeAllContentTypeParsers` API removes all existing content type parsers
|
|
155
|
+
eliminating the need to specify each one individually. This API supports
|
|
156
|
+
encapsulation and is useful for registering a
|
|
157
|
+
[catch-all content type parser](#catch-all) that should be executed for every
|
|
158
|
+
content type, ignoring built-in parsers.
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
fastify.removeAllContentTypeParsers()
|
|
162
|
+
|
|
163
|
+
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
|
|
164
|
+
xmlParser(payload, function (err, body) {
|
|
165
|
+
done(err, body)
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
> ℹ️ Note:
|
|
171
|
+
> `function(req, done)` and `async function(req)` are
|
|
172
|
+
> still supported but deprecated.
|
|
173
|
+
|
|
174
|
+
#### Body Parser
|
|
175
|
+
The request body can be parsed in two ways. First, add a custom content type
|
|
176
|
+
parser and handle the request stream. Or second, use the `parseAs` option in the
|
|
177
|
+
`addContentTypeParser` API, specifying `'string'` or `'buffer'`. Fastify will
|
|
178
|
+
handle the stream, check the [maximum size](./Server.md#factory-body-limit) of
|
|
179
|
+
the body, and the content length. If the limit is exceeded, the custom parser
|
|
180
|
+
will not be invoked.
|
|
181
|
+
```js
|
|
182
|
+
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
|
|
183
|
+
try {
|
|
184
|
+
const json = JSON.parse(body)
|
|
185
|
+
done(null, json)
|
|
186
|
+
} catch (err) {
|
|
187
|
+
err.statusCode = 400
|
|
188
|
+
done(err, undefined)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
See
|
|
194
|
+
[`example/parser.js`](https://github.com/fastify/fastify/blob/main/examples/parser.js)
|
|
195
|
+
for an example.
|
|
196
|
+
|
|
197
|
+
##### Custom Parser Options
|
|
198
|
+
+ `parseAs` (string): `'string'` or `'buffer'` to designate how the incoming
|
|
199
|
+
data should be collected. Default: `'buffer'`.
|
|
200
|
+
+ `bodyLimit` (number): The maximum payload size, in bytes, that the custom
|
|
201
|
+
parser will accept. Defaults to the global body limit passed to the [`Fastify
|
|
202
|
+
factory function`](./Server.md#bodylimit).
|
|
203
|
+
|
|
204
|
+
#### Catch-All
|
|
205
|
+
To catch all requests regardless of content type, use the `'*'` content type:
|
|
206
|
+
```js
|
|
207
|
+
fastify.addContentTypeParser('*', function (request, payload, done) {
|
|
208
|
+
let data = ''
|
|
209
|
+
payload.on('data', chunk => { data += chunk })
|
|
210
|
+
payload.on('end', () => {
|
|
211
|
+
done(null, data)
|
|
212
|
+
})
|
|
213
|
+
})
|
|
214
|
+
```
|
|
215
|
+
All requests without a corresponding content type parser will be handled by
|
|
216
|
+
this function.
|
|
217
|
+
|
|
218
|
+
This is also useful for piping the request stream. Define a content parser like:
|
|
219
|
+
|
|
220
|
+
```js
|
|
221
|
+
fastify.addContentTypeParser('*', function (request, payload, done) {
|
|
222
|
+
done()
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
And then access the core HTTP request directly for piping:
|
|
227
|
+
|
|
228
|
+
```js
|
|
229
|
+
app.post('/hello', (request, reply) => {
|
|
230
|
+
reply.send(request.raw)
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Here is a complete example that logs incoming [json
|
|
235
|
+
line](https://jsonlines.org/) objects:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const split2 = require('split2')
|
|
239
|
+
const pump = require('pump')
|
|
240
|
+
|
|
241
|
+
fastify.addContentTypeParser('*', (request, payload, done) => {
|
|
242
|
+
done(null, pump(payload, split2(JSON.parse)))
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
fastify.route({
|
|
246
|
+
method: 'POST',
|
|
247
|
+
url: '/api/log/jsons',
|
|
248
|
+
handler: (req, res) => {
|
|
249
|
+
req.body.on('data', d => console.log(d)) // log every incoming object
|
|
250
|
+
}
|
|
251
|
+
})
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
For piping file uploads, check out
|
|
255
|
+
[`@fastify/multipart`](https://github.com/fastify/fastify-multipart).
|
|
256
|
+
|
|
257
|
+
To execute the content type parser on all content types, call
|
|
258
|
+
`removeAllContentTypeParsers` first.
|
|
259
|
+
|
|
260
|
+
```js
|
|
261
|
+
// Without this call, the request body with the content type application/json would be processed by the built-in JSON parser
|
|
262
|
+
fastify.removeAllContentTypeParsers()
|
|
263
|
+
|
|
264
|
+
fastify.addContentTypeParser('*', function (request, payload, done) {
|
|
265
|
+
const data = ''
|
|
266
|
+
payload.on('data', chunk => { data += chunk })
|
|
267
|
+
payload.on('end', () => {
|
|
268
|
+
done(null, data)
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
```
|