基本schema测试

This commit is contained in:
2025-09-22 16:00:32 +08:00
commit b70b69c886
2754 changed files with 408678 additions and 0 deletions

3
node_modules/fastify/.borp.yaml generated vendored Normal file
View File

@@ -0,0 +1,3 @@
files:
- 'test/**/*.test.js'
- 'test/**/*.test.mjs'

22
node_modules/fastify/.markdownlint-cli2.yaml generated vendored Normal file
View File

@@ -0,0 +1,22 @@
# See https://github.com/DavidAnson/markdownlint-cli2
config:
# Disable all rules by default.
default: false
# Enforce line length.
MD013:
line_length: 80
code_block_line_length: 120
headings: false
tables: false
strict: false
stern: false
globs:
- '**/*.md'
ignores:
- 'node_modules/**'
- 'test/**/*.md'
noProgress: true

1
node_modules/fastify/.prettierignore generated vendored Normal file
View File

@@ -0,0 +1 @@
*

4
node_modules/fastify/GOVERNANCE.md generated vendored Normal file
View File

@@ -0,0 +1,4 @@
# Fastify Project Governance
Please see Fastify's [organization-wide governance
](https://github.com/fastify/.github/blob/main/GOVERNANCE.md) document.

24
node_modules/fastify/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,24 @@
MIT License
Copyright (c) 2016-present The Fastify Team
The Fastify team members are listed at https://github.com/fastify/fastify#team
and in the README file.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

126
node_modules/fastify/PROJECT_CHARTER.md generated vendored Normal file
View File

@@ -0,0 +1,126 @@
# Fastify Charter
The Fastify project aims to build a fast and low-overhead web framework for
Node.js.
## Section 0: Guiding Principles
The Fastify project is part of the [OpenJS Foundation][openjs foundation]. It
operates transparently, openly, collaboratively, and ethically. Project
proposals, timelines, and status must not merely be open, but also easily
visible to outsiders.
## Section 1: Scope
Fastify is a web framework highly focused on providing the best developer
experience with the least overhead and a plugin architecture.
### 1.1: In-scope
+ Develop a web framework for Node.js with a focus on developer experience,
performance, and extensibility.
+ Plugin Architecture
+ Support web protocols
+ Official plugins for common user requirements
+ Documentation:
+ Project (policies, processes, and releases)
+ Guides and Tutorials
+ Framework API
+ Website
+ Write easier APIs for developers
+ Tools:
+ CI services
+ Bots to improve overall efficiency
+ Releases
+ Support:
+ Community
+ Users's issues and questions
+ Contributors's pull request review
### 1.2: Out-of-Scope
+ Support versions of Node.js at EOL (end of life) stage
+ Support serverless architecture
+ Contributions that violate the [Code of Conduct](CODE_OF_CONDUCT.md)
## Section 2: Relationship with OpenJS Foundation CPC.
Technical leadership for the projects within the [OpenJS Foundation][openjs
foundation] is delegated to the projects through their project charters by the
[OpenJS Foundation Cross-Project Council](https://openjsf.org/about/governance/)
(CPC). In the case of the Fastify project, it is delegated to the [Fastify
Collaborators](README.md#team). The OpenJS Foundation's business leadership is
the Board of Directors (the "Board").
This Fastify Charter reflects a carefully constructed balanced role for the
Collaborators and the CPC in the governance of the OpenJS Foundation. The
charter amendment process is for the Fastify Collaborators to propose change
using a majority of the full Fastify Organization, the proposed changes
being subject to review and approval by the CPC. The CPC may additionally make
amendments to the Collaborators charter at any time, though the CPC will not
interfere with day-to-day discussions, votes, or meetings of the Fastify
Organization.
### 2.1 Other Formal Project Relationships
Section Intentionally Left Blank
## Section 3: Fastify Governing Body
Fastify is governed by its [maintainers](README.md#team). See [how it is
structured](GOVERNANCE.md) for more information.
## Section 4: Roles & Responsibilities
The roles and responsibilities of Fastify's maintainers are described in [the
project organization](GOVERNANCE.md).
### Section 4.1 Project Operations & Management
Section Intentionally Left Blank
### Section 4.2: Decision-making, Voting, and/or Elections
**Decision-making**
Fastify's features can be discussed in GitHub issues and/or projects. Consensus
on a discussion is reached when there is no objection by any collaborators.
When there is no consensus, Lead Maintainers will have the final say on the
topic.
**Voting, and/or Elections**
These processes are described in the [GOVERNANCE](GOVERNANCE.md) document.
### Section 4.3: Other Project Roles
Section Intentionally Left Blank
## Section 5: Definitions
+ *Contributors*: contribute code or other artifacts, but do not have the right
to commit to the code base. Contributors work with the projects Collaborators
to have code committed to the code base. Contributors should rarely be
encumbered by the Fastify Collaborators and never by the CPC or OpenJS
Foundation Board.
+ *Collaborators*: contribute code and other artifacts, have the right to commit
to the code base, and release plugin projects. Collaborators follow the
[CONTRIBUTING](CONTRIBUTING.md) guidelines to manage the project. A
Collaborator could be encumbered by other Fastify Collaborators and never by
the CPC or OpenJS Foundation Board.
+ *Lead Maintainers*: founders of the project, contribute code and other
artifacts, have the right to commit to the code base and release the project.
Lead Maintainers follow the [CONTRIBUTING](CONTRIBUTING.md) guidelines to
manage the project. A Lead Maintainer will be encumbered by the Fastify
Collaborators and by the CPC or OpenJS Foundation Board.
[openjs foundation]: https://openjsf.org

421
node_modules/fastify/README.md generated vendored Normal file
View File

@@ -0,0 +1,421 @@
<div align="center"> <a href="https://fastify.dev/">
<img
src="https://github.com/fastify/graphics/raw/HEAD/fastify-landscape-outlined.svg"
width="650"
height="auto"
/>
</a>
</div>
<div align="center">
[![CI](https://github.com/fastify/fastify/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/ci.yml)
[![Package Manager
CI](https://github.com/fastify/fastify/actions/workflows/package-manager-ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/package-manager-ci.yml)
[![Web
site](https://github.com/fastify/fastify/actions/workflows/website.yml/badge.svg?branch=main)](https://github.com/fastify/fastify/actions/workflows/website.yml)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/7585/badge)](https://bestpractices.coreinfrastructure.org/projects/7585)
</div>
<div align="center">
[![NPM
version](https://img.shields.io/npm/v/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify)
[![NPM
downloads](https://img.shields.io/npm/dm/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify)
[![Security Responsible
Disclosure](https://img.shields.io/badge/Security-Responsible%20Disclosure-yellow.svg)](https://github.com/fastify/fastify/blob/main/SECURITY.md)
[![Discord](https://img.shields.io/discord/725613461949906985)](https://discord.gg/fastify)
[![Contribute with Gitpod](https://img.shields.io/badge/Contribute%20with-Gitpod-908a85?logo=gitpod&color=blue)](https://gitpod.io/#https://github.com/fastify/fastify)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/fastify)](https://github.com/sponsors/fastify#sponsors)
</div>
<br />
An efficient server implies a lower cost of the infrastructure, better
responsiveness under load, and happy users. How can you efficiently handle the
resources of your server, knowing that you are serving the highest number of
requests possible, without sacrificing security validations and handy
development?
Enter Fastify. Fastify is a web framework highly focused on providing the best
developer experience with the least overhead and a powerful plugin architecture.
It is inspired by Hapi and Express and as far as we know, it is one of the
fastest web frameworks in town.
The `main` branch refers to the Fastify `v5` release.
Check out the [`4.x` branch](https://github.com/fastify/fastify/tree/4.x) for `v4`.
### Table of Contents
- [Quick start](#quick-start)
- [Install](#install)
- [Example](#example)
- [Core features](#core-features)
- [Benchmarks](#benchmarks)
- [Documentation](#documentation)
- [Ecosystem](#ecosystem)
- [Support](#support)
- [Team](#team)
- [Hosted by](#hosted-by)
- [License](#license)
### Quick start
Create a folder and make it your current working directory:
```sh
mkdir my-app
cd my-app
```
Generate a fastify project with `npm init`:
```sh
npm init fastify
```
Install dependencies:
```sh
npm i
```
To start the app in dev mode:
```sh
npm run dev
```
For production mode:
```sh
npm start
```
Under the hood `npm init` downloads and runs [Fastify
Create](https://github.com/fastify/create-fastify), which in turn uses the
generate functionality of [Fastify CLI](https://github.com/fastify/fastify-cli).
### Install
To install Fastify in an existing project as a dependency:
```sh
npm i fastify
```
### Example
```js
// Require the framework and instantiate it
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
// Declare a route
fastify.get('/', (request, reply) => {
reply.send({ hello: 'world' })
})
// Run the server!
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
// Server is now listening on ${address}
})
```
With async-await:
```js
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
fastify.get('/', async (request, reply) => {
reply.type('application/json').code(200)
return { hello: 'world' }
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
// Server is now listening on ${address}
})
```
Do you want to know more? Head to the <a
href="./docs/Guides/Getting-Started.md"><code><b>Getting Started</b></code></a>.
If you learn best by reading code, explore the official [demo](https://github.com/fastify/demo).
> ## Note
> `.listen` binds to the local host, `localhost`, interface by default
> (`127.0.0.1` or `::1`, depending on the operating system configuration). If
> you are running Fastify in a container (Docker,
> [GCP](https://cloud.google.com/), etc.), you may need to bind to `0.0.0.0`. Be
> careful when listening on all interfaces; it comes with inherent
> [security
> risks](https://web.archive.org/web/20170711105010/https://snyk.io/blog/mongodb-hack-and-secure-defaults/).
> See [the documentation](./docs/Reference/Server.md#listen) for more
> information.
### Core features
- **Highly performant:** as far as we know, Fastify is one of the fastest web
frameworks in town, depending on the code complexity we can serve up to 76+
thousand requests per second.
- **Extensible:** Fastify is fully extensible via its hooks, plugins, and
decorators.
- **Schema-based:** even if it is not mandatory we recommend using [JSON
Schema](https://json-schema.org/) to validate your routes and serialize your
outputs. Internally Fastify compiles the schema in a highly performant
function.
- **Logging:** logs are extremely important but are costly; we chose the best
logger to almost remove this cost, [Pino](https://github.com/pinojs/pino)!
- **Developer friendly:** the framework is built to be very expressive and help
developers in their daily use without sacrificing performance and
security.
### Benchmarks
__Machine:__ EX41S-SSD, Intel Core i7, 4Ghz, 64GB RAM, 4C/8T, SSD.
__Method:__: `autocannon -c 100 -d 40 -p 10 localhost:3000` * 2, taking the
second average
| Framework | Version | Router? | Requests/sec |
| :----------------- | :------------------------- | :----------: | ------------: |
| Express | 4.17.3 | &#10003; | 14,200 |
| hapi | 20.2.1 | &#10003; | 42,284 |
| Restify | 8.6.1 | &#10003; | 50,363 |
| Koa | 2.13.0 | &#10007; | 54,272 |
| **Fastify** | **4.0.0** | **&#10003;** | **77,193** |
| - | | | |
| `http.Server` | 16.14.2 | &#10007; | 74,513 |
These benchmarks taken using https://github.com/fastify/benchmarks. This is a
synthetic "hello world" benchmark that aims to evaluate the framework overhead.
The overhead that each framework has on your application depends on your
application. You should __always__ benchmark if performance matters to you.
## Documentation
* [__`Getting Started`__](./docs/Guides/Getting-Started.md)
* [__`Guides`__](./docs/Guides/Index.md)
* [__`Server`__](./docs/Reference/Server.md)
* [__`Routes`__](./docs/Reference/Routes.md)
* [__`Encapsulation`__](./docs/Reference/Encapsulation.md)
* [__`Logging`__](./docs/Reference/Logging.md)
* [__`Middleware`__](./docs/Reference/Middleware.md)
* [__`Hooks`__](./docs/Reference/Hooks.md)
* [__`Decorators`__](./docs/Reference/Decorators.md)
* [__`Validation and Serialization`__](./docs/Reference/Validation-and-Serialization.md)
* [__`Fluent Schema`__](./docs/Guides/Fluent-Schema.md)
* [__`Lifecycle`__](./docs/Reference/Lifecycle.md)
* [__`Reply`__](./docs/Reference/Reply.md)
* [__`Request`__](./docs/Reference/Request.md)
* [__`Errors`__](./docs/Reference/Errors.md)
* [__`Content Type Parser`__](./docs/Reference/ContentTypeParser.md)
* [__`Plugins`__](./docs/Reference/Plugins.md)
* [__`Testing`__](./docs/Guides/Testing.md)
* [__`Benchmarking`__](./docs/Guides/Benchmarking.md)
* [__`How to write a good plugin`__](./docs/Guides/Write-Plugin.md)
* [__`Plugins Guide`__](./docs/Guides/Plugins-Guide.md)
* [__`HTTP2`__](./docs/Reference/HTTP2.md)
* [__`Long Term Support`__](./docs/Reference/LTS.md)
* [__`TypeScript and types support`__](./docs/Reference/TypeScript.md)
* [__`Serverless`__](./docs/Guides/Serverless.md)
* [__`Recommendations`__](./docs/Guides/Recommendations.md)
## Ecosystem
- [Core](./docs/Guides/Ecosystem.md#core) - Core plugins maintained by the
_Fastify_ [team](#team).
- [Community](./docs/Guides/Ecosystem.md#community) - Community-supported
plugins.
- [Live Examples](https://github.com/fastify/example) - Multirepo with a broad
set of real working examples.
- [Discord](https://discord.gg/D3FZYPy) - Join our discord server and chat with
the maintainers.
## Support
Please visit [Fastify help](https://github.com/fastify/help) to view prior
support issues and to ask new support questions.
Version 3 of Fastify and lower are EOL and will not receive any security or bug fixes.
Fastify's partner, HeroDevs, provides commercial security fixes for all
unsupported versions at [https://herodevs.com/support/fastify-nes][hd-link].
Fastify's supported version matrix is available in the
[Long Term Support][lts-link] documentation.
## Contributing
Whether reporting bugs, discussing improvements and new ideas, or writing code,
we welcome contributions from anyone and everyone. Please read the [CONTRIBUTING](./CONTRIBUTING.md)
guidelines before submitting pull requests.
## Team
_Fastify_ is the result of the work of a great community. Team members are
listed in alphabetical order.
**Lead Maintainers:**
* [__Matteo Collina__](https://github.com/mcollina),
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
* [__Tomas Della Vedova__](https://github.com/delvedor),
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
* [__KaKa Ng__](https://github.com/climba03003),
<https://www.npmjs.com/~climba03003>
* [__Manuel Spigolon__](https://github.com/eomm),
<https://twitter.com/manueomm>, <https://www.npmjs.com/~eomm>
* [__James Sumners__](https://github.com/jsumners),
<https://twitter.com/jsumners79>, <https://www.npmjs.com/~jsumners>
### Fastify Core team
* [__Aras Abbasi__](https://github.com/uzlopak),
<https://www.npmjs.com/~uzlopak>
* [__Harry Brundage__](https://github.com/airhorns/),
<https://twitter.com/harrybrundage>, <https://www.npmjs.com/~airhorns>
* [__Matteo Collina__](https://github.com/mcollina),
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
* [__Gürgün Dayıoğlu__](https://github.com/gurgunday),
<https://www.npmjs.com/~gurgunday>
* [__Tomas Della Vedova__](https://github.com/delvedor),
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
* [__Carlos Fuentes__](https://github.com/metcoder95),
<https://twitter.com/metcoder95>, <https://www.npmjs.com/~metcoder95>
* [__Vincent Le Goff__](https://github.com/zekth)
* [__Luciano Mammino__](https://github.com/lmammino),
<https://twitter.com/loige>, <https://www.npmjs.com/~lmammino>
* [__Jean Michelet__](https://github.com/jean-michelet),
<https://www.npmjs.com/~jean-michelet>
* [__KaKa Ng__](https://github.com/climba03003),
<https://www.npmjs.com/~climba03003>
* [__Luis Orbaiceta__](https://github.com/luisorbaiceta),
<https://twitter.com/luisorbai>, <https://www.npmjs.com/~luisorbaiceta>
* [__Maksim Sinik__](https://github.com/fox1t),
<https://twitter.com/maksimsinik>, <https://www.npmjs.com/~fox1t>
* [__Manuel Spigolon__](https://github.com/eomm),
<https://twitter.com/manueomm>, <https://www.npmjs.com/~eomm>
* [__James Sumners__](https://github.com/jsumners),
<https://twitter.com/jsumners79>, <https://www.npmjs.com/~jsumners>
### Fastify Plugins team
* [__Harry Brundage__](https://github.com/airhorns/),
<https://twitter.com/harrybrundage>, <https://www.npmjs.com/~airhorns>
* [__Simone Busoli__](https://github.com/simoneb),
<https://twitter.com/simonebu>, <https://www.npmjs.com/~simoneb>
* [__Dan Castillo__](https://github.com/dancastillo),
<https://www.npmjs.com/~dancastillo>
* [__Matteo Collina__](https://github.com/mcollina),
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
* [__Gürgün Dayıoğlu__](https://github.com/gurgunday),
<https://www.npmjs.com/~gurgunday>
* [__Tomas Della Vedova__](https://github.com/delvedor),
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
* [__Carlos Fuentes__](https://github.com/metcoder95),
<https://twitter.com/metcoder95>, <https://www.npmjs.com/~metcoder95>
* [__Vincent Le Goff__](https://github.com/zekth)
* [__Jean Michelet__](https://github.com/jean-michelet),
<https://www.npmjs.com/~jean-michelet>
* [__KaKa Ng__](https://github.com/climba03003),
<https://www.npmjs.com/~climba03003>
* [__Maksim Sinik__](https://github.com/fox1t),
<https://twitter.com/maksimsinik>, <https://www.npmjs.com/~fox1t>
* [__Frazer Smith__](https://github.com/Fdawgs), <https://www.npmjs.com/~fdawgs>
* [__Manuel Spigolon__](https://github.com/eomm),
<https://twitter.com/manueomm>, <https://www.npmjs.com/~eomm>
### Emeritus Contributors
Great contributors to a specific area of the Fastify ecosystem will be invited
to join this group by Lead Maintainers when they decide to step down from the
active contributor's group.
* [__Tommaso Allevi__](https://github.com/allevo),
<https://twitter.com/allevitommaso>, <https://www.npmjs.com/~allevo>
* [__Ethan Arrowood__](https://github.com/Ethan-Arrowood/),
<https://twitter.com/arrowoodtech>, <https://www.npmjs.com/~ethan_arrowood>
* [__Çağatay Çalı__](https://github.com/cagataycali),
<https://twitter.com/cagataycali>, <https://www.npmjs.com/~cagataycali>
* [__David Mark Clements__](https://github.com/davidmarkclements),
<https://twitter.com/davidmarkclem>,
<https://www.npmjs.com/~davidmarkclements>
* [__dalisoft__](https://github.com/dalisoft), <https://twitter.com/dalisoft>,
<https://www.npmjs.com/~dalisoft>
* [__Dustin Deus__](https://github.com/StarpTech),
<https://twitter.com/dustindeus>, <https://www.npmjs.com/~starptech>
* [__Denis Fäcke__](https://github.com/SerayaEryn),
<https://twitter.com/serayaeryn>, <https://www.npmjs.com/~serayaeryn>
* [__Rafael Gonzaga__](https://github.com/rafaelgss),
<https://twitter.com/_rafaelgss>, <https://www.npmjs.com/~rafaelgss>
* [__Trivikram Kamat__](https://github.com/trivikr),
<https://twitter.com/trivikram>, <https://www.npmjs.com/~trivikr>
* [__Ayoub El Khattabi__](https://github.com/AyoubElk),
<https://twitter.com/ayoubelkh>, <https://www.npmjs.com/~ayoubelk>
* [__Cemre Mengu__](https://github.com/cemremengu),
<https://twitter.com/cemremengu>, <https://www.npmjs.com/~cemremengu>
* [__Salman Mitha__](https://github.com/salmanm),
<https://www.npmjs.com/~salmanm>
* [__Nathan Woltman__](https://github.com/nwoltman),
<https://twitter.com/NathanWoltman>, <https://www.npmjs.com/~nwoltman>
## Hosted by
[<img
src="https://github.com/openjs-foundation/artwork/blob/main/openjs_foundation/openjs_foundation-logo-horizontal-color.png?raw=true"
width="250px;"/>](https://openjsf.org/projects)
We are an [At-Large
Project](https://github.com/openjs-foundation/cross-project-council/blob/HEAD/PROJECT_PROGRESSION.md#at-large-projects)
in the [OpenJS Foundation](https://openjsf.org/).
## Sponsors
Support this project by becoming a [SPONSOR](./SPONSORS.md)!
Fastify has an [Open Collective](https://opencollective.com/fastify)
page where we accept and manage financial contributions.
## Acknowledgments
This project is kindly sponsored by:
- [NearForm](https://nearform.com)
- [Platformatic](https://platformatic.dev)
Past Sponsors:
- [LetzDoIt](https://www.letzdoitapp.com/)
This list includes all companies that support one or more team members
in maintaining this project.
## License
Licensed under [MIT](./LICENSE).
For your convenience, here is a list of all the licenses of our production
dependencies:
- MIT
- ISC
- BSD-3-Clause
- BSD-2-Clause
[hd-link]: https://www.herodevs.com/support/fastify-nes?utm_source=fastify&utm_medium=link&utm_campaign=github_readme
[lts-link]: https://fastify.dev/docs/latest/Reference/LTS/

160
node_modules/fastify/SECURITY.md generated vendored Normal file
View File

@@ -0,0 +1,160 @@
# Security Policy
This document describes the management of vulnerabilities for the Fastify
project and its official plugins.
## Reporting vulnerabilities
Individuals who find potential vulnerabilities in Fastify are invited to
complete a vulnerability report via the dedicated pages:
1. [HackerOne](https://hackerone.com/fastify)
2. [GitHub Security Advisory](https://github.com/fastify/fastify/security/advisories/new)
### Strict measures when reporting vulnerabilities
It is of the utmost importance that you read carefully and follow these
guidelines to ensure the ecosystem as a whole isn't disrupted due to improperly
reported vulnerabilities:
* Avoid creating new "informative" reports. Only create new
reports on a vulnerability if you are absolutely sure this should be
tagged as an actual vulnerability. Third-party vendors and individuals are
tracking any new vulnerabilities reported in HackerOne or GitHub and will flag
them as such for their customers (think about snyk, npm audit, ...).
* Security reports should never be created and triaged by the same person. If
you are creating a report for a vulnerability that you found, or on
behalf of someone else, there should always be a 2nd Security Team member who
triages it. If in doubt, invite more Fastify Collaborators to help triage the
validity of the report. In any case, the report should follow the same process
as outlined below of inviting the maintainers to review and accept the
vulnerability.
* ***Do not*** attempt to show CI/CD vulnerabilities by creating new pull
requests to any of the Fastify organization's repositories. Doing so will
result in a [content report][cr] to GitHub as an unsolicited exploit.
The proper way to provide such reports is by creating a new repository,
configured in the same manner as the repository you would like to submit
a report about, and with a pull request to your own repository showing
the proof of concept.
[cr]: https://docs.github.com/en/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam#reporting-an-issue-or-pull-request
### Vulnerabilities found outside this process
⚠ The Fastify project does not support any reporting outside the process mentioned
in this document.
## Handling vulnerability reports
When a potential vulnerability is reported, the following actions are taken:
### Triage
**Delay:** 4 business days
Within 4 business days, a member of the security team provides a first answer to
the individual who submitted the potential vulnerability. The possible responses
can be:
* **Acceptance**: what was reported is considered as a new vulnerability
* **Rejection**: what was reported is not considered as a new vulnerability
* **Need more information**: the security team needs more information in order to
evaluate what was reported.
Triaging should include updating issue fields:
* Asset - set/create the module affected by the report
* Severity - TBD, currently left empty
Reference: [HackerOne: Submitting
Reports](https://docs.hackerone.com/hackers/submitting-reports.html)
### Correction follow-up
**Delay:** 90 days
When a vulnerability is confirmed, a member of the security team volunteers to
follow up on this report.
With the help of the individual who reported the vulnerability, they contact the
maintainers of the vulnerable package to make them aware of the vulnerability.
The maintainers can be invited as participants to the reported issue.
With the package maintainer, they define a release date for the publication of
the vulnerability. Ideally, this release date should not happen before the
package has been patched.
The report's vulnerable versions upper limit should be set to:
* `*` if there is no fixed version available by the time of publishing the
report.
* the last vulnerable version. For example: `<=1.2.3` if a fix exists in `1.2.4`
### Publication
**Delay:** 90 days
Within 90 days after the triage date, the vulnerability must be made public.
**Severity**: Vulnerability severity is assessed using [CVSS
v.3](https://www.first.org/cvss/user-guide). More information can be found on
[HackerOne documentation](https://docs.hackerone.com/hackers/severity.html)
If the package maintainer is actively developing a patch, an additional delay
can be added with the approval of the security team and the individual who
reported the vulnerability.
At this point, a CVE should be requested through the selected platform through
the UI, which should include the Report ID and a summary.
Within HackerOne, this is handled through a "public disclosure request".
Reference: [HackerOne:
Disclosure](https://docs.hackerone.com/hackers/disclosure.html)
## The Fastify Security team
The core team is responsible for the management of the security program and
this policy and process.
Members of this team are expected to keep all information that they have
privileged access to by being on the team completely private to the team. This
includes agreeing to not notify anyone outside the team of issues that have not
yet been disclosed publicly, including the existence of issues, expectations of
upcoming releases, and patching of any issues other than in the process of their
work as a member of the Fastify Core team.
### Members
* [__Matteo Collina__](https://github.com/mcollina),
<https://twitter.com/matteocollina>, <https://www.npmjs.com/~matteo.collina>
* [__Tomas Della Vedova__](https://github.com/delvedor),
<https://twitter.com/delvedor>, <https://www.npmjs.com/~delvedor>
* [__Vincent Le Goff__](https://github.com/zekth)
* [__KaKa Ng__](https://github.com/climba03003)
* [__James Sumners__](https://github.com/jsumners),
<https://twitter.com/jsumners79>, <https://www.npmjs.com/~jsumners>
## OpenSSF CII Best Practices
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/7585/badge)](https://bestpractices.coreinfrastructure.org/projects/7585)
There are three “tiers”: passing, silver, and gold.
### Passing
We meet 100% of the “passing” criteria.
### Silver
We meet 87% of the “silver” criteria. The gaps are as follows:
- we do not have a DCO or a CLA process for contributions.
- we do not currently document
“what the user can and cannot expect in terms of security” for our project.
- we do not currently document ”the architecture (aka high-level design)”
for our project.
### Gold
We meet 70% of the “gold” criteria. The gaps are as follows:
- we do not yet have the “silver” badge; see all the gaps above.
- We do not include a copyright or license statement in each source file.
Efforts are underway to change this archaic practice into a
suggestion instead of a hard requirement.
- There are a few unanswered questions around cryptography that are
waiting for clarification.

23
node_modules/fastify/SPONSORS.md generated vendored Normal file
View File

@@ -0,0 +1,23 @@
# Sponsors
All active sponsors of Fastify are listed here, in order of contribution!
Our sponsors are the reason why we can work on some issues or features
that otherwise would be impossible to do.
If you want to become a sponsor, please check out our [Open Collective page](https://opencollective.com/fastify)
or [GitHub Sponsors](https://github.com/sponsors/fastify)!
## Tier 4
_Be the first!_
## Tier 3
- [Mercedes-Benz Group](https://github.com/mercedes-benz)
- [Val Town, Inc.](https://opencollective.com/valtown)
- [Handsontable - JavaScript Data Grid](https://handsontable.com/docs/react-data-grid/?utm_source=Fastify_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024)
- [Lokalise - A Localization and Translation Software Tool](https://lokalise.com/?utm_source=Fastify_GH&utm_medium=sponsorship)
## Tier 2
_Be the first!_

35
node_modules/fastify/build/build-error-serializer.js generated vendored Normal file
View File

@@ -0,0 +1,35 @@
/* istanbul ignore file */
'use strict'
const FJS = require('fast-json-stringify')
const path = require('node:path')
const fs = require('node:fs')
const code = FJS({
type: 'object',
properties: {
statusCode: { type: 'number' },
code: { type: 'string' },
error: { type: 'string' },
message: { type: 'string' }
}
}, { mode: 'standalone' })
const file = path.join(__dirname, '..', 'lib', 'error-serializer.js')
const moduleCode = `// This file is autogenerated by build/build-error-serializer.js, do not edit
/* c8 ignore start */
${code}
/* c8 ignore stop */
`
/* c8 ignore start */
if (require.main === module) {
fs.writeFileSync(file, moduleCode)
console.log(`Saved ${file} file successfully`)
} else {
module.exports = {
code: moduleCode
}
}
/* c8 ignore stop */

168
node_modules/fastify/build/build-validation.js generated vendored Normal file
View File

@@ -0,0 +1,168 @@
'use strict'
const AjvStandaloneCompiler = require('@fastify/ajv-compiler/standalone')
const { _ } = require('ajv')
const fs = require('node:fs')
const path = require('node:path')
const factory = AjvStandaloneCompiler({
readMode: false,
storeFunction (routeOpts, schemaValidationCode) {
const moduleCode = `// This file is autogenerated by build/build-validation.js, do not edit
/* c8 ignore start */
${schemaValidationCode}
module.exports.defaultInitOptions = ${JSON.stringify(defaultInitOptions)}
/* c8 ignore stop */
`
const file = path.join(__dirname, '..', 'lib', 'configValidator.js')
fs.writeFileSync(file, moduleCode)
console.log(`Saved ${file} file successfully`)
}
})
const defaultInitOptions = {
connectionTimeout: 0, // 0 sec
keepAliveTimeout: 72000, // 72 seconds
forceCloseConnections: undefined, // keep-alive connections
maxRequestsPerSocket: 0, // no limit
requestTimeout: 0, // no limit
bodyLimit: 1024 * 1024, // 1 MiB
caseSensitive: true,
allowUnsafeRegex: false,
disableRequestLogging: false,
ignoreTrailingSlash: false,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
onProtoPoisoning: 'error',
onConstructorPoisoning: 'error',
pluginTimeout: 10000,
requestIdHeader: false,
requestIdLogLabel: 'reqId',
http2SessionTimeout: 72000, // 72 seconds
exposeHeadRoutes: true,
useSemicolonDelimiter: false,
allowErrorHandlerOverride: true, // TODO: set to false in v6
routerOptions: {
ignoreTrailingSlash: false,
ignoreDuplicateSlashes: false,
maxParamLength: 100,
allowUnsafeRegex: false,
useSemicolonDelimiter: false
}
}
const schema = {
type: 'object',
additionalProperties: false,
properties: {
connectionTimeout: { type: 'integer', default: defaultInitOptions.connectionTimeout },
keepAliveTimeout: { type: 'integer', default: defaultInitOptions.keepAliveTimeout },
forceCloseConnections: {
oneOf: [
{
type: 'string',
pattern: 'idle'
},
{
type: 'boolean'
}
]
},
maxRequestsPerSocket: { type: 'integer', default: defaultInitOptions.maxRequestsPerSocket, nullable: true },
requestTimeout: { type: 'integer', default: defaultInitOptions.requestTimeout },
bodyLimit: { type: 'integer', default: defaultInitOptions.bodyLimit },
caseSensitive: { type: 'boolean', default: defaultInitOptions.caseSensitive },
allowUnsafeRegex: { type: 'boolean', default: defaultInitOptions.allowUnsafeRegex },
http2: { type: 'boolean' },
https: {
if: {
not: {
oneOf: [
{ type: 'boolean' },
{ type: 'null' },
{
type: 'object',
additionalProperties: false,
required: ['allowHTTP1'],
properties: {
allowHTTP1: { type: 'boolean' }
}
}
]
}
},
then: { setDefaultValue: true }
},
ignoreTrailingSlash: { type: 'boolean', default: defaultInitOptions.ignoreTrailingSlash },
ignoreDuplicateSlashes: { type: 'boolean', default: defaultInitOptions.ignoreDuplicateSlashes },
disableRequestLogging: {
type: 'boolean',
default: false
},
maxParamLength: { type: 'integer', default: defaultInitOptions.maxParamLength },
onProtoPoisoning: { type: 'string', default: defaultInitOptions.onProtoPoisoning },
onConstructorPoisoning: { type: 'string', default: defaultInitOptions.onConstructorPoisoning },
pluginTimeout: { type: 'integer', default: defaultInitOptions.pluginTimeout },
requestIdHeader: { anyOf: [{ type: 'boolean' }, { type: 'string' }], default: defaultInitOptions.requestIdHeader },
requestIdLogLabel: { type: 'string', default: defaultInitOptions.requestIdLogLabel },
http2SessionTimeout: { type: 'integer', default: defaultInitOptions.http2SessionTimeout },
exposeHeadRoutes: { type: 'boolean', default: defaultInitOptions.exposeHeadRoutes },
useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.useSemicolonDelimiter },
routerOptions: {
type: 'object',
additionalProperties: false,
properties: {
ignoreTrailingSlash: { type: 'boolean', default: defaultInitOptions.routerOptions.ignoreTrailingSlash },
ignoreDuplicateSlashes: { type: 'boolean', default: defaultInitOptions.routerOptions.ignoreDuplicateSlashes },
maxParamLength: { type: 'integer', default: defaultInitOptions.routerOptions.maxParamLength },
allowUnsafeRegex: { type: 'boolean', default: defaultInitOptions.routerOptions.allowUnsafeRegex },
useSemicolonDelimiter: { type: 'boolean', default: defaultInitOptions.routerOptions.useSemicolonDelimiter }
}
},
constraints: {
type: 'object',
additionalProperties: {
type: 'object',
required: ['name', 'storage', 'validate', 'deriveConstraint'],
additionalProperties: true,
properties: {
name: { type: 'string' },
storage: {},
validate: {},
deriveConstraint: {}
}
}
}
}
}
const compiler = factory({}, {
customOptions: {
code: {
source: true,
lines: true,
optimize: 3
},
removeAdditional: true,
useDefaults: true,
coerceTypes: true,
keywords: [
{
keyword: 'setDefaultValue',
$data: true,
// error: false,
modifying: true,
valid: true,
code (keywordCxt) {
const { gen, it, schemaValue } = keywordCxt
const logicCode = gen.assign(_`${it.parentData}[${it.parentDataProperty}]`, schemaValue)
return logicCode
}
}
]
}
})
compiler({ schema })

11
node_modules/fastify/build/sync-version.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
'use strict'
const fs = require('node:fs')
const path = require('node:path')
// package.json:version -> fastify.js:VERSION
const { version } = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')).toString('utf8'))
const fastifyJs = path.join(__dirname, '..', 'fastify.js')
fs.writeFileSync(fastifyJs, fs.readFileSync(fastifyJs).toString('utf8').replace(/const\s*VERSION\s*=.*/, `const VERSION = '${version}'`))

60
node_modules/fastify/docs/Guides/Benchmarking.md generated vendored Normal file
View File

@@ -0,0 +1,60 @@
<h1 align="center">Fastify</h1>
## Benchmarking
Benchmarking is important if you want to measure how a change can affect your
application's performance. We provide a simple way to benchmark your
application from the point of view of a user and contributor. The setup allows
you to automate benchmarks in different branches and on different Node.js
versions.
The modules we will use:
- [Autocannon](https://github.com/mcollina/autocannon): An HTTP/1.1 benchmarking
tool written in node.
- [Branch-comparer](https://github.com/StarpTech/branch-comparer): Checkout
multiple git branches, execute scripts, and log the results.
- [Concurrently](https://github.com/open-cli-tools/concurrently): Run commands
concurrently.
- [Npx](https://github.com/npm/npx): NPM package runner used to run scripts
against different Node.js Versions and execute local binaries. Shipped with
npm@5.2.0.
## Simple
### Run the test in the current branch
```sh
npm run benchmark
```
### Run the test against different Node.js versions ✨
```sh
npx -p node@10 -- npm run benchmark
```
## Advanced
### Run the test in different branches
```sh
branchcmp --rounds 2 --script "npm run benchmark"
```
### Run the test in different branches against different Node.js versions ✨
```sh
branchcmp --rounds 2 --script "npm run benchmark"
```
### Compare current branch with main (Gitflow)
```sh
branchcmp --rounds 2 --gitflow --script "npm run benchmark"
```
or
```sh
npm run bench
```
### Run different examples
<!-- markdownlint-disable -->
```sh
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/\""
```
<!-- markdownlint-enable -->

321
node_modules/fastify/docs/Guides/Database.md generated vendored Normal file
View File

@@ -0,0 +1,321 @@
<h1 align="center">Fastify</h1>
## Database
Fastify's ecosystem provides a handful of
plugins for connecting to various database engines.
This guide covers engines that have Fastify
plugins maintained within the Fastify organization.
> If a plugin for your database of choice does not exist
> you can still use the database as Fastify is database agnostic.
> By following the examples of the database plugins listed in this guide,
> a plugin can be written for the missing database engine.
> If you would like to write your own Fastify plugin
> please take a look at the [plugins guide](./Plugins-Guide.md)
### [MySQL](https://github.com/fastify/fastify-mysql)
Install the plugin by running `npm i @fastify/mysql`.
*Usage:*
```javascript
const fastify = require('fastify')()
fastify.register(require('@fastify/mysql'), {
connectionString: 'mysql://root@localhost/mysql'
})
fastify.get('/user/:id', function(req, reply) {
fastify.mysql.query(
'SELECT id, username, hash, salt FROM users WHERE id=?', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})
fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```
### [Postgres](https://github.com/fastify/fastify-postgres)
Install the plugin by running `npm i pg @fastify/postgres`.
*Example*:
```javascript
const fastify = require('fastify')()
fastify.register(require('@fastify/postgres'), {
connectionString: 'postgres://postgres@localhost/postgres'
})
fastify.get('/user/:id', function (req, reply) {
fastify.pg.query(
'SELECT id, username, hash, salt FROM users WHERE id=$1', [req.params.id],
function onResult (err, result) {
reply.send(err || result)
}
)
})
fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```
### [Redis](https://github.com/fastify/fastify-redis)
Install the plugin by running `npm i @fastify/redis`
*Usage:*
```javascript
'use strict'
const fastify = require('fastify')()
fastify.register(require('@fastify/redis'), { host: '127.0.0.1' })
// or
fastify.register(require('@fastify/redis'), { url: 'redis://127.0.0.1', /* other redis options */ })
fastify.get('/foo', function (req, reply) {
const { redis } = fastify
redis.get(req.query.key, (err, val) => {
reply.send(err || val)
})
})
fastify.post('/foo', function (req, reply) {
const { redis } = fastify
redis.set(req.body.key, req.body.value, (err) => {
reply.send(err || { status: 'ok' })
})
})
fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```
By default `@fastify/redis` doesn't close
the client connection when Fastify server shuts down.
To opt-in to this behavior, register the client like so:
```javascript
fastify.register(require('@fastify/redis'), {
client: redis,
closeClient: true
})
```
### [Mongo](https://github.com/fastify/fastify-mongodb)
Install the plugin by running `npm i @fastify/mongodb`
*Usage:*
```javascript
const fastify = require('fastify')()
fastify.register(require('@fastify/mongodb'), {
// force to close the mongodb connection when app stopped
// the default value is false
forceClose: true,
url: 'mongodb://mongo/mydb'
})
fastify.get('/user/:id', async function (req, reply) {
// Or this.mongo.client.db('mydb').collection('users')
const users = this.mongo.db.collection('users')
// if the id is an ObjectId format, you need to create a new ObjectId
const id = this.mongo.ObjectId(req.params.id)
try {
const user = await users.findOne({ id })
return user
} catch (err) {
return err
}
})
fastify.listen({ port: 3000 }, err => {
if (err) throw err
})
```
### [LevelDB](https://github.com/fastify/fastify-leveldb)
Install the plugin by running `npm i @fastify/leveldb`
*Usage:*
```javascript
const fastify = require('fastify')()
fastify.register(
require('@fastify/leveldb'),
{ name: 'db' }
)
fastify.get('/foo', async function (req, reply) {
const val = await this.level.db.get(req.query.key)
return val
})
fastify.post('/foo', async function (req, reply) {
await this.level.db.put(req.body.key, req.body.value)
return { status: 'ok' }
})
fastify.listen({ port: 3000 }, err => {
if (err) throw err
console.log(`server listening on ${fastify.server.address().port}`)
})
```
### Writing plugin for a database library
We could write a plugin for a database
library too (e.g. Knex, Prisma, or TypeORM).
We will use [Knex](https://knexjs.org/) in our example.
```javascript
'use strict'
const fp = require('fastify-plugin')
const knex = require('knex')
function knexPlugin(fastify, options, done) {
if(!fastify.knex) {
const knex = knex(options)
fastify.decorate('knex', knex)
fastify.addHook('onClose', (fastify, done) => {
if (fastify.knex === knex) {
fastify.knex.destroy(done)
}
})
}
done()
}
export default fp(knexPlugin, { name: 'fastify-knex-example' })
```
### Writing a plugin for a database engine
In this example, we will create a basic Fastify MySQL plugin from scratch (it is
a stripped-down example, please use the official plugin in production).
```javascript
const fp = require('fastify-plugin')
const mysql = require('mysql2/promise')
function fastifyMysql(fastify, options, done) {
const connection = mysql.createConnection(options)
if (!fastify.mysql) {
fastify.decorate('mysql', connection)
}
fastify.addHook('onClose', (fastify, done) => connection.end().then(done).catch(done))
done()
}
export default fp(fastifyMysql, { name: 'fastify-mysql-example' })
```
### Migrations
Database schema migrations are an integral part of database management and
development. Migrations provide a repeatable and testable way to modify a
database's schema and prevent data loss.
As stated at the beginning of the guide, Fastify is database agnostic and any
Node.js database migration tool can be used with it. We will give an example of
using [Postgrator](https://www.npmjs.com/package/postgrator) which has support
for Postgres, MySQL, SQL Server and SQLite. For MongoDB migrations, please check
[migrate-mongo](https://www.npmjs.com/package/migrate-mongo).
#### [Postgrator](https://www.npmjs.com/package/postgrator)
Postgrator is Node.js SQL migration tool that uses a directory of SQL scripts to
alter the database schema. Each file in a migrations folder needs to follow the
pattern: ` [version].[action].[optional-description].sql`.
**version:** must be an incrementing number (e.g. `001` or a timestamp).
**action:** should be `do` or `undo`. `do` implements the version, `undo`
reverts it. Think about it like `up` and `down` in other migration tools.
**optional-description** describes which changes migration makes. Although
optional, it should be used for all migrations as it makes it easier for
everyone to know which changes are made in a migration.
In our example, we are going to have a single migration that creates a `users`
table and we are going to use `Postgrator` to run the migration.
> Run `npm i pg postgrator` to install dependencies needed for the
> example.
```sql
// 001.do.create-users-table.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY NOT NULL,
created_at DATE NOT NULL DEFAULT CURRENT_DATE,
firstName TEXT NOT NULL,
lastName TEXT NOT NULL
);
```
```javascript
const pg = require('pg')
const Postgrator = require('postgrator')
const path = require('node:path')
async function migrate() {
const client = new pg.Client({
host: 'localhost',
port: 5432,
database: 'example',
user: 'example',
password: 'example',
});
try {
await client.connect();
const postgrator = new Postgrator({
migrationPattern: path.join(__dirname, '/migrations/*'),
driver: 'pg',
database: 'example',
schemaTable: 'migrations',
currentSchema: 'public', // Postgres and MS SQL Server only
execQuery: (query) => client.query(query),
});
const result = await postgrator.migrate()
if (result.length === 0) {
console.log(
'No migrations run for schema "public". Already at the latest one.'
)
}
console.log('Migration done.')
process.exitCode = 0
} catch(err) {
console.error(err)
process.exitCode = 1
}
await client.end()
}
migrate()
```

View File

@@ -0,0 +1,608 @@
<h1 align="center">Fastify</h1>
# Delay Accepting Requests
## Introduction
Fastify provides several [hooks](../Reference/Hooks.md) useful for a variety of
situations. One of them is the [`onReady`](../Reference/Hooks.md#onready) hook,
which is useful for executing tasks *right before* the server starts accepting
new requests. There isn't, though, a direct mechanism to handle scenarios in
which you'd like the server to start accepting **specific** requests and denying
all others, at least up to some point.
Say, for instance, your server needs to authenticate with an OAuth provider to
start serving requests. To do that it'd need to engage in the [OAuth
Authorization Code
Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow),
which would require it to listen to two requests from the authentication
provider:
1. the Authorization Code webhook
2. the tokens webhook
Until the authorization flow is done you wouldn't be able to serve customer
requests. What to do then?
There are several solutions for achieving that kind of behavior. Here we'll
introduce one of such techniques and, hopefully, you'll be able to get things
rolling asap!
## Solution
### Overview
The proposed solution is one of many possible ways of dealing with this scenario
and many similar to it. It relies solely on Fastify, so no fancy infrastructure
tricks or third-party libraries will be necessary.
To simplify things we won't be dealing with a precise OAuth flow but, instead,
simulate a scenario in which some key is needed to serve a request and that key
can only be retrieved in runtime by authenticating with an external provider.
The main goal here is to deny requests that would otherwise fail **as early as
possible** and with some **meaningful context**. That's both useful for the
server (fewer resources allocated to a bound-to-fail task) and for the client
(they get some meaningful information and don't need to wait long for it).
That will be achieved by wrapping into a custom plugin two main features:
1. the mechanism for authenticating with the provider
[decorating](../Reference/Decorators.md) the `fastify` object with the
authentication key (`magicKey` from here onward)
1. the mechanism for denying requests that would, otherwise, fail
### Hands-on
For this sample solution we'll be using the following:
- `node.js v16.14.2`
- `npm 8.5.0`
- `fastify 4.0.0-rc.1`
- `fastify-plugin 3.0.1`
- `undici 5.0.0`
Say we have the following base server set up at first:
```js
const Fastify = require('fastify')
const provider = require('./provider')
const server = Fastify({ logger: true })
const USUAL_WAIT_TIME_MS = 5000
server.get('/ping', function (request, reply) {
reply.send({ error: false, ready: request.server.magicKey !== null })
})
server.post('/webhook', function (request, reply) {
// It's good practice to validate webhook requests come from
// who you expect. This is skipped in this sample for the sake
// of simplicity
const { magicKey } = request.body
request.server.magicKey = magicKey
request.log.info('Ready for customer requests!')
reply.send({ error: false })
})
server.get('/v1*', async function (request, reply) {
try {
const data = await provider.fetchSensitiveData(request.server.magicKey)
return { customer: true, error: false }
} catch (error) {
request.log.error({
error,
message: 'Failed at fetching sensitive data from provider',
})
reply.statusCode = 500
return { customer: null, error: true }
}
})
server.decorate('magicKey')
server.listen({ port: '1234' }, () => {
provider.thirdPartyMagicKeyGenerator(USUAL_WAIT_TIME_MS)
.catch((error) => {
server.log.error({
error,
message: 'Got an error while trying to get the magic key!'
})
// Since we won't be able to serve requests, might as well wrap
// things up
server.close(() => process.exit(1))
})
})
```
Our code is simply setting up a Fastify server with a few routes:
- a `/ping` route that specifies whether the service is ready or not to serve
requests by checking if the `magicKey` has been set up
- a `/webhook` endpoint for our provider to reach back to us when they're ready
to share the `magicKey`. The `magicKey` is, then, saved into the previously set
decorator on the `fastify` object
- a catchall `/v1*` route to simulate what would have been customer-initiated
requests. These requests rely on us having a valid `magicKey`
The `provider.js` file, simulating actions of an external provider, is as
follows:
```js
const { fetch } = require('undici')
const { setTimeout } = require('node:timers/promises')
const MAGIC_KEY = '12345'
const delay = setTimeout
exports.thirdPartyMagicKeyGenerator = async (ms) => {
// Simulate processing delay
await delay(ms)
// Simulate webhook request to our server
const { status } = await fetch(
'http://localhost:1234/webhook',
{
body: JSON.stringify({ magicKey: MAGIC_KEY }),
method: 'POST',
headers: {
'content-type': 'application/json',
},
},
)
if (status !== 200) {
throw new Error('Failed to fetch magic key')
}
}
exports.fetchSensitiveData = async (key) => {
// Simulate processing delay
await delay(700)
const data = { sensitive: true }
if (key === MAGIC_KEY) {
return data
}
throw new Error('Invalid key')
}
```
The most important snippet here is the `thirdPartyMagicKeyGenerator` function,
which will wait for 5 seconds and, then, make the POST request to our `/webhook`
endpoint.
When our server spins up we start listening to new connections without having
our `magicKey` set up. Until we receive the webhook request from our external
provider (in this example we're simulating a 5 second delay) all our requests
under the `/v1*` path (customer requests) will fail. Worse than that: they'll
fail after we've reached out to our provider with an invalid key and got an
error from them. That wasted time and resources for us and our customers.
Depending on the kind of application we're running and on the request rate we're
expecting this delay is not acceptable or, at least, very annoying.
Of course, that could be simply mitigated by checking whether or not the
`magicKey` has been set up before hitting the provider in the `/v1*` handler.
Sure, but that would lead to bloat in the code. And imagine we have dozens of
different routes, with different controllers, that require that key. Should we
repeatedly add that check to all of them? That's error-prone and there are more
elegant solutions.
What we'll do to improve this setup overall is create a
[`Plugin`](../Reference/Plugins.md) that'll be solely responsible for making
sure we both:
- do not accept requests that would otherwise fail until we're ready for them
- make sure we reach out to our provider as soon as possible
This way we'll make sure all our setup regarding this specific _business rule_
is placed on a single entity, instead of scattered all across our code base.
With the changes to improve this behavior, the code will look like this:
##### index.js
```js
const Fastify = require('fastify')
const customerRoutes = require('./customer-routes')
const { setup, delay } = require('./delay-incoming-requests')
const server = new Fastify({ logger: true })
server.register(setup)
// Non-blocked URL
server.get('/ping', function (request, reply) {
reply.send({ error: false, ready: request.server.magicKey !== null })
})
// Webhook to handle the provider's response - also non-blocked
server.post('/webhook', function (request, reply) {
// It's good practice to validate webhook requests really come from
// whoever you expect. This is skipped in this sample for the sake
// of simplicity
const { magicKey } = request.body
request.server.magicKey = magicKey
request.log.info('Ready for customer requests!')
reply.send({ error: false })
})
// Blocked URLs
// Mind we're building a new plugin by calling the `delay` factory with our
// customerRoutes plugin
server.register(delay(customerRoutes), { prefix: '/v1' })
server.listen({ port: '1234' })
```
##### provider.js
```js
const { fetch } = require('undici')
const { setTimeout } = require('node:timers/promises')
const MAGIC_KEY = '12345'
const delay = setTimeout
exports.thirdPartyMagicKeyGenerator = async (ms) => {
// Simulate processing delay
await delay(ms)
// Simulate webhook request to our server
const { status } = await fetch(
'http://localhost:1234/webhook',
{
body: JSON.stringify({ magicKey: MAGIC_KEY }),
method: 'POST',
headers: {
'content-type': 'application/json',
},
},
)
if (status !== 200) {
throw new Error('Failed to fetch magic key')
}
}
exports.fetchSensitiveData = async (key) => {
// Simulate processing delay
await delay(700)
const data = { sensitive: true }
if (key === MAGIC_KEY) {
return data
}
throw new Error('Invalid key')
}
```
##### delay-incoming-requests.js
```js
const fp = require('fastify-plugin')
const provider = require('./provider')
const USUAL_WAIT_TIME_MS = 5000
async function setup(fastify) {
// As soon as we're listening for requests, let's work our magic
fastify.server.on('listening', doMagic)
// Set up the placeholder for the magicKey
fastify.decorate('magicKey')
// Our magic -- important to make sure errors are handled. Beware of async
// functions outside `try/catch` blocks
// If an error is thrown at this point and not captured it'll crash the
// application
function doMagic() {
fastify.log.info('Doing magic!')
provider.thirdPartyMagicKeyGenerator(USUAL_WAIT_TIME_MS)
.catch((error) => {
fastify.log.error({
error,
message: 'Got an error while trying to get the magic key!'
})
// Since we won't be able to serve requests, might as well wrap
// things up
fastify.close(() => process.exit(1))
})
}
}
const delay = (routes) =>
function (fastify, opts, done) {
// Make sure customer requests won't be accepted if the magicKey is not
// available
fastify.addHook('onRequest', function (request, reply, next) {
if (!request.server.magicKey) {
reply.statusCode = 503
reply.header('Retry-After', USUAL_WAIT_TIME_MS)
reply.send({ error: true, retryInMs: USUAL_WAIT_TIME_MS })
}
next()
})
// Register to-be-delayed routes
fastify.register(routes, opts)
done()
}
module.exports = {
setup: fp(setup),
delay,
}
```
##### customer-routes.js
```js
const fp = require('fastify-plugin')
const provider = require('./provider')
module.exports = fp(async function (fastify) {
fastify.get('*', async function (request ,reply) {
try {
const data = await provider.fetchSensitiveData(request.server.magicKey)
return { customer: true, error: false }
} catch (error) {
request.log.error({
error,
message: 'Failed at fetching sensitive data from provider',
})
reply.statusCode = 500
return { customer: null, error: true }
}
})
})
```
There is a very specific change on the previously existing files that is worth
mentioning: Beforehand we were using the `server.listen` callback to start the
authentication process with the external provider and we were decorating the
`server` object right before initializing the server. That was bloating our
server initialization setup with unnecessary code and didn't have much to do
with starting the Fastify server. It was a business logic that didn't have its
specific place in the code base.
Now we've implemented the `delayIncomingRequests` plugin in the
`delay-incoming-requests.js` file. That's, in truth, a module split into two
different plugins that will build up to a single use-case. That's the brains of
our operation. Let's walk through what the plugins do:
##### setup
The `setup` plugin is responsible for making sure we reach out to our provider
asap and store the `magicKey` somewhere available to all our handlers.
```js
fastify.server.on('listening', doMagic)
```
As soon as the server starts listening (very similar behavior to adding a piece
of code to the `server.listen`'s callback function) a `listening` event is
emitted (for more info refer to
https://nodejs.org/api/net.html#event-listening). We use that to reach out to
our provider as soon as possible, with the `doMagic` function.
```js
fastify.decorate('magicKey')
```
The `magicKey` decoration is also part of the plugin now. We initialize it with
a placeholder, waiting for the valid value to be retrieved.
##### delay
`delay` is not a plugin itself. It's actually a plugin *factory*. It expects a
Fastify plugin with `routes` and exports the actual plugin that'll handle
enveloping those routes with an `onRequest` hook that will make sure no requests
are handled until we're ready for them.
```js
const delay = (routes) =>
function (fastify, opts, done) {
// Make sure customer requests won't be accepted if the magicKey is not
// available
fastify.addHook('onRequest', function (request, reply, next) {
if (!request.server.magicKey) {
reply.statusCode = 503
reply.header('Retry-After', USUAL_WAIT_TIME_MS)
reply.send({ error: true, retryInMs: USUAL_WAIT_TIME_MS })
}
next()
})
// Register to-be-delayed routes
fastify.register(routes, opts)
done()
}
```
Instead of updating every single controller that might use the `magicKey`, we
simply make sure that no route that's related to customer requests will be
served until we have everything ready. And there's more: we fail **FAST** and
have the possibility of giving the customer meaningful information, like how
long they should wait before retrying the request. Going even further, by
issuing a [`503` status
code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503) we're
signaling to our infrastructure components (namely load balancers) that we're
still not ready to take incoming requests and they should redirect traffic to
other instances, if available. Additionally, we are providing a `Retry-After`
header with the time in milliseconds the client should wait before retrying.
It's noteworthy that we didn't use the `fastify-plugin` wrapper in the `delay`
factory. That's because we wanted the `onRequest` hook to only be set within
that specific scope and not to the scope that called it (in our case, the main
`server` object defined in `index.js`). `fastify-plugin` sets the
`skip-override` hidden property, which has a practical effect of making whatever
changes we make to our `fastify` object available to the upper scope. That's
also why we used it with the `customerRoutes` plugin: we wanted those routes to
be available to its calling scope, the `delay` plugin. For more info on that
subject refer to [Plugins](../Reference/Plugins.md#handle-the-scope).
Let's see how that behaves in action. If we fired our server up with `node
index.js` and made a few requests to test things out. These were the logs we'd
see (some bloat was removed to ease things up):
<!-- markdownlint-disable -->
```sh
{"time":1650063793316,"msg":"Doing magic!"}
{"time":1650063793316,"msg":"Server listening at http://127.0.0.1:1234"}
{"time":1650063795030,"reqId":"req-1","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51928},"msg":"incoming request"}
{"time":1650063795033,"reqId":"req-1","res":{"statusCode":503},"responseTime":2.5721680000424385,"msg":"request completed"}
{"time":1650063796248,"reqId":"req-2","req":{"method":"GET","url":"/ping","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51930},"msg":"incoming request"}
{"time":1650063796248,"reqId":"req-2","res":{"statusCode":200},"responseTime":0.4802369996905327,"msg":"request completed"}
{"time":1650063798377,"reqId":"req-3","req":{"method":"POST","url":"/webhook","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51932},"msg":"incoming request"}
{"time":1650063798379,"reqId":"req-3","msg":"Ready for customer requests!"}
{"time":1650063798379,"reqId":"req-3","res":{"statusCode":200},"responseTime":1.3567829988896847,"msg":"request completed"}
{"time":1650063799858,"reqId":"req-4","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51934},"msg":"incoming request"}
{"time":1650063800561,"reqId":"req-4","res":{"statusCode":200},"responseTime":702.4662979990244,"msg":"request completed"}
```
<!-- markdownlint-enable -->
Let's focus on a few parts:
```sh
{"time":1650063793316,"msg":"Doing magic!"}
{"time":1650063793316,"msg":"Server listening at http://127.0.0.1:1234"}
```
These are the initial logs we'd see as soon as the server started. We reach out
to the external provider as early as possible within a valid time window (we
couldn't do that before the server was ready to receive connections).
While the server is still not ready, a few requests are attempted:
<!-- markdownlint-disable -->
```sh
{"time":1650063795030,"reqId":"req-1","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51928},"msg":"incoming request"}
{"time":1650063795033,"reqId":"req-1","res":{"statusCode":503},"responseTime":2.5721680000424385,"msg":"request completed"}
{"time":1650063796248,"reqId":"req-2","req":{"method":"GET","url":"/ping","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51930},"msg":"incoming request"}
{"time":1650063796248,"reqId":"req-2","res":{"statusCode":200},"responseTime":0.4802369996905327,"msg":"request completed"}
```
<!-- markdownlint-enable -->
The first one (`req-1`) was a `GET /v1`, that failed (**FAST** - `responseTime`
is in `ms`) with our `503` status code and the meaningful information in the
response. Below is the response for that request:
```sh
HTTP/1.1 503 Service Unavailable
Connection: keep-alive
Content-Length: 31
Content-Type: application/json; charset=utf-8
Date: Fri, 15 Apr 2022 23:03:15 GMT
Keep-Alive: timeout=5
Retry-After: 5000
{
"error": true,
"retryInMs": 5000
}
```
Then we attempted a new request (`req-2`), which was a `GET /ping`. As expected,
since that was not one of the requests we asked our plugin to filter, it
succeeded. That could also be used as a means of informing an interested party
whether or not we were ready to serve requests with the `ready` field. Although
`/ping` is more commonly associated with *liveness* checks and that would be
the responsibility of a *readiness* check. The curious reader can get more info
on these terms in the article
["Kubernetes best practices: Setting up health checks with readiness and liveness probes"](
https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes).
Below is the response to that request:
```sh
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 29
Content-Type: application/json; charset=utf-8
Date: Fri, 15 Apr 2022 23:03:16 GMT
Keep-Alive: timeout=5
{
"error": false,
"ready": false
}
```
After that, there were more interesting log messages:
<!-- markdownlint-disable -->
```sh
{"time":1650063798377,"reqId":"req-3","req":{"method":"POST","url":"/webhook","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51932},"msg":"incoming request"}
{"time":1650063798379,"reqId":"req-3","msg":"Ready for customer requests!"}
{"time":1650063798379,"reqId":"req-3","res":{"statusCode":200},"responseTime":1.3567829988896847,"msg":"request completed"}
```
<!-- markdownlint-enable -->
This time it was our simulated external provider hitting us to let us know
authentication had gone well and telling us what our `magicKey` was. We saved
that into our `magicKey` decorator and celebrated with a log message saying we
were now ready for customers to hit us!
<!-- markdownlint-disable -->
```sh
{"time":1650063799858,"reqId":"req-4","req":{"method":"GET","url":"/v1","hostname":"localhost:1234","remoteAddress":"127.0.0.1","remotePort":51934},"msg":"incoming request"}
{"time":1650063800561,"reqId":"req-4","res":{"statusCode":200},"responseTime":702.4662979990244,"msg":"request completed"}
```
<!-- markdownlint-enable -->
Finally, a final `GET /v1` request was made and, this time, it succeeded. Its
response was the following:
```sh
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 31
Content-Type: application/json; charset=utf-8
Date: Fri, 15 Apr 2022 23:03:20 GMT
Keep-Alive: timeout=5
{
"customer": true,
"error": false
}
```
## Conclusion
Specifics of the implementation will vary from one problem to another, but the
main goal of this guide was to show a very specific use case of an issue that
could be solved within Fastify's ecosystem.
This guide is a tutorial on the use of plugins, decorators, and hooks to solve
the problem of delaying serving specific requests on our application. It's not
production-ready, as it keeps local state (the `magicKey`) and it's not
horizontally scalable (we don't want to flood our provider, right?). One way of
improving it would be storing the `magicKey` somewhere else (perhaps a cache
database?).
The keywords here were [Decorators](../Reference/Decorators.md),
[Hooks](../Reference/Hooks.md), and [Plugins](../Reference/Plugins.md).
Combining what Fastify has to offer can lead to very ingenious and creative
solutions to a wide variety of problems. Let's be creative! :)

View File

@@ -0,0 +1,172 @@
<h1 align="center">Fastify</h1>
# Detecting When Clients Abort
## Introduction
Fastify provides request events to trigger at certain points in a request's
lifecycle. However, there isn't a built-in mechanism to
detect unintentional client disconnection scenarios such as when the client's
internet connection is interrupted. This guide covers methods to detect if
and when a client intentionally aborts a request.
Keep in mind, Fastify's `clientErrorHandler` is not designed to detect when a
client aborts a request. This works in the same way as the standard Node HTTP
module, which triggers the `clientError` event when there is a bad request or
exceedingly large header data. When a client aborts a request, there is no
error on the socket and the `clientErrorHandler` will not be triggered.
## Solution
### Overview
The proposed solution is a possible way of detecting when a client
intentionally aborts a request, such as when a browser is closed or the HTTP
request is aborted from your client application. If there is an error in your
application code that results in the server crashing, you may require
additional logic to avoid a false abort detection.
The goal here is to detect when a client intentionally aborts a connection
so your application logic can proceed accordingly. This can be useful for
logging purposes or halting business logic.
### Hands-on
Say we have the following base server set up:
```js
import Fastify from 'fastify';
const sleep = async (time) => {
return await new Promise(resolve => setTimeout(resolve, time || 1000));
}
const app = Fastify({
logger: {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
})
app.addHook('onRequest', async (request, reply) => {
request.raw.on('close', () => {
if (request.raw.aborted) {
app.log.info('request closed')
}
})
})
app.get('/', async (request, reply) => {
await sleep(3000)
reply.code(200).send({ ok: true })
})
const start = async () => {
try {
await app.listen({ port: 3000 })
} catch (err) {
app.log.error(err)
process.exit(1)
}
}
start()
```
Our code is setting up a Fastify server which includes the following
functionality:
- Accepting requests at http://localhost:3000, with a 3 second delayed response
of `{ ok: true }`.
- An onRequest hook that triggers when every request is received.
- Logic that triggers in the hook when the request is closed.
- Logging that occurs when the closed request property `aborted` is true.
Whilst the `aborted` property has been deprecated, `destroyed` is not a
suitable replacement as the
[Node.js documentation suggests](https://nodejs.org/api/http.html#requestaborted).
A request can be `destroyed` for various reasons, such as when the server closes
the connection. The `aborted` property is still the most reliable way to detect
when a client intentionally aborts a request.
You can also perform this logic outside of a hook, directly in a specific route.
```js
app.get('/', async (request, reply) => {
request.raw.on('close', () => {
if (request.raw.aborted) {
app.log.info('request closed')
}
})
await sleep(3000)
reply.code(200).send({ ok: true })
})
```
At any point in your business logic, you can check if the request has been
aborted and perform alternative actions.
```js
app.get('/', async (request, reply) => {
await sleep(3000)
if (request.raw.aborted) {
// do something here
}
await sleep(3000)
reply.code(200).send({ ok: true })
})
```
A benefit to adding this in your application code is that you can log Fastify
details such as the reqId, which may be unavailable in lower-level code that
only has access to the raw request information.
### Testing
To test this functionality you can use an app like Postman and cancel your
request within 3 seconds. Alternatively, you can use Node to send an HTTP
request with logic to abort the request before 3 seconds. Example:
```js
const controller = new AbortController();
const signal = controller.signal;
(async () => {
try {
const response = await fetch('http://localhost:3000', { signal });
const body = await response.text();
console.log(body);
} catch (error) {
console.error(error);
}
})();
setTimeout(() => {
controller.abort()
}, 1000);
```
With either approach, you should see the Fastify log appear at the moment the
request is aborted.
## Conclusion
Specifics of the implementation will vary from one problem to another, but the
main goal of this guide was to show a very specific use case of an issue that
could be solved within Fastify's ecosystem.
You can listen to the request close event and determine if the request was
aborted or if it was successfully delivered. You can implement this solution
in an onRequest hook or directly in an individual route.
This approach will not trigger in the event of internet disruption, and such
detection would require additional business logic. If you have flawed backend
application logic that results in a server crash, then you could trigger a
false detection. The `clientErrorHandler`, either by default or with custom
logic, is not intended to handle this scenario and will not trigger when the
client aborts a request.

765
node_modules/fastify/docs/Guides/Ecosystem.md generated vendored Normal file
View File

@@ -0,0 +1,765 @@
<h1 align="center">Fastify</h1>
## Ecosystem
Plugins maintained by the Fastify team are listed under [Core](#core) while
plugins maintained by the community are listed in the [Community](#community)
section.
#### [Core](#core)
- [`@fastify/accepts`](https://github.com/fastify/fastify-accepts) to have
[accepts](https://www.npmjs.com/package/accepts) in your request object.
- [`@fastify/accepts-serializer`](https://github.com/fastify/fastify-accepts-serializer)
to serialize to output according to the `Accept` header.
- [`@fastify/auth`](https://github.com/fastify/fastify-auth) Run multiple auth
functions in Fastify.
- [`@fastify/autoload`](https://github.com/fastify/fastify-autoload) Require all
plugins in a directory.
- [`@fastify/awilix`](https://github.com/fastify/fastify-awilix) Dependency
injection support for Fastify, based on
[awilix](https://github.com/jeffijoe/awilix).
- [`@fastify/aws-lambda`](https://github.com/fastify/aws-lambda-fastify) allows
you to easily build serverless web applications/services and RESTful APIs
using Fastify on top of AWS Lambda and Amazon API Gateway.
- [`@fastify/basic-auth`](https://github.com/fastify/fastify-basic-auth) Basic
auth plugin for Fastify.
- [`@fastify/bearer-auth`](https://github.com/fastify/fastify-bearer-auth)
Bearer auth plugin for Fastify.
- [`@fastify/caching`](https://github.com/fastify/fastify-caching) General
server-side cache and ETag support.
- [`@fastify/circuit-breaker`](https://github.com/fastify/fastify-circuit-breaker)
A low overhead circuit breaker for your routes.
- [`@fastify/compress`](https://github.com/fastify/fastify-compress) Fastify
compression utils.
- [`@fastify/cookie`](https://github.com/fastify/fastify-cookie) Parse and set
cookie headers.
- [`@fastify/cors`](https://github.com/fastify/fastify-cors) Enables the use of
CORS in a Fastify application.
- [`@fastify/csrf-protection`](https://github.com/fastify/csrf-protection) A
plugin for adding
[CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection to
Fastify.
- [`@fastify/elasticsearch`](https://github.com/fastify/fastify-elasticsearch)
Plugin to share the same ES client.
- [`@fastify/env`](https://github.com/fastify/fastify-env) Load and check
configuration.
- [`@fastify/etag`](https://github.com/fastify/fastify-etag) Automatically
generate ETags for HTTP responses.
- [`@fastify/express`](https://github.com/fastify/fastify-express) Express
compatibility layer for Fastify.
- [`@fastify/flash`](https://github.com/fastify/fastify-flash) Set and get flash
messages using the session.
- [`@fastify/formbody`](https://github.com/fastify/fastify-formbody) Plugin to
parse x-www-form-urlencoded bodies.
- [`@fastify/funky`](https://github.com/fastify/fastify-funky) Makes functional
programming in Fastify more convenient. Adds support for Fastify routes
returning functional structures, such as Either, Task or plain parameterless
function.
- [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important
security headers for Fastify.
- [`@fastify/hotwire`](https://github.com/fastify/fastify-hotwire) Use the
Hotwire pattern with Fastify.
- [`@fastify/http-proxy`](https://github.com/fastify/fastify-http-proxy) Proxy
your HTTP requests to another server, with hooks.
- [`@fastify/jwt`](https://github.com/fastify/fastify-jwt) JWT utils for
Fastify, internally uses [fast-jwt](https://github.com/nearform/fast-jwt).
- [`@fastify/kafka`](https://github.com/fastify/fastify-kafka) Plugin to interact
with Apache Kafka.
- [`@fastify/leveldb`](https://github.com/fastify/fastify-leveldb) Plugin to
share a common LevelDB connection across Fastify.
- [`@fastify/middie`](https://github.com/fastify/middie) Middleware engine for
Fastify.
- [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify
MongoDB connection plugin, with which you can share the same MongoDB
connection pool across every part of your server.
- [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart
support for Fastify.
- [`@fastify/mysql`](https://github.com/fastify/fastify-mysql) Fastify MySQL
connection plugin.
- [`@fastify/nextjs`](https://github.com/fastify/fastify-nextjs) React
server-side rendering support for Fastify with
[Next](https://github.com/zeit/next.js/).
- [`@fastify/oauth2`](https://github.com/fastify/fastify-oauth2) Wrap around
[`simple-oauth2`](https://github.com/lelylan/simple-oauth2).
- [`@fastify/one-line-logger`](https://github.com/fastify/one-line-logger) Formats
Fastify's logs into a nice one-line message.
- [`@fastify/otel`](https://github.com/fastify/otel) OpenTelemetry
instrumentation library.
- [`@fastify/passport`](https://github.com/fastify/fastify-passport) Use Passport
strategies to authenticate requests and protect route.
- [`@fastify/postgres`](https://github.com/fastify/fastify-postgres) Fastify
PostgreSQL connection plugin, with this you can share the same PostgreSQL
connection pool in every part of your server.
- [`@fastify/rate-limit`](https://github.com/fastify/fastify-rate-limit) A low
overhead rate limiter for your routes.
- [`@fastify/redis`](https://github.com/fastify/fastify-redis) Fastify Redis
connection plugin, with which you can share the same Redis connection across
every part of your server.
- [`@fastify/reply-from`](https://github.com/fastify/fastify-reply-from) Plugin
to forward the current HTTP request to another server.
- [`@fastify/request-context`](https://github.com/fastify/fastify-request-context)
Request-scoped storage, based on
[AsyncLocalStorage](https://nodejs.org/api/async_hooks.html#async_hooks_class_asynclocalstorage)
(with fallback to [cls-hooked](https://github.com/Jeff-Lewis/cls-hooked)),
providing functionality similar to thread-local storages.
- [`@fastify/response-validation`](https://github.com/fastify/fastify-response-validation)
A simple plugin that enables response validation for Fastify.
- [`@fastify/routes`](https://github.com/fastify/fastify-routes) Plugin that
provides a `Map` of routes.
- [`@fastify/routes-stats`](https://github.com/fastify/fastify-routes-stats)
Provide stats for routes using `node:perf_hooks`.
- [`@fastify/schedule`](https://github.com/fastify/fastify-schedule) Plugin for
scheduling periodic jobs, based on
[toad-scheduler](https://github.com/kibertoad/toad-scheduler).
- [`@fastify/secure-session`](https://github.com/fastify/fastify-secure-session)
Create a secure stateless cookie session for Fastify.
- [`@fastify/sensible`](https://github.com/fastify/fastify-sensible) Defaults
for Fastify that everyone can agree on. It adds some useful decorators such as
HTTP errors and assertions, but also more request and reply methods.
- [`@fastify/session`](https://github.com/fastify/session) a session plugin for
Fastify.
- [`@fastify/static`](https://github.com/fastify/fastify-static) Plugin for
serving static files as fast as possible.
- [`@fastify/swagger`](https://github.com/fastify/fastify-swagger) Plugin for
serving Swagger/OpenAPI documentation for Fastify, supporting dynamic
generation.
- [`@fastify/swagger-ui`](https://github.com/fastify/fastify-swagger-ui) Plugin
for serving Swagger UI.
- [`@fastify/throttle`](https://github.com/fastify/fastify-throttle) Plugin for
throttling the download speed of a request.
- [`@fastify/type-provider-json-schema-to-ts`](https://github.com/fastify/fastify-type-provider-json-schema-to-ts)
Fastify
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [json-schema-to-ts](https://github.com/ThomasAribart/json-schema-to-ts).
- [`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
Fastify
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [Typebox](https://github.com/sinclairzx81/typebox).
- [`@fastify/under-pressure`](https://github.com/fastify/under-pressure) Measure
process load with automatic handling of _"Service Unavailable"_ plugin for
Fastify.
- [`@fastify/url-data`](https://github.com/fastify/fastify-url-data) Decorate
the `Request` object with a method to access raw URL components.
- [`@fastify/view`](https://github.com/fastify/point-of-view) Templates
rendering (_ejs, pug, handlebars, marko_) plugin support for Fastify.
- [`@fastify/vite`](https://github.com/fastify/fastify-vite) Integration with
[Vite](https://vitejs.dev/), allows for serving SPA/MPA/SSR Vite applications.
- [`@fastify/websocket`](https://github.com/fastify/fastify-websocket) WebSocket
support for Fastify. Built upon [ws](https://github.com/websockets/ws).
- [`@fastify/zipkin`](https://github.com/fastify/fastify-zipkin) Plugin
for Zipkin distributed tracing system.
#### [Community](#community)
> Note:
> Fastify community plugins are part of the broader community efforts,
> and we are thankful for these contributions. However, they are not
> maintained by the Fastify team.
> Use them at your own discretion.
> If you find malicious code, please
> [open an issue](https://github.com/fastify/fastify/issues/new/choose) or
> submit a PR to remove the plugin from the list.
- [`@aaroncadillac/crudify-mongo`](https://github.com/aaroncadillac/crudify-mongo)
A simple way to add a crud in your fastify project.
- [`@applicazza/fastify-nextjs`](https://github.com/applicazza/fastify-nextjs)
Alternate Fastify and Next.js integration.
- [`@blastorg/fastify-aws-dynamodb-cache`](https://github.com/blastorg/fastify-aws-dynamodb-cache)
A plugin to help with caching API responses using AWS DynamoDB.
- [`@clerk/fastify`](https://github.com/clerkinc/javascript/tree/main/packages/fastify)
Add authentication and user management to your Fastify application with Clerk.
- [`@coobaha/typed-fastify`](https://github.com/Coobaha/typed-fastify) Strongly
typed routes with a runtime validation using JSON schema generated from types.
- [`@dnlup/fastify-doc`](https://github.com/dnlup/fastify-doc) A plugin for
sampling process metrics.
- [`@dnlup/fastify-traps`](https://github.com/dnlup/fastify-traps) A plugin to
close the server gracefully on `SIGINT` and `SIGTERM` signals.
- [`@eropple/fastify-openapi3`](https://github.com/eropple/fastify-openapi3) Provides
easy, developer-friendly OpenAPI 3.1 specs + doc explorer based on your routes.
- [`@ethicdevs/fastify-custom-session`](https://github.com/EthicDevs/fastify-custom-session)
A plugin lets you use session and decide only where to load/save from/to. Has
great TypeScript support + built-in adapters for common ORMs/databases (Firebase,
Prisma Client, Postgres (wip), InMemory) and you can easily make your own adapter!
- [`@ethicdevs/fastify-git-server`](https://github.com/EthicDevs/fastify-git-server)
A plugin to easily create git server and make one/many Git repositories available
for clone/fetch/push through the standard `git` (over http) commands.
- [`@exortek/fastify-mongo-sanitize`](https://github.com/ExorTek/fastify-mongo-sanitize)
A Fastify plugin that protects against No(n)SQL injection by sanitizing data.
- [`@exortek/remix-fastify`](https://github.com/ExorTek/remix-fastify)
Fastify plugin for Remix.
- [`@fastify-userland/request-id`](https://github.com/fastify-userland/request-id)
Fastify Request ID Plugin
- [`@fastify-userland/typeorm-query-runner`](https://github.com/fastify-userland/typeorm-query-runner)
Fastify typeorm QueryRunner plugin
- [`@gquittet/graceful-server`](https://github.com/gquittet/graceful-server)
Tiny (~5k), Fast, KISS, and dependency-free Node.js library to make your
Fastify API graceful.
- [`@h4ad/serverless-adapter`](https://github.com/H4ad/serverless-adapter)
Run REST APIs and other web applications using your existing Node.js
application framework (Express, Koa, Hapi and Fastify), on top of AWS Lambda,
Huawei and many other clouds.
- [`@hey-api/openapi-ts`](https://heyapi.dev/openapi-ts/plugins/fastify)
The OpenAPI to TypeScript codegen. Generate clients, SDKs, validators, and more.
- [`@immobiliarelabs/fastify-metrics`](https://github.com/immobiliare/fastify-metrics)
Minimalistic and opinionated plugin that collects usage/process metrics and
dispatches to [statsd](https://github.com/statsd/statsd).
- [`@inaiat/fastify-papr`](https://github.com/inaiat/fastify-papr)
A plugin to integrate [Papr](https://github.com/plexinc/papr),
the MongoDB ORM for TypeScript & MongoDB, with Fastify.
- [`@jerome1337/fastify-enforce-routes-pattern`](https://github.com/Jerome1337/fastify-enforce-routes-pattern)
A Fastify plugin that enforces naming pattern for routes path.
- [`@joggr/fastify-prisma`](https://github.com/joggrdocs/fastify-prisma)
A plugin for accessing an instantiated PrismaClient on your server.
- [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit)
A plugin to close the server gracefully
- [`@mgcrea/fastify-request-logger`](https://github.com/mgcrea/fastify-request-logger)
A plugin to enable compact request logging for Fastify
- [`@mgcrea/fastify-session`](https://github.com/mgcrea/fastify-session) Session
plugin for Fastify that supports both stateless and stateful sessions
- [`@mgcrea/fastify-session-redis-store`](https://github.com/mgcrea/fastify-session-redis-store)
Redis store for @mgcrea/fastify-session using ioredis
- [`@mgcrea/fastify-session-sodium-crypto`](https://github.com/mgcrea/fastify-session-sodium-crypto)
Fast sodium-based crypto for @mgcrea/fastify-session
- [`@mgcrea/pino-pretty-compact`](https://github.com/mgcrea/pino-pretty-compact)
A custom compact pino-base prettifier
- [`@pybot/fastify-autoload`](https://github.com/kunal097/fastify-autoload)
Plugin to generate routes automatically with valid json content
- [`@scalar/fastify-api-reference`](https://github.com/scalar/scalar/tree/main/integrations/fastify)
Beautiful OpenAPI/Swagger API references for Fastify
- [`@trubavuong/fastify-seaweedfs`](https://github.com/trubavuong/fastify-seaweedfs)
SeaweedFS for Fastify
- [`apitally`](https://github.com/apitally/apitally-js) Fastify plugin to
integrate with [Apitally](https://apitally.io/fastify), an API analytics,
logging and monitoring tool.
- [`arecibo`](https://github.com/nucleode/arecibo) Fastify ping responder for
Kubernetes Liveness and Readiness Probes.
- [`aws-xray-sdk-fastify`](https://github.com/aws/aws-xray-sdk-node/tree/master/sdk_contrib/fastify)
A Fastify plugin to log requests and subsegments through AWSXray.
- [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware
for CLS-based request ID generation. An out-of-the-box solution for adding
request IDs into your logs.
- [`electron-server`](https://github.com/anonrig/electron-server) A plugin for
using Fastify without the need of consuming a port on Electron apps.
- [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for
waterline. Decorates Fastify with waterline models.
- [`fastify-204`](https://github.com/Shiva127/fastify-204) Fastify plugin that
return 204 status on empty response.
- [`fastify-405`](https://github.com/Eomm/fastify-405) Fastify plugin that adds
405 HTTP status to your routes
- [`fastify-allow`](https://github.com/mattbishop/fastify-allow) Fastify plugin
that automatically adds an Allow header to responses with routes. Also sends
405 responses for routes that have a handler but not for the request's method.
- [`fastify-amqp`](https://github.com/RafaelGSS/fastify-amqp) Fastify AMQP
connection plugin, to use with RabbitMQ or another connector. Just a wrapper
to [`amqplib`](https://github.com/squaremo/amqp.node).
- [`fastify-amqp-async`](https://github.com/kffl/fastify-amqp-async) Fastify
AMQP plugin with a Promise-based API provided by
[`amqplib-as-promised`](https://github.com/twawszczak/amqplib-as-promised).
- [`fastify-angular-universal`](https://github.com/exequiel09/fastify-angular-universal)
Angular server-side rendering support using
[`@angular/platform-server`](https://github.com/angular/angular/tree/master/packages/platform-server)
for Fastify
- [`fastify-api-key`](https://github.com/arkerone/fastify-api-key) Fastify
plugin to authenticate HTTP requests based on API key and signature
- [`fastify-appwrite`](https://github.com/Dev-Manny/fastify-appwrite) Fastify
Plugin for interacting with Appwrite server.
- [`fastify-asyncforge`](https://github.com/mcollina/fastify-asyncforge) Plugin
to access Fastify instance, logger, request and reply from Node.js [Async
Local Storage](https://nodejs.org/api/async_context.html#class-asynclocalstorage).
- [`fastify-at-mysql`](https://github.com/mateonunez/fastify-at-mysql) Fastify
MySQL plugin with auto SQL injection attack prevention.
- [`fastify-at-postgres`](https://github.com/mateonunez/fastify-at-postgres) Fastify
Postgres plugin with auto SQL injection attack prevention.
- [`fastify-auth0-verify`](https://github.com/nearform/fastify-auth0-verify):
Auth0 verification plugin for Fastify, internally uses
[fastify-jwt](https://npm.im/fastify-jwt) and
[jsonwebtoken](https://npm.im/jsonwebtoken).
- [`fastify-autocrud`](https://github.com/paranoiasystem/fastify-autocrud)
Plugin to auto-generate CRUD routes as fast as possible.
- [`fastify-autoroutes`](https://github.com/GiovanniCardamone/fastify-autoroutes)
Plugin to scan and load routes based on filesystem path from a custom
directory.
- [`fastify-aws-sns`](https://github.com/gzileni/fastify-aws-sns) Fastify plugin
for AWS Simple Notification Service (AWS SNS) that coordinates and manages
the delivery or sending of messages to subscribing endpoints or clients.
- [`fastify-aws-timestream`](https://github.com/gzileni/fastify-aws-timestream)
Fastify plugin for managing databases, tables, and querying and creating
scheduled queries with AWS Timestream.
- [`fastify-axios`](https://github.com/davidedantonio/fastify-axios) Plugin to
send HTTP requests via [axios](https://github.com/axios/axios).
- [`fastify-babel`](https://github.com/cfware/fastify-babel) Fastify plugin for
development servers that require Babel transformations of JavaScript sources.
- [`fastify-bcrypt`](https://github.com/beliven-it/fastify-bcrypt) A Bcrypt hash
generator & checker.
- [`fastify-better-sqlite3`](https://github.com/punkish/fastify-better-sqlite3)
Plugin for better-sqlite3.
- [`fastify-blipp`](https://github.com/PavelPolyakov/fastify-blipp) Prints your
routes to the console, so you definitely know which endpoints are available.
- [`fastify-bookshelf`](https://github.com/butlerx/fastify-bookshelfjs) Fastify
plugin to add [bookshelf.js](https://bookshelfjs.org/) ORM support.
- [`fastify-boom`](https://github.com/jeromemacias/fastify-boom) Fastify plugin
to add [boom](https://github.com/hapijs/boom) support.
- [`fastify-bree`](https://github.com/climba03003/fastify-bree) Fastify plugin
to add [bree](https://github.com/breejs/bree) support.
- [`fastify-bugsnag`](https://github.com/ZigaStrgar/fastify-bugsnag) Fastify plugin
to add support for [Bugsnag](https://www.bugsnag.com/) error reporting.
- [`fastify-cacheman`](https://gitlab.com/aalfiann/fastify-cacheman)
Small and efficient cache provider for Node.js with In-memory, File, Redis
and MongoDB engines for Fastify
- [`fastify-casbin`](https://github.com/nearform/fastify-casbin) Casbin support
for Fastify.
- [`fastify-casbin-rest`](https://github.com/nearform/fastify-casbin-rest)
Casbin support for Fastify based on a RESTful model.
- [`fastify-casl`](https://github.com/Inlecom/fastify-casl) Fastify
[CASL](https://github.com/stalniy/casl) plugin that supports ACL-like
protection of endpoints via either a preSerialization & preHandler hook,
sanitizing the inputs and outputs of your application based on user rights.
- [`fastify-cloudevents`](https://github.com/smartiniOnGitHub/fastify-cloudevents)
Fastify plugin to generate and forward Fastify events in the Cloudevents
format.
- [`fastify-cloudflare-turnstile`](https://github.com/112RG/fastify-cloudflare-turnstile)
Fastify plugin for CloudFlare Turnstile.
- [`fastify-cloudinary`](https://github.com/Vanilla-IceCream/fastify-cloudinary)
Plugin to share a common Cloudinary connection across Fastify.
- [`fastify-cockroachdb`](https://github.com/alex-ppg/fastify-cockroachdb)
Fastify plugin to connect to a CockroachDB PostgreSQL instance via the
Sequelize ORM.
- [`fastify-constraints`](https://github.com/nearform/fastify-constraints)
Fastify plugin to add constraints to multiple routes
- [`fastify-couchdb`](https://github.com/nigelhanlon/fastify-couchdb) Fastify
plugin to add CouchDB support via [nano](https://github.com/apache/nano).
- [`fastify-crud-generator`](https://github.com/beliven-it/fastify-crud-generator)
A plugin to rapidly generate CRUD routes for any entity.
- [`fastify-custom-healthcheck`](https://github.com/gkampitakis/fastify-custom-healthcheck)
Fastify plugin to add health route in your server that asserts custom
functions.
- [`fastify-decorators`](https://github.com/L2jLiga/fastify-decorators) Fastify
plugin that provides the set of TypeScript decorators.
- [`fastify-delay-request`](https://github.com/climba03003/fastify-delay-request)
Fastify plugin that allows requests to be delayed whilst a task the response is
dependent on is run, such as a resource intensive process.
- [`fastify-disablecache`](https://github.com/Fdawgs/fastify-disablecache)
Fastify plugin to disable client-side caching, inspired by
[nocache](https://github.com/helmetjs/nocache).
- [`fastify-dynamodb`](https://github.com/matrus2/fastify-dynamodb) AWS DynamoDB
plugin for Fastify. It exposes
[AWS.DynamoDB.DocumentClient()](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html)
object.
- [`fastify-dynareg`](https://github.com/greguz/fastify-dynareg) Dynamic plugin
register for Fastify.
- [`fastify-envalid`](https://github.com/alemagio/fastify-envalid) Fastify
plugin to integrate [envalid](https://github.com/af/envalid) in your Fastify
project.
- [`fastify-error-page`](https://github.com/hemerajs/fastify-error-page) Fastify
plugin to print errors in structured HTML to the browser.
- [`fastify-esso`](https://github.com/patrickpissurno/fastify-esso) The easiest
authentication plugin for Fastify, with built-in support for Single sign-on
(and great documentation).
- [`fastify-event-bus`](https://github.com/Shiva127/fastify-event-bus) Event bus
support for Fastify. Built upon [js-event-bus](https://github.com/bcerati/js-event-bus).
- [`fastify-evervault`](https://github.com/Briscoooe/fastify-evervault/) Fastify
plugin for instantiating and encapsulating the
[Evervault](https://evervault.com/) client.
- [`fastify-explorer`](https://github.com/Eomm/fastify-explorer) Get control of
your decorators across all the encapsulated contexts.
- [`fastify-favicon`](https://github.com/smartiniOnGitHub/fastify-favicon)
Fastify plugin to serve default favicon.
- [`fastify-feature-flags`](https://gitlab.com/m03geek/fastify-feature-flags)
Fastify feature flags plugin with multiple providers support (e.g. env,
[config](https://lorenwest.github.io/node-config/),
[unleash](https://unleash.github.io/)).
- [`fastify-file-routes`](https://github.com/spa5k/fastify-file-routes) Get
Next.js based file system routing into fastify.
- [`fastify-file-upload`](https://github.com/huangang/fastify-file-upload)
Fastify plugin for uploading files.
- [`fastify-firebase`](https://github.com/now-ims/fastify-firebase) Fastify
plugin for [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup)
to Fastify so you can easily use Firebase Auth, Firestore, Cloud Storage,
Cloud Messaging, and more.
- [`fastify-firebase-auth`](https://github.com/oxsav/fastify-firebase-auth)
Firebase Authentication for Fastify supporting all of the methods relating to
the authentication API.
- [`fastify-formidable`](https://github.com/climba03003/fastify-formidable)
Handy plugin to provide multipart support and fastify-swagger integration.
- [`fastify-gcloud-trace`](https://github.com/mkinoshi/fastify-gcloud-trace)
[Google Cloud Trace API](https://cloud.google.com/trace/docs/reference)
Connector for Fastify.
- [`fastify-get-head`](https://github.com/MetCoder95/fastify-get-head) Small
plugin to set a new HEAD route handler for each GET route previously
registered in Fastify.
- [`fastify-get-only`](https://github.com/DanieleFedeli/fastify-get-only) Small
plugin used to make fastify accept only GET requests
- [`fastify-good-sessions`](https://github.com/Phara0h/fastify-good-sessions) A
good Fastify sessions plugin focused on speed.
- [`fastify-google-cloud-storage`](https://github.com/carlozamagni/fastify-google-cloud-storage)
Fastify plugin that exposes a GCP Cloud Storage client instance.
- [`fastify-graceful-shutdown`](https://github.com/hemerajs/fastify-graceful-shutdown)
Shutdown Fastify gracefully and asynchronously.
- [`fastify-grant`](https://github.com/simov/fastify-grant)
Authentication/Authorization plugin for Fastify that supports 200+ OAuth
Providers.
- [`fastify-guard`](https://github.com/hsynlms/fastify-guard) A Fastify plugin
that protects endpoints by checking authenticated user roles and/or scopes.
- [`fastify-hana`](https://github.com/yoav0gal/fastify-hana) connects your
application to [`SAP-HANA`](https://help.sap.com/docs/SAP_HANA_CLIENT).
- [`fastify-hashids`](https://github.com/andersonjoseph/fastify-hashids) A Fastify
plugin to encode/decode IDs using [hashids](https://github.com/niieani/hashids.js).
- [`fastify-hasura`](https://github.com/ManUtopiK/fastify-hasura) A Fastify
plugin to have fun with [Hasura](https://github.com/hasura/graphql-engine).
- [`fastify-healthcheck`](https://github.com/smartiniOnGitHub/fastify-healthcheck)
Fastify plugin to serve a health check route and a probe script.
- [`fastify-hemera`](https://github.com/hemerajs/fastify-hemera) Fastify Hemera
plugin, for writing reliable & fault-tolerant microservices with
[nats.io](https://nats.io/).
- [`fastify-hl7`](https://github.com/Bugs5382/fastify-hl7) A Fastify Plugin to
create a server, build, and send HL7 formatted Hl7 messages. Using
[node-hl7-client](https://github.com/Bugs5382/node-hl7-client) and
[node-hl7-server](https://github.com/Bugs5382/node-hl7-server) as the
underlining technology to do this.
- [`fastify-http-client`](https://github.com/kenuyx/fastify-http-client) Plugin
to send HTTP(s) requests. Built upon [urllib](https://github.com/node-modules/urllib).
- [`fastify-http-context`](https://github.com/thorough-developer/fastify-http-context)
Fastify plugin for "simulating" a thread of execution to allow for true HTTP
context to take place per API call within the Fastify lifecycle of calls.
- [`fastify-http-errors-enhanced`](https://github.com/ShogunPanda/fastify-http-errors-enhanced)
An error handling plugin for Fastify that uses enhanced HTTP errors.
- [`fastify-http2https`](https://github.com/lolo32/fastify-http2https) Redirect
HTTP requests to HTTPS, both using the same port number, or different response
on HTTP and HTTPS.
- [`fastify-https-always`](https://github.com/mattbishop/fastify-https-always)
Lightweight, proxy-aware redirect plugin from HTTP to HTTPS.
- [`fastify-https-redirect`](https://github.com/tomsvogel/fastify-https-redirect)
Fastify plugin for auto-redirect from HTTP to HTTPS.
- [`fastify-i18n`](https://github.com/Vanilla-IceCream/fastify-i18n)
Internationalization plugin for Fastify. Built upon node-polyglot.
- [`fastify-impressions`](https://github.com/manju4ever/fastify-impressions)
Fastify plugin to track impressions of all the routes.
- [`fastify-influxdb`](https://github.com/alex-ppg/fastify-influxdb) Fastify
InfluxDB plugin connecting to an InfluxDB instance via the Influx default
package.
- [`fastify-ip`](https://github.com/metcoder95/fastify-ip) A plugin
for Fastify that allows you to infer a request ID by a
given set of custom Request headers.
- [`fastify-json-to-xml`](https://github.com/Fdawgs/fastify-json-to-xml) Fastify
plugin to serialize JSON responses into XML.
- [`fastify-jwt-authz`](https://github.com/Ethan-Arrowood/fastify-jwt-authz) JWT
user scope verifier.
- [`fastify-jwt-webapp`](https://github.com/charlesread/fastify-jwt-webapp) JWT
authentication for Fastify-based web apps.
- [`fastify-kafkajs`](https://github.com/kffl/fastify-kafkajs) Fastify plugin
that adds support for KafkaJS - a modern Apache Kafka client library.
- [`fastify-keycloak-adapter`](https://github.com/yubinTW/fastify-keycloak-adapter)
A keycloak adapter for a Fastify app.
- [`fastify-knexjs`](https://github.com/chapuletta/fastify-knexjs) Fastify
plugin for supporting KnexJS Query Builder.
- [`fastify-knexjs-mock`](https://github.com/chapuletta/fastify-knexjs-mock)
Fastify Mock KnexJS for testing support.
- [`fastify-koa`](https://github.com/rozzilla/fastify-koa) Convert Koa
middlewares into Fastify plugins
- [`fastify-kubernetes`](https://github.com/greguz/fastify-kubernetes) Fastify
Kubernetes client plugin.
- [`fastify-kysely`](https://github.com/alenap93/fastify-kysely) Fastify
plugin for supporting Kysely type-safe query builder.
- [`fastify-language-parser`](https://github.com/lependu/fastify-language-parser)
Fastify plugin to parse request language.
- [`fastify-lcache`](https://github.com/denbon05/fastify-lcache)
Lightweight cache plugin
- [`fastify-list-routes`](https://github.com/chuongtrh/fastify-list-routes)
A simple plugin for Fastify to list all available routes.
- [`fastify-lm`](https://github.com/galiprandi/fastify-lm#readme)
Use OpenAI, Claude, Google, Deepseek, and others LMs with one Fastify plugin.
- [`fastify-loader`](https://github.com/TheNoim/fastify-loader) Load routes from
a directory and inject the Fastify instance in each file.
- [`fastify-log-controller`](https://github.com/Eomm/fastify-log-controller/)
changes the log level of your Fastify server at runtime.
- [`fastify-lured`](https://github.com/lependu/fastify-lured) Plugin to load lua
scripts with [fastify-redis](https://github.com/fastify/fastify-redis) and
[lured](https://github.com/enobufs/lured).
A plugin to implement [Lyra](https://github.com/LyraSearch/lyra) search engine
on Fastify.
- [`fastify-mailer`](https://github.com/coopflow/fastify-mailer) Plugin to
initialize and encapsulate [Nodemailer](https://nodemailer.com)'s transporters
instances in Fastify.
- [`fastify-markdown`](https://github.com/freezestudio/fastify-markdown) Plugin
to markdown support.
- [`fastify-method-override`](https://github.com/corsicanec82/fastify-method-override)
Plugin for Fastify, which allows the use of HTTP verbs, such as DELETE, PATCH,
HEAD, PUT, OPTIONS in case the client doesn't support them.
- [`fastify-metrics`](https://gitlab.com/m03geek/fastify-metrics) Plugin for
exporting [Prometheus](https://prometheus.io) metrics.
- [`fastify-minify`](https://github.com/Jelenkee/fastify-minify) Plugin for
minification and transformation of responses.
- [`fastify-mongo-memory`](https://github.com/chapuletta/fastify-mongo-memory)
Fastify MongoDB in Memory Plugin for testing support.
- [`fastify-mongodb-sanitizer`](https://github.com/KlemenKozelj/fastify-mongodb-sanitizer)
Fastify plugin that sanitizes client input to prevent
potential MongoDB query injection attacks.
- [`fastify-mongoose-api`](https://github.com/jeka-kiselyov/fastify-mongoose-api)
Fastify plugin to create REST API methods based on Mongoose MongoDB models.
- [`fastify-mongoose-driver`](https://github.com/alex-ppg/fastify-mongoose)
Fastify Mongoose plugin that connects to a MongoDB via the Mongoose plugin
with support for Models.
- [`fastify-mqtt`](https://github.com/love-lena/fastify-mqtt) Plugin to share
[mqtt](https://www.npmjs.com/package/mqtt) client across Fastify.
- [`fastify-msgpack`](https://github.com/kenriortega/fastify-msgpack) Fastify
and MessagePack, together at last. Uses @msgpack/msgpack by default.
- [`fastify-msgraph-webhook`](https://github.com/flower-of-the-bridges/fastify-msgraph-change-notifications-webhook)
to manage
[MS Graph Change Notifications webhooks](https://learn.microsoft.com/it-it/graph/change-notifications-delivery-webhooks?tabs=http).
- [`fastify-multer`](https://github.com/fox1t/fastify-multer) Multer is a plugin
for handling multipart/form-data, which is primarily used for uploading files.
- [`fastify-multilingual`](https://github.com/gbrugger/fastify-multilingual) Unobtrusively
decorates fastify request with Polyglot.js for i18n.
- [`fastify-nats`](https://github.com/mahmed8003/fastify-nats) Plugin to share
[NATS](https://nats.io) client across Fastify.
- [`fastify-next-auth`](https://github.com/wobsoriano/fastify-next-auth)
NextAuth.js plugin for Fastify.
- [`fastify-no-additional-properties`](https://github.com/greguz/fastify-no-additional-properties)
Add `additionalProperties: false` by default to your JSON Schemas.
- [`fastify-no-icon`](https://github.com/jsumners/fastify-no-icon) Plugin to
eliminate thrown errors for `/favicon.ico` requests.
- [`fastify-normalize-request-reply`](https://github.com/ericrglass/fastify-normalize-request-reply)
Plugin to normalize the request and reply to the Express version 4.x request
and response, which allows use of middleware, like swagger-stats, that was
originally written for Express.
- [`fastify-now`](https://github.com/yonathan06/fastify-now) Structure your
endpoints in a folder and load them dynamically with Fastify.
- [`fastify-nuxtjs`](https://github.com/gomah/fastify-nuxtjs) Vue server-side
rendering support for Fastify with Nuxt.js Framework.
- [`fastify-oas`](https://gitlab.com/m03geek/fastify-oas) Generates OpenAPI 3.0+
documentation from routes schemas for Fastify.
- [`fastify-objectionjs`](https://github.com/jarcodallo/fastify-objectionjs)
Plugin for the Fastify framework that provides integration with objectionjs
ORM.
- [`fastify-objectionjs-classes`](https://github.com/kamikazechaser/fastify-objectionjs-classes)
Plugin to cherry-pick classes from objectionjs ORM.
- [`fastify-opaque-apake`](https://github.com/squirrelchat/fastify-opaque-apake)
A Fastify plugin to implement the OPAQUE aPAKE protocol. Uses
[@squirrelchat/opaque-wasm-server](https://github.com/squirrelchat/opaque-wasm).
- [`fastify-openapi-docs`](https://github.com/ShogunPanda/fastify-openapi-docs)
A Fastify plugin that generates OpenAPI spec automatically.
- [`fastify-openapi-glue`](https://github.com/seriousme/fastify-openapi-glue)
Glue for OpenAPI specifications in Fastify, autogenerates routes based on an
OpenAPI Specification.
- [`fastify-opentelemetry`](https://github.com/autotelic/fastify-opentelemetry)
A Fastify plugin that uses the [OpenTelemetry
API](https://github.com/open-telemetry/opentelemetry-js-api) to provide
request tracing.
- [`fastify-oracle`](https://github.com/cemremengu/fastify-oracle) Attaches an
[`oracledb`](https://github.com/oracle/node-oracledb) connection pool to a
Fastify server instance.
- [`fastify-orama`](https://github.com/mateonunez/fastify-orama)
- [`fastify-orientdb`](https://github.com/mahmed8003/fastify-orientdb) Fastify
OrientDB connection plugin, with which you can share the OrientDB connection
across every part of your server.
- [`fastify-osm`](https://github.com/gzileni/fastify-osm) Fastify
OSM plugin to run overpass queries by OpenStreetMap.
- [`fastify-override`](https://github.com/matthyk/fastify-override)
Fastify plugin to override decorators, plugins and hooks for testing purposes
- [`fastify-passkit-webservice`](https://github.com/alexandercerutti/fastify-passkit-webservice)
A set of Fastify plugins to integrate Apple Wallet Web Service specification
- [`fastify-peekaboo`](https://github.com/simone-sanfratello/fastify-peekaboo)
Fastify plugin for memoize responses by expressive settings.
- [`fastify-permissions`](https://github.com/pckrishnadas88/fastify-permissions)
Route-level permission middleware for Fastify supports
custom permission checks.
- [`fastify-piscina`](https://github.com/piscinajs/fastify-piscina) A worker
thread pool plugin using [Piscina](https://github.com/piscinajs/piscina).
- [`fastify-polyglot`](https://github.com/beliven-it/fastify-polyglot) A plugin to
handle i18n using
[node-polyglot](https://www.npmjs.com/package/node-polyglot).
- [`fastify-postgraphile`](https://github.com/alemagio/fastify-postgraphile)
Plugin to integrate [PostGraphile](https://www.graphile.org/postgraphile/) in
a Fastify project.
- [`fastify-postgres-dot-js`](https://github.com/kylerush/fastify-postgresjs) Fastify
PostgreSQL connection plugin that uses [Postgres.js](https://github.com/porsager/postgres).
- [`fastify-prettier`](https://github.com/hsynlms/fastify-prettier) A Fastify
plugin that uses [prettier](https://github.com/prettier/prettier) under the
hood to beautify outgoing responses and/or other things in the Fastify server.
- [`fastify-print-routes`](https://github.com/ShogunPanda/fastify-print-routes)
A Fastify plugin that prints all available routes.
- [`fastify-protobufjs`](https://github.com/kenriortega/fastify-protobufjs)
Fastify and protobufjs, together at last. Uses protobufjs by default.
- [`fastify-qrcode`](https://github.com/chonla/fastify-qrcode) This plugin
utilizes [qrcode](https://github.com/soldair/node-qrcode) to generate QR Code.
- [`fastify-qs`](https://github.com/vanodevium/fastify-qs) A plugin for Fastify
that adds support for parsing URL query parameters with
[qs](https://github.com/ljharb/qs).
- [`fastify-rabbitmq`](https://github.com/Bugs5382/fastify-rabbitmq) Fastify
RabbitMQ plugin that uses
[node-rabbitmq-client](https://github.com/cody-greene/node-rabbitmq-client)
plugin as a wrapper.
- [`fastify-racing`](https://github.com/metcoder95/fastify-racing) Fastify's
plugin that adds support to handle an aborted request asynchronous.
- [`fastify-ravendb`](https://github.com/nearform/fastify-ravendb) RavenDB
connection plugin. It exposes the same `DocumentStore` (or multiple ones)
across the whole Fastify application.
- [`fastify-raw-body`](https://github.com/Eomm/fastify-raw-body) Add the
`request.rawBody` field.
- [`fastify-rbac`](https://gitlab.com/m03geek/fastify-rbac) Fastify role-based
access control plugin.
- [`fastify-recaptcha`](https://github.com/qwertyforce/fastify-recaptcha)
Fastify plugin for reCAPTCHA verification.
- [`fastify-redis-channels`](https://github.com/hearit-io/fastify-redis-channels)
A plugin for fast, reliable, and scalable channels implementation based on
Redis streams.
- [`fastify-redis-session`](https://github.com/mohammadraufzahed/fastify-redis-session)
Redis Session plugin for fastify.
- [`fastify-register-routes`](https://github.com/israeleriston/fastify-register-routes)
Plugin to automatically load routes from a specified path and optionally limit
loaded file names by a regular expression.
- [`fastify-response-caching`](https://github.com/codeaholicguy/fastify-response-caching)
A Fastify plugin for caching the response.
- [`fastify-response-time`](https://github.com/lolo32/fastify-response-time) Add
`X-Response-Time` header at each request for Fastify, in milliseconds.
- [`fastify-resty`](https://github.com/FastifyResty/fastify-resty) Fastify-based
web framework with REST API routes auto-generation for TypeORM entities using
DI and decorators.
- [`fastify-reverse-routes`](https://github.com/dimonnwc3/fastify-reverse-routes)
Fastify reverse routes plugin, allows to defined named routes and build path
using name and parameters.
- [`fastify-rob-config`](https://github.com/jeromemacias/fastify-rob-config)
Fastify Rob-Config integration.
- [`fastify-route-group`](https://github.com/TakNePoidet/fastify-route-group)
Convenient grouping and inheritance of routes.
- [`fastify-route-preset`](https://github.com/inyourtime/fastify-route-preset)
A Fastify plugin that enables you to create route configurations that can be
applied to multiple routes.
- [`fastify-s3-buckets`](https://github.com/kibertoad/fastify-s3-buckets)
Ensure the existence of defined S3 buckets on the application startup.
- [`fastify-schema-constraint`](https://github.com/Eomm/fastify-schema-constraint)
Choose the JSON schema to use based on request parameters.
- [`fastify-schema-to-typescript`](https://github.com/thomasthiebaud/fastify-schema-to-typescript)
Generate typescript types based on your JSON/YAML validation schemas so they
are always in sync.
- [`fastify-sentry`](https://github.com/alex-ppg/fastify-sentry) Fastify plugin
to add the Sentry SDK error handler to requests.
- [`fastify-sequelize`](https://github.com/lyquocnam/fastify-sequelize) Fastify
plugin work with Sequelize (adapter for Node.js -> Sqlite, Mysql, Mssql,
Postgres).
- [`fastify-server-session`](https://github.com/jsumners/fastify-server-session)
A session plugin with support for arbitrary backing caches via
`fastify-caching`.
- [`fastify-shared-schema`](https://github.com/Adibla/fastify-shared-schema) Plugin
for sharing schemas between different routes.
- [`fastify-slonik`](https://github.com/Unbuttun/fastify-slonik) Fastify Slonik
plugin, with this you can use slonik in every part of your server.
- [`fastify-slow-down`](https://github.com/nearform/fastify-slow-down) A plugin
to delay the response from the server.
- [`fastify-socket.io`](https://github.com/alemagio/fastify-socket.io) a
Socket.io plugin for Fastify.
- [`fastify-split-validator`](https://github.com/MetCoder95/fastify-split-validator)
Small plugin to allow you use multiple validators in one route based on each
HTTP part of the request.
- [`fastify-sqlite`](https://github.com/Eomm/fastify-sqlite) connects your
application to a sqlite3 database.
- [`fastify-sqlite-typed`](https://github.com/yoav0gal/fastify-sqlite-typed) connects
your application to a SQLite database with full Typescript support.
- [`fastify-sse`](https://github.com/lolo32/fastify-sse) to provide Server-Sent
Events with `reply.sse( … )` to Fastify.
- [`fastify-sse-v2`](https://github.com/nodefactoryio/fastify-sse-v2) to provide
Server-Sent Events using Async Iterators (supports newer versions of Fastify).
- [`fastify-ssr-vite`](https://github.com/nineohnine/fastify-ssr-vite) A simple
plugin for setting up server side rendering with vite.
- [`fastify-stripe`](https://github.com/coopflow/fastify-stripe) Plugin to
initialize and encapsulate [Stripe
Node.js](https://github.com/stripe/stripe-node) instances in Fastify.
- [`fastify-supabase`](https://github.com/coopflow/fastify-supabase) Plugin to
initialize and encapsulate [Supabase](https://github.com/supabase/supabase-js)
instances in Fastify.
- [`fastify-tls-keygen`](https://gitlab.com/sebdeckers/fastify-tls-keygen)
Automatically generate a browser-compatible, trusted, self-signed,
localhost-only, TLS certificate.
- [`fastify-tokenize`](https://github.com/Bowser65/fastify-tokenize)
[Tokenize](https://github.com/Bowser65/Tokenize) plugin for Fastify that
removes the pain of managing authentication tokens, with built-in integration
for `fastify-auth`.
- [`fastify-totp`](https://github.com/beliven-it/fastify-totp) A plugin to handle
TOTP (e.g. for 2FA).
- [`fastify-twitch-ebs-tools`](https://github.com/lukemnet/fastify-twitch-ebs-tools)
Useful functions for Twitch Extension Backend Services (EBS).
- [`fastify-type-provider-effect-schema`](https://github.com/daotl/fastify-type-provider-effect-schema)
Fastify
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [@effect/schema](https://github.com/effect-ts/schema).
- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
Fastify
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
for [zod](https://github.com/colinhacks/zod).
- [`fastify-typeorm-plugin`](https://github.com/inthepocket/fastify-typeorm-plugin)
Fastify plugin to work with TypeORM.
- [`fastify-user-agent`](https://github.com/Eomm/fastify-user-agent) parses your
request's `user-agent` header.
- [`fastify-uws`](https://github.com/geut/fastify-uws) A Fastify plugin to
use the web server [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js).
- [`fastify-vhost`](https://github.com/patrickpissurno/fastify-vhost) Proxy
subdomain HTTP requests to another server (useful if you want to point
multiple subdomains to the same IP address, while running different servers on
the same machine).
- [`fastify-vite`](https://github.com/galvez/fastify-vite)
[Vite](https://vitejs.dev/) plugin for Fastify with SSR data support.
- [`fastify-vue-plugin`](https://github.com/TheNoim/fastify-vue)
[Nuxt.js](https://nuxtjs.org) plugin for Fastify. Control the routes nuxt
should use.
- [`fastify-wamp-router`](https://github.com/lependu/fastify-wamp-router) Web
Application Messaging Protocol router for Fastify.
- [`fastify-web-response`](https://github.com/erfanium/fastify-web-response)
Enables returning web streams objects `Response` and `ReadableStream` in routes.
- [`fastify-webpack-hmr`](https://github.com/lependu/fastify-webpack-hmr)
Webpack hot module reloading plugin for Fastify.
- [`fastify-webpack-hot`](https://github.com/gajus/fastify-webpack-hot) Webpack
Hot Module Replacement for Fastify.
- [`fastify-ws`](https://github.com/gj/fastify-ws) WebSocket integration for
Fastify — with support for WebSocket lifecycle hooks instead of a single
handler function. Built upon [ws](https://github.com/websockets/ws) and
[uws](https://github.com/uNetworking/uWebSockets).
- [`fastify-xml-body-parser`](https://github.com/NaturalIntelligence/fastify-xml-body-parser)
Parse XML payload / request body into JS / JSON object.
- [`http-wizard`](https://github.com/flodlc/http-wizard)
Exports a typescript API client for your Fastify API and ensures fullstack type
safety for your project.
- [`i18next-http-middleware`](https://github.com/i18next/i18next-http-middleware#fastify-usage)
An [i18next](https://www.i18next.com) based i18n (internationalization)
middleware to be used with Node.js web frameworks like Express or Fastify and
also for Deno.
- [`k-fastify-gateway`](https://github.com/jkyberneees/fastify-gateway) API
Gateway plugin for Fastify, a low footprint implementation that uses the
`fastify-reply-from` HTTP proxy library.
- [`mercurius`](https://mercurius.dev/) A fully-featured and performant GraphQL
server implementation for Fastify.
- [`nstats`](https://github.com/Phara0h/nstats) A fast and compact way to get
all your network and process stats for your node application. Websocket,
HTTP/S, and prometheus compatible!
- [`oas-fastify`](https://github.com/ahmadnassri/node-oas-fastify) OAS 3.x to
Fastify routes automation. Automatically generates route handlers with fastify
configuration and validation.
- [`openapi-validator-middleware`](https://github.com/PayU/openapi-validator-middleware#fastify)
Swagger and OpenAPI 3.0 spec-based request validation middleware that supports
Fastify.
- [`pubsub-http-handler`](https://github.com/simenandre/pubsub-http-handler) A Fastify
plugin to easily create Google Cloud PubSub endpoints.
- [`sequelize-fastify`](https://github.com/hsynlms/sequelize-fastify) A simple
and lightweight Sequelize plugin for Fastify.
- [`typeorm-fastify-plugin`](https://github.com/jclemens24/fastify-typeorm) A simple
and updated Typeorm plugin for use with Fastify.
#### [Community Tools](#community-tools)
- [`@fastify-userland/workflows`](https://github.com/fastify-userland/workflows)
Reusable workflows for use in the Fastify plugin
- [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
generator by directory structure.
- [`fastify-flux`](https://github.com/Jnig/fastify-flux) Tool for building
Fastify APIs using decorators and convert Typescript interface to JSON Schema.
- [`jeasx`](https://www.jeasx.dev)
A flexible server-rendering framework built on Fastify
that leverages asynchronous JSX to simplify web development.
- [`simple-tjscli`](https://github.com/imjuni/simple-tjscli) CLI tool to
generate JSON Schema from TypeScript interfaces.
- [`vite-plugin-fastify`](https://github.com/Vanilla-IceCream/vite-plugin-fastify)
Fastify plugin for Vite with Hot-module Replacement.
- [`vite-plugin-fastify-routes`](https://github.com/Vanilla-IceCream/vite-plugin-fastify-routes)
File-based routing for Fastify applications using Vite.

126
node_modules/fastify/docs/Guides/Fluent-Schema.md generated vendored Normal file
View File

@@ -0,0 +1,126 @@
<h1 align="center">Fastify</h1>
## Fluent Schema
The [Validation and
Serialization](../Reference/Validation-and-Serialization.md) documentation
outlines all parameters accepted by Fastify to set up JSON Schema Validation to
validate the input, and JSON Schema Serialization to optimize the output.
[`fluent-json-schema`](https://github.com/fastify/fluent-json-schema) can be
used to simplify this task while allowing the reuse of constants.
### Basic settings
```js
const S = require('fluent-json-schema')
// You can have an object like this, or query a DB to get the values
const MY_KEYS = {
KEY1: 'ONE',
KEY2: 'TWO'
}
const bodyJsonSchema = S.object()
.prop('someKey', S.string())
.prop('someOtherKey', S.number())
.prop('requiredKey', S.array().maxItems(3).items(S.integer()).required())
.prop('nullableKey', S.mixed([S.TYPES.NUMBER, S.TYPES.NULL]))
.prop('multipleTypesKey', S.mixed([S.TYPES.BOOLEAN, S.TYPES.NUMBER]))
.prop('multipleRestrictedTypesKey', S.oneOf([S.string().maxLength(5), S.number().minimum(10)]))
.prop('enumKey', S.enum(Object.values(MY_KEYS)))
.prop('notTypeKey', S.not(S.array()))
const queryStringJsonSchema = S.object()
.prop('name', S.string())
.prop('excitement', S.integer())
const paramsJsonSchema = S.object()
.prop('par1', S.string())
.prop('par2', S.integer())
const headersJsonSchema = S.object()
.prop('x-foo', S.string().required())
// Note that there is no need to call `.valueOf()`!
const schema = {
body: bodyJsonSchema,
querystring: queryStringJsonSchema, // (or) query: queryStringJsonSchema
params: paramsJsonSchema,
headers: headersJsonSchema
}
fastify.post('/the/url', { schema }, handler)
```
### Reuse
With `fluent-json-schema`, you can manipulate your schemas more easily and
programmatically and then reuse them thanks to the `addSchema()` method. You can
refer to the schema in two different manners that are detailed in the
[Validation and
Serialization](../Reference/Validation-and-Serialization.md#adding-a-shared-schema)
documentation.
Here are some usage examples:
**`$ref-way`**: refer to an external schema.
```js
const addressSchema = S.object()
.id('#address')
.prop('line1').required()
.prop('line2')
.prop('country').required()
.prop('city').required()
.prop('zipcode').required()
const commonSchemas = S.object()
.id('https://fastify/demo')
.definition('addressSchema', addressSchema)
.definition('otherSchema', otherSchema) // You can add any schemas you need
fastify.addSchema(commonSchemas)
const bodyJsonSchema = S.object()
.prop('residence', S.ref('https://fastify/demo#address')).required()
.prop('office', S.ref('https://fastify/demo#/definitions/addressSchema')).required()
const schema = { body: bodyJsonSchema }
fastify.post('/the/url', { schema }, handler)
```
**`replace-way`**: refer to a shared schema to replace before the validation
process.
```js
const sharedAddressSchema = {
$id: 'sharedAddress',
type: 'object',
required: ['line1', 'country', 'city', 'zipcode'],
properties: {
line1: { type: 'string' },
line2: { type: 'string' },
country: { type: 'string' },
city: { type: 'string' },
zipcode: { type: 'string' }
}
}
fastify.addSchema(sharedAddressSchema)
const bodyJsonSchema = {
type: 'object',
properties: {
vacation: 'sharedAddress#'
}
}
const schema = { body: bodyJsonSchema }
fastify.post('/the/url', { schema }, handler)
```
NB You can mix up the `$ref-way` and the `replace-way` when using
`fastify.addSchema`.

620
node_modules/fastify/docs/Guides/Getting-Started.md generated vendored Normal file
View File

@@ -0,0 +1,620 @@
<h1 align="center">Fastify</h1>
## Getting Started
Hello! Thank you for checking out Fastify!
This document aims to be a gentle introduction to the framework and its
features. It is an elementary preface with examples and links to other parts of
the documentation.
Let's start!
### Install
<a id="install"></a>
Install with npm:
```sh
npm i fastify
```
Install with yarn:
```sh
yarn add fastify
```
### Your first server
<a id="first-server"></a>
Let's write our first server:
```js
// Require the framework and instantiate it
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
// Declare a route
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
// Run the server!
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
> If you are using ECMAScript Modules (ESM) in your project, be sure to
> include "type": "module" in your package.json.
>```js
>{
> "type": "module"
>}
>```
Do you prefer to use `async/await`? Fastify supports it out-of-the-box.
```js
// ESM
import Fastify from 'fastify'
const fastify = Fastify({
logger: true
})
// CommonJs
const fastify = require('fastify')({
logger: true
})
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
/**
* Run the server!
*/
const start = async () => {
try {
await fastify.listen({ port: 3000 })
} catch (err) {
fastify.log.error(err)
process.exit(1)
}
}
start()
```
Awesome, that was easy.
Unfortunately, writing a complex application requires significantly more code
than this example. A classic problem when you are building a new application is
how to handle multiple files, asynchronous bootstrapping, and the architecture
of your code.
Fastify offers an easy platform that helps to solve all of the problems outlined
above, and more!
> **Note**
> The above examples, and subsequent examples in this document, default to
> listening *only* on the localhost `127.0.0.1` interface. To listen on all
> available IPv4 interfaces the example should be modified to listen on
> `0.0.0.0` like so:
>
> ```js
> fastify.listen({ port: 3000, host: '0.0.0.0' }, function (err, address) {
> if (err) {
> fastify.log.error(err)
> process.exit(1)
> }
> fastify.log.info(`server listening on ${address}`)
> })
> ```
>
> Similarly, specify `::1` to accept only local connections via IPv6. Or specify
> `::` to accept connections on all IPv6 addresses, and, if the operating system
> supports it, also on all IPv4 addresses.
>
> When deploying to a Docker (or another type of) container using `0.0.0.0` or
> `::` would be the easiest method for exposing the application.
>
> Note that when using `0.0.0.0`, the address provided in the callback argument
> above will be the first address the wildcard refers to.
### Your first plugin
<a id="first-plugin"></a>
As with JavaScript, where everything is an object, with Fastify everything is a
plugin.
Before digging into it, let's see how it works!
Let's declare our basic server, but instead of declaring the route inside the
entry point, we'll declare it in an external file (check out the [route
declaration](../Reference/Routes.md) docs).
```js
// ESM
import Fastify from 'fastify'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = Fastify({
logger: true
})
fastify.register(firstRoute)
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
```js
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = require('fastify')({
logger: true
})
fastify.register(require('./our-first-route'))
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
```js
// our-first-route.js
/**
* Encapsulates the routes
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/
async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
//ESM
export default routes;
// CommonJs
module.exports = routes
```
In this example, we used the `register` API, which is the core of the Fastify
framework. It is the only way to add routes, plugins, et cetera.
At the beginning of this guide, we noted that Fastify provides a foundation that
assists with asynchronous bootstrapping of your application. Why is this
important?
Consider the scenario where a database connection is needed to handle data
storage. The database connection needs to be available before the server is
accepting connections. How do we address this problem?
A typical solution is to use a complex callback, or promises - a system that
will mix the framework API with other libraries and the application code.
Fastify handles this internally, with minimum effort!
Let's rewrite the above example with a database connection.
First, install `fastify-plugin` and `@fastify/mongodb`:
```sh
npm i fastify-plugin @fastify/mongodb
```
**server.js**
```js
// ESM
import Fastify from 'fastify'
import dbConnector from './our-db-connector.js'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = Fastify({
logger: true
})
fastify.register(dbConnector)
fastify.register(firstRoute)
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
```js
// CommonJs
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
const fastify = require('fastify')({
logger: true
})
fastify.register(require('./our-db-connector'))
fastify.register(require('./our-first-route'))
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
**our-db-connector.js**
```js
// ESM
import fastifyPlugin from 'fastify-plugin'
import fastifyMongo from '@fastify/mongodb'
/**
* @param {FastifyInstance} fastify
* @param {Object} options
*/
async function dbConnector (fastify, options) {
fastify.register(fastifyMongo, {
url: 'mongodb://localhost:27017/test_database'
})
}
// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
export default fastifyPlugin(dbConnector)
```
```js
// CommonJs
/**
* @type {import('fastify-plugin').FastifyPlugin}
*/
const fastifyPlugin = require('fastify-plugin')
/**
* Connects to a MongoDB database
* @param {FastifyInstance} fastify Encapsulated Fastify Instance
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/
async function dbConnector (fastify, options) {
fastify.register(require('@fastify/mongodb'), {
url: 'mongodb://localhost:27017/test_database'
})
}
// Wrapping a plugin function with fastify-plugin exposes the decorators
// and hooks, declared inside the plugin to the parent scope.
module.exports = fastifyPlugin(dbConnector)
```
**our-first-route.js**
```js
/**
* A plugin that provide encapsulated routes
* @param {FastifyInstance} fastify encapsulated fastify instance
* @param {Object} options plugin options, refer to https://fastify.dev/docs/latest/Reference/Plugins/#plugin-options
*/
async function routes (fastify, options) {
const collection = fastify.mongo.db.collection('test_collection')
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
fastify.get('/animals', async (request, reply) => {
const result = await collection.find().toArray()
if (result.length === 0) {
throw new Error('No documents found')
}
return result
})
fastify.get('/animals/:animal', async (request, reply) => {
const result = await collection.findOne({ animal: request.params.animal })
if (!result) {
throw new Error('Invalid value')
}
return result
})
const animalBodyJsonSchema = {
type: 'object',
required: ['animal'],
properties: {
animal: { type: 'string' },
},
}
const schema = {
body: animalBodyJsonSchema,
}
fastify.post('/animals', { schema }, async (request, reply) => {
// we can use the `request.body` object to get the data sent by the client
const result = await collection.insertOne({ animal: request.body.animal })
return result
})
}
module.exports = routes
```
Wow, that was fast!
Let's recap what we have done here since we've introduced some new concepts.
As you can see, we used `register` for both the database connector and the
registration of the routes.
This is one of the best features of Fastify, it will load your plugins in the
same order you declare them, and it will load the next plugin only once the
current one has been loaded. In this way, we can register the database connector
in the first plugin and use it in the second *(read
[here](../Reference/Plugins.md#handle-the-scope) to understand how to handle the
scope of a plugin)*.
Plugin loading starts when you call `fastify.listen()`, `fastify.inject()` or
`fastify.ready()`
The MongoDB plugin uses the `decorate` API to add custom objects to the Fastify
instance, making them available for use everywhere. Use of this API is
encouraged to facilitate easy code reuse and to decrease code or logic
duplication.
To dig deeper into how Fastify plugins work, how to develop new plugins, and for
details on how to use the whole Fastify API to deal with the complexity of
asynchronously bootstrapping an application, read [the hitchhiker's guide to
plugins](./Plugins-Guide.md).
### Loading order of your plugins
<a id="plugin-loading-order"></a>
To guarantee consistent and predictable behavior of your application, we highly
recommend to always load your code as shown below:
```
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
```
In this way, you will always have access to all of the properties declared in
the current scope.
As discussed previously, Fastify offers a solid encapsulation model, to help you
build your application as independent services. If you want to
register a plugin only for a subset of routes, you just have to replicate the
above structure.
```
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
└── service A
│ └── plugins (from the Fastify ecosystem)
│ └── your plugins (your custom plugins)
│ └── decorators
│ └── hooks
│ └── your services
└── service B
└── plugins (from the Fastify ecosystem)
└── your plugins (your custom plugins)
└── decorators
└── hooks
└── your services
```
### Validate your data
<a id="validate-data"></a>
Data validation is extremely important and a core concept of the framework.
To validate incoming requests, Fastify uses [JSON
Schema](https://json-schema.org/).
Let's look at an example demonstrating validation for routes:
```js
/**
* @type {import('fastify').RouteShorthandOptions}
* @const
*/
const opts = {
schema: {
body: {
type: 'object',
properties: {
someKey: { type: 'string' },
someOtherKey: { type: 'number' }
}
}
}
}
fastify.post('/', opts, async (request, reply) => {
return { hello: 'world' }
})
```
This example shows how to pass an options object to the route, which accepts a
`schema` key that contains all of the schemas for route, `body`, `querystring`,
`params`, and `headers`.
Read [Validation and
Serialization](../Reference/Validation-and-Serialization.md) to learn more.
### Serialize your data
<a id="serialize-data"></a>
Fastify has first-class support for JSON. It is extremely optimized to parse
JSON bodies and serialize JSON output.
To speed up JSON serialization (yes, it is slow!) use the `response` key of the
schema option as shown in the following example:
```js
/**
* @type {import('fastify').RouteShorthandOptions}
* @const
*/
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, async (request, reply) => {
return { hello: 'world' }
})
```
By specifying a schema as shown, you can speed up serialization by a factor of
2-3. This also helps to protect against leakage of potentially sensitive data,
since Fastify will serialize only the data present in the response schema. Read
[Validation and Serialization](../Reference/Validation-and-Serialization.md) to
learn more.
### Parsing request payloads
<a id="request-payload"></a>
Fastify parses `'application/json'` and `'text/plain'` request payloads
natively, with the result accessible from the [Fastify
request](../Reference/Request.md) object at `request.body`.
The following example returns the parsed body of a request back to the client:
```js
/**
* @type {import('fastify').RouteShorthandOptions}
*/
const opts = {}
fastify.post('/', opts, async (request, reply) => {
return request.body
})
```
Read [Content-Type Parser](../Reference/ContentTypeParser.md) to learn more
about Fastify's default parsing functionality and how to support other content
types.
### Extend your server
<a id="extend-server"></a>
Fastify is built to be extremely extensible and minimal, we believe that a
bare-bones framework is all that is necessary to make great applications
possible.
In other words, Fastify is not a "batteries included" framework, and relies on
an amazing [ecosystem](./Ecosystem.md)!
### Test your server
<a id="test-server"></a>
Fastify does not offer a testing framework, but we do recommend a way to write
your tests that uses the features and architecture of Fastify.
Read the [testing](./Testing.md) documentation to learn more!
### Run your server from CLI
<a id="cli"></a>
Fastify also has CLI integration via
[fastify-cli](https://github.com/fastify/fastify-cli),
a separate tool for scaffolding and managing Fastify projects.
First, install `fastify-cli`:
```sh
npm i fastify-cli
```
You can also install it globally with `-g`.
Then, add the following lines to `package.json`:
```json
{
"scripts": {
"start": "fastify start server.js"
}
}
```
And create your server file(s):
```js
// server.js
'use strict'
module.exports = async function (fastify, opts) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
```
Then run your server with:
```bash
npm start
```
### Slides and Videos
<a id="slides"></a>
- Slides
- [Take your HTTP server to ludicrous
speed](https://mcollina.github.io/take-your-http-server-to-ludicrous-speed)
by [@mcollina](https://github.com/mcollina)
- [What if I told you that HTTP can be
fast](https://delvedor.github.io/What-if-I-told-you-that-HTTP-can-be-fast)
by [@delvedor](https://github.com/delvedor)
- Videos
- [Take your HTTP server to ludicrous
speed](https://www.youtube.com/watch?v=5z46jJZNe8k) by
[@mcollina](https://github.com/mcollina)
- [What if I told you that HTTP can be
fast](https://www.webexpo.net/prague2017/talk/what-if-i-told-you-that-http-can-be-fast/)
by [@delvedor](https://github.com/delvedor)

43
node_modules/fastify/docs/Guides/Index.md generated vendored Normal file
View File

@@ -0,0 +1,43 @@
<h1 align="center">Fastify</h1>
## Guides Table Of Contents
<a id="guides-toc"></a>
This table of contents is in alphabetical order.
+ [Benchmarking](./Benchmarking.md): This guide introduces how to benchmark
applications based on Fastify.
+ [Contributing](./Contributing.md): Details how to participate in the
development of Fastify, and shows how to setup an environment compatible with
the project's code style.
+ [Delay Accepting Requests](./Delay-Accepting-Requests.md): A practical guide
on how to delay serving requests to specific routes until some condition is
met in your application. This guide focuses on solving the problem using
[`Hooks`](../Reference/Hooks.md), [`Decorators`](../Reference/Decorators.md),
and [`Plugins`](../Reference/Plugins.md).
+ [Detecting When Clients Abort](./Detecting-When-Clients-Abort.md): A
practical guide on detecting if and when a client aborts a request.
+ [Ecosystem](./Ecosystem.md): Lists all core plugins and many known community
plugins.
+ [Fluent Schema](./Fluent-Schema.md): Shows how JSON Schema can be
written with a fluent API and used in Fastify.
+ [Getting Started](./Getting-Started.md): Introduction tutorial for Fastify.
This is where beginners should start.
+ [Migration Guide (v4)](./Migration-Guide-V4.md): Details how to migrate to
Fastify v4 from earlier versions.
+ [Migration Guide (v3)](./Migration-Guide-V3.md): Details how to migrate to
Fastify v3 from earlier versions.
+ [Plugins Guide](./Plugins-Guide.md): An informal introduction to writing
Fastify plugins.
+ [Prototype Poisoning](./Prototype-Poisoning.md): A description of how the
prototype poisoning attack works and is mitigated.
+ [Recommendations](./Recommendations.md): Recommendations for how to deploy
Fastify into production environments.
+ [Serverless](./Serverless.md): Details on how to deploy Fastify applications
in various Function as a Service (FaaS) environments.
+ [Style Guide](./Style-Guide.md): Explains the writing style we use for the
Fastify documentation for those who want to contribute documentation.
+ [Testing](./Testing.md): Explains how to write unit tests for Fastify
applications.
+ [Write Plugin](./Write-Plugin.md): A set of guidelines for what the Fastify
team considers good practices for writing a Fastify plugin.

287
node_modules/fastify/docs/Guides/Migration-Guide-V3.md generated vendored Normal file
View File

@@ -0,0 +1,287 @@
# V3 Migration Guide
This guide is intended to help with migration from Fastify v2 to v3.
Before beginning please ensure that any deprecation warnings from v2 are fixed.
All v2 deprecations have been removed and they will no longer work after
upgrading. ([#1750](https://github.com/fastify/fastify/pull/1750))
## Breaking changes
### Changed middleware support ([#2014](https://github.com/fastify/fastify/pull/2014))
From Fastify v3, middleware support does not come out-of-the-box with the
framework itself.
If you use Express middleware in your application, please install and register
the [`@fastify/express`](https://github.com/fastify/fastify-express) or
[`@fastify/middie`](https://github.com/fastify/middie) plugin before doing so.
**v2:**
```js
// Using the Express `cors` middleware in Fastify v2.
fastify.use(require('cors')());
```
**v3:**
```js
// Using the Express `cors` middleware in Fastify v3.
await fastify.register(require('@fastify/express'));
fastify.use(require('cors')());
```
### Changed logging serialization ([#2017](https://github.com/fastify/fastify/pull/2017))
The logging [Serializers](../Reference/Logging.md) have been updated to now
Fastify [`Request`](../Reference/Request.md) and
[`Reply`](../Reference/Reply.md) objects instead of native ones.
Any custom serializers must be updated if they rely upon `request` or `reply`
properties that are present on the native objects but not the Fastify objects.
**v2:**
```js
const fastify = require('fastify')({
logger: {
serializers: {
res(res) {
return {
statusCode: res.statusCode,
customProp: res.customProp
};
}
}
}
});
```
**v3:**
```js
const fastify = require('fastify')({
logger: {
serializers: {
res(reply) {
return {
statusCode: reply.statusCode, // No change required
customProp: reply.raw.customProp // Log custom property from res object
};
}
}
}
});
```
### Changed schema substitution ([#2023](https://github.com/fastify/fastify/pull/2023))
The non-standard `replace-way` shared schema support has been removed. This
feature has been replaced with JSON Schema specification compliant `$ref` based
substitution. To help understand this change read [Validation and Serialization
in Fastify
v3](https://dev.to/eomm/validation-and-serialization-in-fastify-v3-2e8l).
**v2:**
```js
const schema = {
body: 'schemaId#'
};
fastify.route({ method, url, schema, handler });
```
**v3:**
```js
const schema = {
body: {
$ref: 'schemaId#'
}
};
fastify.route({ method, url, schema, handler });
```
### Changed schema validation options ([#2023](https://github.com/fastify/fastify/pull/2023))
The `setSchemaCompiler` and `setSchemaResolver` options have been replaced with
the `setValidatorCompiler` to enable future tooling improvements. To help
understand this change read [Validation and Serialization in Fastify
v3](https://dev.to/eomm/validation-and-serialization-in-fastify-v3-2e8l).
**v2:**
```js
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setSchemaCompiler(schema => ajv.compile(schema));
fastify.setSchemaResolver(ref => ajv.getSchema(ref).schema);
```
**v3:**
```js
const fastify = Fastify();
const ajv = new AJV();
ajv.addSchema(schemaA);
ajv.addSchema(schemaB);
fastify.setValidatorCompiler(({ schema, method, url, httpPart }) =>
ajv.compile(schema)
);
```
### Changed preParsing hook behavior ([#2286](https://github.com/fastify/fastify/pull/2286))
From Fastify v3, the behavior of the `preParsing` hook will change slightly
to support request payload manipulation.
The hook now takes an additional argument, `payload`, and therefore the new hook
signature is `fn(request, reply, payload, done)` or `async fn(request, reply,
payload)`.
The hook can optionally return a new stream via `done(null, stream)` or
returning the stream in case of async functions.
If the hook returns a new stream, it will be used instead of the original one in
subsequent hooks. A sample use case for this is handling compressed requests.
The new stream should add the `receivedEncodedLength` property to the stream
that should reflect the actual data size received from the client. For instance,
in a compressed request it should be the size of the compressed payload. This
property can (and should) be dynamically updated during `data` events.
The old syntax of Fastify v2 without payload is supported but it is deprecated.
### Changed hooks behavior ([#2004](https://github.com/fastify/fastify/pull/2004))
From Fastify v3, the behavior of `onRoute` and `onRegister` hooks will change
slightly to support hook encapsulation.
- `onRoute` - The hook will be called asynchronously. The hook is now inherited
when registering a new plugin within the same encapsulation scope. Thus, this
hook should be registered _before_ registering any plugins.
- `onRegister` - Same as the onRoute hook. The only difference is that now the
very first call will no longer be the framework itself, but the first
registered plugin.
### Changed Content Type Parser syntax ([#2286](https://github.com/fastify/fastify/pull/2286))
In Fastify v3 the content type parsers now have a single signature for parsers.
The new signatures are `fn(request, payload, done)` or `async fn(request,
payload)`. Note that `request` is now a Fastify request, not an
`IncomingMessage`. The payload is, by default, a stream. If the `parseAs` option
is used in `addContentTypeParser`, then `payload` reflects the option value
(string or buffer).
The old signatures `fn(req, [done])` or `fn(req, payload, [done])` (where `req`
is `IncomingMessage`) are still supported but are deprecated.
### Changed TypeScript support
The type system was changed in Fastify version 3. The new type system introduces
generic constraining and defaulting, plus a new way to define schema types such
as a request body, querystring, and more!
**v2:**
```ts
interface PingQuerystring {
foo?: number;
}
interface PingParams {
bar?: string;
}
interface PingHeaders {
a?: string;
}
interface PingBody {
baz?: string;
}
server.get<PingQuerystring, PingParams, PingHeaders, PingBody>(
'/ping/:bar',
opts,
(request, reply) => {
console.log(request.query); // This is of type `PingQuerystring`
console.log(request.params); // This is of type `PingParams`
console.log(request.headers); // This is of type `PingHeaders`
console.log(request.body); // This is of type `PingBody`
}
);
```
**v3:**
```ts
server.get<{
Querystring: PingQuerystring;
Params: PingParams;
Headers: PingHeaders;
Body: PingBody;
}>('/ping/:bar', opts, async (request, reply) => {
console.log(request.query); // This is of type `PingQuerystring`
console.log(request.params); // This is of type `PingParams`
console.log(request.headers); // This is of type `PingHeaders`
console.log(request.body); // This is of type `PingBody`
});
```
### Manage uncaught exception ([#2073](https://github.com/fastify/fastify/pull/2073))
In sync route handlers, if an error was thrown the server crashed by design
without calling the configured `.setErrorHandler()`. This has changed and now
all unexpected errors in sync and async routes are managed.
**v2:**
```js
fastify.setErrorHandler((error, request, reply) => {
// this is NOT called
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // Thrown: [].substr is not a function and crash the server
})
```
**v3:**
```js
fastify.setErrorHandler((error, request, reply) => {
// this IS called
reply.send(error)
})
fastify.get('/', (request, reply) => {
const maybeAnArray = request.body.something ? [] : 'I am a string'
maybeAnArray.substr() // Thrown: [].substr is not a function, but it is handled
})
```
## Further additions and improvements
- Hooks now have consistent context regardless of how they are registered
([#2005](https://github.com/fastify/fastify/pull/2005))
- Deprecated `request.req` and `reply.res` for
[`request.raw`](../Reference/Request.md) and
[`reply.raw`](../Reference/Reply.md)
([#2008](https://github.com/fastify/fastify/pull/2008))
- Removed `modifyCoreObjects` option
([#2015](https://github.com/fastify/fastify/pull/2015))
- Added [`connectionTimeout`](../Reference/Server.md#factory-connection-timeout)
option ([#2086](https://github.com/fastify/fastify/pull/2086))
- Added [`keepAliveTimeout`](../Reference/Server.md#factory-keep-alive-timeout)
option ([#2086](https://github.com/fastify/fastify/pull/2086))
- Added async-await support for [plugins](../Reference/Plugins.md#async-await)
([#2093](https://github.com/fastify/fastify/pull/2093))
- Added the feature to throw object as error
([#2134](https://github.com/fastify/fastify/pull/2134))

270
node_modules/fastify/docs/Guides/Migration-Guide-V4.md generated vendored Normal file
View File

@@ -0,0 +1,270 @@
# V4 Migration Guide
This guide is intended to help with migration from Fastify v3 to v4.
Before migrating to v4, please ensure that you have fixed all deprecation
warnings from v3. All v3 deprecations have been removed and they will no longer
work after upgrading.
## Codemods
### Fastify v4 Codemods
To help with the upgrade, weve worked with the team at
[Codemod](https://github.com/codemod-com/codemod) to
publish codemods that will automatically update your code to many of
the new APIs and patterns in Fastify v4.
Run the following
[migration recipe](https://go.codemod.com/fastify-4-migration-recipe) to
automatically update your code to Fastify v4:
```
npx codemod@latest fastify/4/migration-recipe
```
This will run the following codemods:
- [`fastify/4/remove-app-use`](https://go.codemod.com/fastify-4-remove-app-use)
- [`fastify/4/reply-raw-access`](https://go.codemod.com/fastify-4-reply-raw-access)
- [`fastify/4/wrap-routes-plugin`](https://go.codemod.com/fastify-4-wrap-routes-plugin)
- [`fastify/4/await-register-calls`](https://go.codemod.com/fastify-4-await-register-calls)
Each of these codemods automates the changes listed in the v4 migration guide.
For a complete list of available Fastify codemods and further details,
see [Codemod Registry](https://go.codemod.com/fastify).
## Breaking Changes
### Error handling composition ([#3261](https://github.com/fastify/fastify/pull/3261))
When an error is thrown in an async error handler function, the upper-level
error handler is executed if set. If there is no upper-level error handler,
the default will be executed as it was previously:
```js
import Fastify from 'fastify'
const fastify = Fastify()
fastify.register(async fastify => {
fastify.setErrorHandler(async err => {
console.log(err.message) // 'kaboom'
throw new Error('caught')
})
fastify.get('/encapsulated', async () => {
throw new Error('kaboom')
})
})
fastify.setErrorHandler(async err => {
console.log(err.message) // 'caught'
throw new Error('wrapped')
})
const res = await fastify.inject('/encapsulated')
console.log(res.json().message) // 'wrapped'
```
>The root error handler is Fastifys generic error handler.
>This error handler will use the headers and status code in the Error object,
>if they exist. **The headers and status code will not be automatically set if
>a custom error handler is provided**.
### Removed `app.use()` ([#3506](https://github.com/fastify/fastify/pull/3506))
With v4 of Fastify, `app.use()` has been removed and the use of middleware is
no longer supported.
If you need to use middleware, use
[`@fastify/middie`](https://github.com/fastify/middie) or
[`@fastify/express`](https://github.com/fastify/fastify-express), which will
continue to be maintained.
However, it is strongly recommended that you migrate to Fastify's [hooks](../Reference/Hooks.md).
> **Note**: Codemod remove `app.use()` with:
>
> ```bash
> npx codemod@latest fastify/4/remove-app-use
> ```
### `reply.res` moved to `reply.raw`
If you previously used the `reply.res` attribute to access the underlying Request
object you will now need to use `reply.raw`.
> **Note**: Codemod `reply.res` to `reply.raw` with:
>
> ```bash
> npx codemod@latest fastify/4/reply-raw-access
> ```
### Need to `return reply` to signal a "fork" of the promise chain
In some situations, like when a response is sent asynchronously or when you are
not explicitly returning a response, you will now need to return the `reply`
argument from your router handler.
### `exposeHeadRoutes` true by default
Starting with v4, every `GET` route will create a sibling `HEAD` route.
You can revert this behavior by setting `exposeHeadRoutes: false` in the server options.
### Synchronous route definitions ([#2954](https://github.com/fastify/fastify/pull/2954))
To improve error reporting in route definitions, route registration is now synchronous.
As a result, if you specify an `onRoute` hook in a plugin you should now either:
* wrap your routes in a plugin (recommended)
For example, refactor this:
```js
fastify.register((instance, opts, done) => {
instance.addHook('onRoute', (routeOptions) => {
const { path, method } = routeOptions;
console.log({ path, method });
done();
});
});
fastify.get('/', (request, reply) => { reply.send('hello') });
```
Into this:
```js
fastify.register((instance, opts, done) => {
instance.addHook('onRoute', (routeOptions) => {
const { path, method } = routeOptions;
console.log({ path, method });
done();
});
});
fastify.register((instance, opts, done) => {
instance.get('/', (request, reply) => { reply.send('hello') });
done();
});
```
> **Note**: Codemod synchronous route definitions with:
>
> ```bash
> npx codemod@latest fastify/4/wrap-routes-plugin
> ```
* use `await register(...)`
For example, refactor this:
```js
fastify.register((instance, opts, done) => {
instance.addHook('onRoute', (routeOptions) => {
const { path, method } = routeOptions;
console.log({ path, method });
});
done();
});
```
Into this:
```js
await fastify.register((instance, opts, done) => {
instance.addHook('onRoute', (routeOptions) => {
const { path, method } = routeOptions;
console.log({ path, method });
});
done();
});
```
> **Note**: Codemod 'await register(...)' with:
>
> ```bash
> npx codemod@latest fastify/4/await-register-calls
> ```
### Optional URL parameters
If you've already used any implicitly optional parameters, you'll get a 404
error when trying to access the route. You will now need to declare the
optional parameters explicitly.
For example, if you have the same route for listing and showing a post,
refactor this:
```js
fastify.get('/posts/:id', (request, reply) => {
const { id } = request.params;
});
```
Into this:
```js
fastify.get('/posts/:id?', (request, reply) => {
const { id } = request.params;
});
```
## Non-Breaking Changes
### Deprecation of variadic `.listen()` signature
The [variadic signature](https://en.wikipedia.org/wiki/Variadic_function) of the
`fastify.listen()` method is now deprecated.
Before this release, the following invocations of this method were valid:
- `fastify.listen(8000)`
- `fastify.listen(8000, 127.0.0.1)`
- `fastify.listen(8000, 127.0.0.1, 511)`
- `fastify.listen(8000, (err) => { if (err) throw err })`
- `fastify.listen({ port: 8000 }, (err) => { if (err) throw err })`
With Fastify v4, only the following invocations are valid:
- `fastify.listen()`
- `fastify.listen({ port: 8000 })`
- `fastify.listen({ port: 8000 }, (err) => { if (err) throw err })`
### Change of schema for multiple types
Ajv has been upgraded to v8 in Fastify v4, meaning "type" keywords with multiple
types other than "null"
[are now prohibited](https://ajv.js.org/strict-mode.html#strict-types).
You may encounter a console warning such as:
```sh
strict mode: use allowUnionTypes to allow union type keyword at "#/properties/image" (strictTypes)
```
As such, schemas like below will need to be changed from:
```js
{
type: 'object',
properties: {
api_key: { type: 'string' },
image: { type: ['object', 'array'] }
}
}
```
Into:
```js
{
type: 'object',
properties: {
api_key: { type: 'string' },
image: {
anyOf: [
{ type: 'array' },
{ type: 'object' }
]
}
}
}
```
### Add `reply.trailers` methods ([#3794](https://github.com/fastify/fastify/pull/3794))
Fastify now supports the [HTTP Trailer] response headers.
[HTTP Trailer]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer

718
node_modules/fastify/docs/Guides/Migration-Guide-V5.md generated vendored Normal file
View File

@@ -0,0 +1,718 @@
# V5 Migration Guide
This guide is intended to help with migration from Fastify v4 to v5.
Before migrating to v5, please ensure that you have fixed all deprecation
warnings from v4. All v4 deprecations have been removed and will no longer
work after upgrading.
## Long Term Support Cycle
Fastify v5 will only support Node.js v20+. If you are using an older version of
Node.js, you will need to upgrade to a newer version to use Fastify v5.
Fastify v4 is still supported until June 30, 2025. If you are unable to upgrade,
you should consider buying an end-of-life support plan from HeroDevs.
### Why Node.js v20?
Fastify v5 will only support Node.js v20+ because it has significant differences
compared to v18, such as
better support for `node:test`. This allows us to provide a better developer
experience and streamline maintenance.
Node.js v18 will exit Long Term Support on April 30, 2025, so you should be planning
to upgrade to v20 anyway.
## Breaking Changes
### Full JSON Schema is now required for `querystring`, `params` and `body` and response schemas
Starting with v5, Fastify will require a full JSON schema for the `querystring`,
`params` and `body` schema. Note that the `jsonShortHand` option has been
removed as well.
If the default JSON Schema validator is used, you will need
to provide a full JSON schema for the
`querystring`, `params`, `body`, and `response` schemas,
including the `type` property.
```js
// v4
fastify.get('/route', {
schema: {
querystring: {
name: { type: 'string' }
}
}
}, (req, reply) => {
reply.send({ hello: req.query.name });
});
```
```js
// v5
fastify.get('/route', {
schema: {
querystring: {
type: 'object',
properties: {
name: { type: 'string' }
},
required: ['name']
}
}
}, (req, reply) => {
reply.send({ hello: req.query.name });
});
```
See [#5586](https://github.com/fastify/fastify/pull/5586) for more details
Note that it's still possible to override the JSON Schema validator to
use a different format, such as Zod. This change simplifies that as well.
This change helps with integration of other tools, such as
[`@fastify/swagger`](https://github.com/fastify/fastify-swagger).
### New logger constructor signature
In Fastify v4, Fastify accepted the options to build a pino
logger in the `logger` option, as well as a custom logger instance.
This was the source of significant confusion.
As a result, the `logger` option will not accept a custom logger anymore in v5.
To use a custom logger, you should use the `loggerInstance` option instead:
```js
// v4
const logger = require('pino')();
const fastify = require('fastify')({
logger
});
```
```js
// v5
const loggerInstance = require('pino')();
const fastify = require('fastify')({
loggerInstance
});
```
### `useSemicolonDelimiter` false by default
Starting with v5, Fastify instances will no longer default to supporting the use
of semicolon delimiters in the query string as they did in v4.
This is due to it being non-standard
behavior and not adhering to [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.4).
If you still wish to use semicolons as delimiters, you can do so by
setting `useSemicolonDelimiter: true` in the server configuration.
```js
const fastify = require('fastify')({
useSemicolonDelimiter: true
});
```
### The parameters object no longer has a prototype
In v4, the `parameters` object had a prototype. This is no longer the case in v5.
This means that you can no longer access properties inherited from `Object` on
the `parameters` object, such as `toString` or `hasOwnProperty`.
```js
// v4
fastify.get('/route/:name', (req, reply) => {
console.log(req.params.hasOwnProperty('name')); // true
return { hello: req.params.name };
});
```
```js
// v5
fastify.get('/route/:name', (req, reply) => {
console.log(Object.hasOwn(req.params, 'name')); // true
return { hello: req.params.name };
});
```
This increases the security of the application by hardening against prototype
pollution attacks.
### Type Providers now differentiate between validator and serializer schemas
In v4, the type providers had the same types for both validation and serialization.
In v5, the type providers have been split into two separate types: `ValidatorSchema`
and `SerializerSchema`.
[`@fastify/type-provider-json-schema-to-ts`](https://github.com/fastify/fastify-type-provider-json-schema-to-ts)
and
[`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
have already been updated: upgrade to the latest version to get the new types.
If you are using a custom type provider, you will need to modify it like
the following:
```
--- a/index.ts
+++ b/index.ts
@@ -11,7 +11,8 @@ import {
import { FromSchema, FromSchemaDefaultOptions, FromSchemaOptions, JSONSchema } from 'json-schema-to-ts'
export interface JsonSchemaToTsProvider<
Options extends FromSchemaOptions = FromSchemaDefaultOptions
> extends FastifyTypeProvider {
- output: this['input'] extends JSONSchema ? FromSchema<this['input'], Options> : unknown;
+ validator: this['schema'] extends JSONSchema ? FromSchema<this['schema'], Options> : unknown;
+ serializer: this['schema'] extends JSONSchema ? FromSchema<this['schema'], Options> : unknown;
}
```
### Changes to the .listen() method
The variadic argument signature of the `.listen()` method has been removed.
This means that you can no longer call `.listen()` with a variable number of arguments.
```js
// v4
fastify.listen(8000)
```
Will become:
```js
// v5
fastify.listen({ port: 8000 })
```
This was already deprecated in v4 as `FSTDEP011`, so you should have already updated
your code to use the new signature.
### Direct return of trailers has been removed
In v4, you could directly return trailers from a handler.
This is no longer possible in v5.
```js
// v4
fastify.get('/route', (req, reply) => {
reply.trailer('ETag', function (reply, payload) {
return 'custom-etag'
})
reply.send('')
});
```
```js
// v5
fastify.get('/route', (req, reply) => {
reply.trailer('ETag', async function (reply, payload) {
return 'custom-etag'
})
reply.send('')
});
```
A callback could also be used.
This was already deprecated in v4 as `FSTDEP013`,
so you should have already updated your code to use the new signature.
### Streamlined access to route definition
All deprecated properties relating to accessing the route definition have been removed
and are now accessed via `request.routeOptions`.
| Code | Description | How to solve | Discussion |
| ---- | ----------- | ------------ | ---------- |
| FSTDEP012 | You are trying to access the deprecated `request.context` property. | Use `request.routeOptions.config` or `request.routeOptions.schema`. | [#4216](https://github.com/fastify/fastify/pull/4216) [#5084](https://github.com/fastify/fastify/pull/5084) |
| FSTDEP015 | You are accessing the deprecated `request.routeSchema` property. | Use `request.routeOptions.schema`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| FSTDEP016 | You are accessing the deprecated `request.routeConfig` property. | Use `request.routeOptions.config`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| FSTDEP017 | You are accessing the deprecated `request.routerPath` property. | Use `request.routeOptions.url`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| FSTDEP018 | You are accessing the deprecated `request.routerMethod` property. | Use `request.routeOptions.method`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| FSTDEP019 | You are accessing the deprecated `reply.context` property. | Use `reply.routeOptions.config` or `reply.routeOptions.schema`. | [#5032](https://github.com/fastify/fastify/pull/5032) [#5084](https://github.com/fastify/fastify/pull/5084) |
See [#5616](https://github.com/fastify/fastify/pull/5616) for more information.
### `reply.redirect()` has a new signature
The `reply.redirect()` method has a new signature:
`reply.redirect(url: string, code?: number)`.
```js
// v4
reply.redirect(301, '/new-route')
```
Change it to:
```js
// v5
reply.redirect('/new-route', 301)
```
This was already deprecated in v4 as `FSTDEP021`, so you should have already
updated your code to use the new signature.
### Modifying `reply.sent` is now forbidden
In v4, you could modify the `reply.sent` property to prevent the response from
being sent.
This is no longer possible in v5, use `reply.hijack()` instead.
```js
// v4
fastify.get('/route', (req, reply) => {
reply.sent = true;
reply.raw.end('hello');
});
```
Change it to:
```js
// v5
fastify.get('/route', (req, reply) => {
reply.hijack();
reply.raw.end('hello');
});
```
This was already deprecated in v4 as `FSTDEP010`, so you should have already
updated your code to use the new signature.
### Constraints for route versioning signature changes
We changed the signature for route versioning constraints.
The `version` and `versioning` options have been removed and you should
use the `constraints` option instead.
| Code | Description | How to solve | Discussion |
| ---- | ----------- | ------------ | ---------- |
| FSTDEP008 | You are using route constraints via the route `{version: "..."}` option. | Use `{constraints: {version: "..."}}` option. | [#2682](https://github.com/fastify/fastify/pull/2682) |
| FSTDEP009 | You are using a custom route versioning strategy via the server `{versioning: "..."}` option. | Use `{constraints: {version: "..."}}` option. | [#2682](https://github.com/fastify/fastify/pull/2682) |
### `HEAD` routes requires to register before `GET` when `exposeHeadRoutes: true`
We have a more strict requirement for custom `HEAD` route when
`exposeHeadRoutes: true`.
When you provides a custom `HEAD` route, you must either explicitly
set `exposeHeadRoutes` to `false`
```js
// v4
fastify.get('/route', {
}, (req, reply) => {
reply.send({ hello: 'world' });
});
fastify.head('/route', (req, reply) => {
// ...
});
```
```js
// v5
fastify.get('/route', {
exposeHeadRoutes: false
}, (req, reply) => {
reply.send({ hello: 'world' });
});
fastify.head('/route', (req, reply) => {
// ...
});
```
or place the `HEAD` route before `GET`.
```js
// v5
fastify.head('/route', (req, reply) => {
// ...
});
fastify.get('/route', {
}, (req, reply) => {
reply.send({ hello: 'world' });
});
```
This was changed in [#2700](https://github.com/fastify/fastify/pull/2700),
and the old behavior was deprecated in v4 as `FSTDEP007`.
### Removed `request.connection`
The `request.connection` property has been removed in v5.
You should use `request.socket` instead.
```js
// v4
fastify.get('/route', (req, reply) => {
console.log(req.connection.remoteAddress);
return { hello: 'world' };
});
```
```js
// v5
fastify.get('/route', (req, reply) => {
console.log(req.socket.remoteAddress);
return { hello: 'world' };
});
```
This was already deprecated in v4 as `FSTDEP05`, so you should
have already updated your code to use the new signature.
### `reply.getResponseTime()` has been removed, use `reply.elapsedTime` instead
The `reply.getResponseTime()` method has been removed in v5.
You should use `reply.elapsedTime` instead.
```js
// v4
fastify.get('/route', (req, reply) => {
console.log(reply.getResponseTime());
return { hello: 'world' };
});
```
```js
// v5
fastify.get('/route', (req, reply) => {
console.log(reply.elapsedTime);
return { hello: 'world' };
});
```
This was already deprecated in v4 as `FSTDEP20`, so you should have already
updated your code to use the new signature.
### `fastify.hasRoute()` now matches the behavior of `find-my-way`
The `fastify.hasRoute()` method now matches the behavior of `find-my-way`
and requires the route definition to be passed as it is defined in the route.
```js
// v4
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
console.log(fastify.hasRoute({
method: 'GET',
url: '/example/12345.png'
)); // true
```
```js
// v5
fastify.get('/example/:file(^\\d+).png', function (request, reply) { })
console.log(fastify.hasRoute({
method: 'GET',
url: '/example/:file(^\\d+).png'
)); // true
```
### Removal of some non-standard HTTP methods
We have removed the following HTTP methods from Fastify:
- `PROPFIND`
- `PROPPATCH`
- `MKCOL`
- `COPY`
- `MOVE`
- `LOCK`
- `UNLOCK`
- `TRACE`
- `SEARCH`
It's now possible to add them back using the `addHttpMethod` method.
```js
const fastify = Fastify()
// add a new http method on top of the default ones:
fastify.addHttpMethod('REBIND')
// add a new HTTP method that accepts a body:
fastify.addHttpMethod('REBIND', { hasBody: true })
// reads the HTTP methods list:
fastify.supportedMethods // returns a string array
```
See [#5567](https://github.com/fastify/fastify/pull/5567) for more
information.
### Removed support from reference types in decorators
Decorating Request/Reply with a reference type (`Array`, `Object`)
is now prohibited as this reference is shared amongst all requests.
```js
// v4
fastify.decorateRequest('myObject', { hello: 'world' });
```
```js
// v5
fastify.decorateRequest('myObject');
fastify.addHook('onRequest', async (req, reply) => {
req.myObject = { hello: 'world' };
});
```
or turn it into a function
```js
// v5
fastify.decorateRequest('myObject', () => ({ hello: 'world' }));
```
or as a getter
```js
// v5
fastify.decorateRequest('myObject', {
getter () {
return { hello: 'world' }
}
});
```
See [#5462](https://github.com/fastify/fastify/pull/5462) for more information.
### Remove support for DELETE with a `Content-Type: application/json` header and an empty body
In v4, Fastify allowed `DELETE` requests with a `Content-Type: application/json`
header and an empty body was accepted.
This is no longer allowed in v5.
See [#5419](https://github.com/fastify/fastify/pull/5419) for more information.
### Plugins cannot mix callback/promise API anymore
In v4, plugins could mix the callback and promise API, leading to unexpected behavior.
This is no longer allowed in v5.
```js
// v4
fastify.register(async function (instance, opts, done) {
done();
});
```
```js
// v5
fastify.register(async function (instance, opts) {
return;
});
```
or
```js
// v5
fastify.register(function (instance, opts, done) {
done();
});
```
### Requests now have `host`, `hostname`, and `port`, and `hostname` no longer includes the port number
In Fastify v4, `req.hostname` would include both the hostname and the
servers port, so locally it might have the value `localhost:1234`.
With v5, we aligned to the Node.js URL object and now include `host`, `hostname`,
and `port` properties. `req.host` has the same value as `req.hostname` did in v4,
while `req.hostname` includes the hostname _without_ a port if a port is present,
and `req.port` contains just the port number.
See [#4766](https://github.com/fastify/fastify/pull/4766)
and [#4682](https://github.com/fastify/fastify/issues/4682) for more information.
### Removes `getDefaultRoute` and `setDefaultRoute` methods
The `getDefaultRoute` and `setDefaultRoute` methods have been removed in v5.
See [#4485](https://github.com/fastify/fastify/pull/4485)
and [#4480](https://github.com/fastify/fastify/pull/4485)
for more information.
This was already deprecated in v4 as `FSTDEP014`,
so you should have already updated your code.
## New Features
### Diagnostic Channel support
Fastify v5 now supports the [Diagnostics Channel](https://nodejs.org/api/diagnostics_channel.html)
API natively
and provides a way to trace the lifecycle of a request.
```js
'use strict'
const diagnostics = require('node:diagnostics_channel')
const Fastify = require('fastify')
diagnostics.subscribe('tracing:fastify.request.handler:start', (msg) => {
console.log(msg.route.url) // '/:id'
console.log(msg.route.method) // 'GET'
})
diagnostics.subscribe('tracing:fastify.request.handler:end', (msg) => {
// msg is the same as the one emitted by the 'tracing:fastify.request.handler:start' channel
console.log(msg)
})
diagnostics.subscribe('tracing:fastify.request.handler:error', (msg) => {
// in case of error
})
const fastify = Fastify()
fastify.route({
method: 'GET',
url: '/:id',
handler: function (req, reply) {
return { hello: 'world' }
}
})
fastify.listen({ port: 0 }, async function () {
const result = await fetch(fastify.listeningOrigin + '/7')
t.assert.ok(result.ok)
t.assert.strictEqual(response.status, 200)
t.assert.deepStrictEqual(await result.json(), { hello: 'world' })
})
```
See the [documentation](https://github.com/fastify/fastify/blob/main/docs/Reference/Hooks.md#diagnostics-channel-hooks)
and [#5252](https://github.com/fastify/fastify/pull/5252) for additional details.
## Contributors
The complete list of contributors, across all of the core
Fastify packages, is provided below. Please consider
contributing to those that are capable of accepting sponsorships.
| Contributor | Sponsor Link | Packages |
| --- | --- | --- |
| 10xLaCroixDrinker | [❤️ sponsor](https://github.com/sponsors/10xLaCroixDrinker) | fastify-cli |
| Bram-dc | | fastify; fastify-swagger |
| BrianValente | | fastify |
| BryanAbate | | fastify-cli |
| Cadienvan | [❤️ sponsor](https://github.com/sponsors/Cadienvan) | fastify |
| Cangit | | fastify |
| Cyberlane | | fastify-elasticsearch |
| Eomm | [❤️ sponsor](https://github.com/sponsors/Eomm) | ajv-compiler; fastify; fastify-awilix; fastify-diagnostics-channel; fastify-elasticsearch; fastify-hotwire; fastify-mongodb; fastify-nextjs; fastify-swagger-ui; under-pressure |
| EstebanDalelR | [❤️ sponsor](https://github.com/sponsors/EstebanDalelR) | fastify-cli |
| Fdawgs | [❤️ sponsor](https://github.com/sponsors/Fdawgs) | aws-lambda-fastify; csrf-protection; env-schema; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-cli; fastify-cookie; fastify-cors; fastify-diagnostics-channel; fastify-elasticsearch; fastify-env; fastify-error; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-hotwire; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-mongodb; fastify-multipart; fastify-mysql; fastify-nextjs; fastify-oauth2; fastify-passport; fastify-plugin; fastify-postgres; fastify-rate-limit; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-sensible; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; forwarded; middie; point-of-view; process-warning; proxy-addr; safe-regex2; secure-json-parse; under-pressure |
| Gehbt | | fastify-secure-session |
| Gesma94 | | fastify-routes-stats |
| H4ad | [❤️ sponsor](https://github.com/sponsors/H4ad) | aws-lambda-fastify |
| JohanManders | | fastify-secure-session |
| LiviaMedeiros | | fastify |
| Momy93 | | fastify-secure-session |
| MunifTanjim | | fastify-swagger-ui |
| Nanosync | | fastify-secure-session |
| RafaelGSS | [❤️ sponsor](https://github.com/sponsors/RafaelGSS) | fastify; under-pressure |
| Rantoledo | | fastify |
| SMNBLMRR | | fastify |
| SimoneDevkt | | fastify-cli |
| Tony133 | | fastify |
| Uzlopak | [❤️ sponsor](https://github.com/sponsors/Uzlopak) | fastify; fastify-autoload; fastify-diagnostics-channel; fastify-hotwire; fastify-nextjs; fastify-passport; fastify-plugin; fastify-rate-limit; fastify-routes; fastify-static; fastify-swagger-ui; point-of-view; under-pressure |
| Zamiell | | fastify-secure-session |
| aadito123 | | fastify |
| aaroncadillac | [❤️ sponsor](https://github.com/sponsors/aaroncadillac) | fastify |
| aarontravass | | fastify |
| acro5piano | [❤️ sponsor](https://github.com/sponsors/acro5piano) | fastify-secure-session |
| adamward459 | | fastify-cli |
| adrai | [❤️ sponsor](https://github.com/sponsors/adrai) | aws-lambda-fastify |
| alenap93 | | fastify |
| alexandrucancescu | | fastify-nextjs |
| anthonyringoet | | aws-lambda-fastify |
| arshcodemod | | fastify |
| autopulated | | point-of-view |
| barbieri | | fastify |
| beyazit | | fastify |
| big-kahuna-burger | [❤️ sponsor](https://github.com/sponsors/big-kahuna-burger) | fastify-cli; fastify-compress; fastify-helmet |
| bilalshareef | | fastify-routes |
| blue86321 | | fastify-swagger-ui |
| bodinsamuel | | fastify-rate-limit |
| busybox11 | [❤️ sponsor](https://github.com/sponsors/busybox11) | fastify |
| climba03003 | | csrf-protection; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-compress; fastify-cors; fastify-env; fastify-etag; fastify-flash; fastify-formbody; fastify-http-proxy; fastify-mongodb; fastify-swagger-ui; fastify-url-data; fastify-websocket; middie |
| dancastillo | [❤️ sponsor](https://github.com/sponsors/dancastillo) | fastify; fastify-basic-auth; fastify-caching; fastify-circuit-breaker; fastify-cors; fastify-helmet; fastify-passport; fastify-response-validation; fastify-routes; fastify-schedule |
| danny-andrews | | fastify-kafka |
| davidcralph | [❤️ sponsor](https://github.com/sponsors/davidcralph) | csrf-protection |
| davideroffo | | under-pressure |
| dhensby | | fastify-cli |
| dmkng | | fastify |
| domdomegg | | fastify |
| faustman | | fastify-cli |
| floridemai | | fluent-json-schema |
| fox1t | | fastify-autoload |
| giuliowaitforitdavide | | fastify |
| gunters63 | | fastify-reply-from |
| gurgunday | | fastify; fastify-circuit-breaker; fastify-cookie; fastify-multipart; fastify-mysql; fastify-rate-limit; fastify-response-validation; fastify-sensible; fastify-swagger-ui; fluent-json-schema; middie; proxy-addr; safe-regex2; secure-json-parse |
| ildella | | under-pressure |
| james-kaguru | | fastify |
| jcbain | | fastify-http-proxy |
| jdhollander | | fastify-swagger-ui |
| jean-michelet | | fastify; fastify-autoload; fastify-cli; fastify-mysql; fastify-sensible |
| johaven | | fastify-multipart |
| jordanebelanger | | fastify-plugin |
| jscheffner | | fastify |
| jsprw | | fastify-secure-session |
| jsumners | [❤️ sponsor](https://github.com/sponsors/jsumners) | ajv-compiler; avvio; csrf-protection; env-schema; fast-json-stringify; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-autoload; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-compress; fastify-cookie; fastify-cors; fastify-env; fastify-error; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-multipart; fastify-mysql; fastify-oauth2; fastify-plugin; fastify-postgres; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-sensible; fastify-static; fastify-swagger; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; forwarded; light-my-request; middie; process-warning; proxy-addr; safe-regex2; secure-json-parse; under-pressure |
| karankraina | | under-pressure |
| kerolloz | [❤️ sponsor](https://github.com/sponsors/kerolloz) | fastify-jwt |
| kibertoad | | fastify-rate-limit |
| kukidon-dev | | fastify-passport |
| kunal097 | | fastify |
| lamweili | | fastify-sensible |
| lemonclown | | fastify-mongodb |
| liuhanqu | | fastify |
| matthyk | | fastify-plugin |
| mch-dsk | | fastify |
| mcollina | [❤️ sponsor](https://github.com/sponsors/mcollina) | ajv-compiler; avvio; csrf-protection; fastify; fastify-accepts; fastify-accepts-serializer; fastify-auth; fastify-autoload; fastify-awilix; fastify-basic-auth; fastify-bearer-auth; fastify-caching; fastify-circuit-breaker; fastify-cli; fastify-compress; fastify-cookie; fastify-cors; fastify-diagnostics-channel; fastify-elasticsearch; fastify-env; fastify-etag; fastify-express; fastify-flash; fastify-formbody; fastify-funky; fastify-helmet; fastify-http-proxy; fastify-jwt; fastify-kafka; fastify-leveldb; fastify-multipart; fastify-mysql; fastify-oauth2; fastify-passport; fastify-plugin; fastify-postgres; fastify-rate-limit; fastify-redis; fastify-reply-from; fastify-request-context; fastify-response-validation; fastify-routes; fastify-routes-stats; fastify-schedule; fastify-secure-session; fastify-static; fastify-swagger; fastify-swagger-ui; fastify-url-data; fastify-websocket; fastify-zipkin; fluent-json-schema; light-my-request; middie; point-of-view; proxy-addr; secure-json-parse; under-pressure |
| melroy89 | [❤️ sponsor](https://github.com/sponsors/melroy89) | under-pressure |
| metcoder95 | [❤️ sponsor](https://github.com/sponsors/metcoder95) | fastify-elasticsearch |
| mhamann | | fastify-cli |
| mihaur | | fastify-elasticsearch |
| mikesamm | | fastify |
| mikhael-abdallah | | secure-json-parse |
| miquelfire | [❤️ sponsor](https://github.com/sponsors/miquelfire) | fastify-routes |
| miraries | | fastify-swagger-ui |
| mohab-sameh | | fastify |
| monish001 | | fastify |
| moradebianchetti81 | | fastify |
| mouhannad-sh | | aws-lambda-fastify |
| multivoltage | | point-of-view |
| muya | [❤️ sponsor](https://github.com/sponsors/muya) | under-pressure |
| mweberxyz | | point-of-view |
| nflaig | | fastify |
| nickfla1 | | avvio |
| o-az | | process-warning |
| ojeytonwilliams | | csrf-protection |
| onosendi | | fastify-formbody |
| philippviereck | | fastify |
| pip77 | | fastify-mongodb |
| puskin94 | | fastify |
| remidewitte | | fastify |
| rozzilla | | fastify |
| samialdury | | fastify-cli |
| sknetl | | fastify-cors |
| sourcecodeit | | fastify |
| synapse | | env-schema |
| timursaurus | | secure-json-parse |
| tlhunter | | fastify |
| tlund101 | | fastify-rate-limit |
| ttshivers | | fastify-http-proxy |
| voxpelli | [❤️ sponsor](https://github.com/sponsors/voxpelli) | fastify |
| weixinwu | | fastify-cli |
| zetaraku | | fastify-cli |

520
node_modules/fastify/docs/Guides/Plugins-Guide.md generated vendored Normal file
View File

@@ -0,0 +1,520 @@
<h1 align="center">Fastify</h1>
# The hitchhiker's guide to plugins
First of all, `DON'T PANIC`!
Fastify was built from the beginning to be an extremely modular system. We built
a powerful API that allows you to add methods and utilities to Fastify by
creating a namespace. We built a system that creates an encapsulation model,
which allows you to split your application into multiple microservices at any
moment, without the need to refactor the entire application.
**Table of contents**
- [The hitchhiker's guide to plugins](#the-hitchhikers-guide-to-plugins)
- [Register](#register)
- [Decorators](#decorators)
- [Hooks](#hooks)
- [How to handle encapsulation and
distribution](#how-to-handle-encapsulation-and-distribution)
- [ESM support](#esm-support)
- [Handle errors](#handle-errors)
- [Custom errors](#custom-errors)
- [Emit Warnings](#emit-warnings)
- [Let's start!](#lets-start)
## Register
<a id="register"></a>
As with JavaScript, where everything is an object, in Fastify everything is a
plugin.
Your routes, your utilities, and so on are all plugins. To add a new plugin,
whatever its functionality may be, in Fastify you have a nice and unique API:
[`register`](../Reference/Plugins.md).
```js
fastify.register(
require('./my-plugin'),
{ options }
)
```
`register` creates a new Fastify context, which means that if you perform any
changes on the Fastify instance, those changes will not be reflected in the
context's ancestors. In other words, encapsulation!
*Why is encapsulation important?*
Well, let's say you are creating a new disruptive startup, what do you do? You
create an API server with all your stuff, everything in the same place, a
monolith!
Ok, you are growing very fast and you want to change your architecture and try
microservices. Usually, this implies a huge amount of work, because of cross
dependencies and a lack of separation of concerns in the codebase.
Fastify helps you in that regard. Thanks to the encapsulation model, it will
completely avoid cross dependencies and will help you structure your code into
cohesive blocks.
*Let's return to how to correctly use `register`.*
As you probably know, the required plugins must expose a single function with
the following signature
```js
module.exports = function (fastify, options, done) {}
```
Where `fastify` is the encapsulated Fastify instance, `options` is the options
object, and `done` is the function you **must** call when your plugin is ready.
Fastify's plugin model is fully reentrant and graph-based, it handles
asynchronous code without any problems and it enforces both the load and close
order of plugins. *How?* Glad you asked, check out
[`avvio`](https://github.com/mcollina/avvio)! Fastify starts loading the plugin
__after__ `.listen()`, `.inject()` or `.ready()` are called.
Inside a plugin you can do whatever you want, register routes and utilities (we
will see this in a moment), and do nested registers, just remember to call `done`
when everything is set up!
```js
module.exports = function (fastify, options, done) {
fastify.get('/plugin', (request, reply) => {
reply.send({ hello: 'world' })
})
done()
}
```
Well, now you know how to use the `register` API and how it works, but how do we
add new functionality to Fastify and even better, share them with other
developers?
## Decorators
<a id="decorators"></a>
Okay, let's say that you wrote a utility that is so good that you decided to
make it available along with all your code. How would you do it? Probably
something like the following:
```js
// your-awesome-utility.js
module.exports = function (a, b) {
return a + b
}
```
```js
const util = require('./your-awesome-utility')
console.log(util('that is ', 'awesome'))
```
Now you will import your utility in every file you need it in. (And do not
forget that you will probably also need it in your tests).
Fastify offers you a more elegant and comfortable way to do this, *decorators*.
Creating a decorator is extremely easy, just use the
[`decorate`](../Reference/Decorators.md) API:
```js
fastify.decorate('util', (a, b) => a + b)
```
Now you can access your utility just by calling `fastify.util` whenever you need
it - even inside your test.
And here starts the magic; do you remember how just now we were talking about
encapsulation? Well, using `register` and `decorate` in conjunction enables
exactly that, let me show you an example to clarify this:
```js
fastify.register((instance, opts, done) => {
instance.decorate('util', (a, b) => a + b)
console.log(instance.util('that is ', 'awesome'))
done()
})
fastify.register((instance, opts, done) => {
console.log(instance.util('that is ', 'awesome')) // This will throw an error
done()
})
```
Inside the second register call `instance.util` will throw an error because
`util` exists only inside the first register context.
Let's step back for a moment and dig deeper into this: every time you use the
`register` API, a new context is created that avoids the negative situations
mentioned above.
Do note that encapsulation applies to the ancestors and siblings, but not the
children.
```js
fastify.register((instance, opts, done) => {
instance.decorate('util', (a, b) => a + b)
console.log(instance.util('that is ', 'awesome'))
fastify.register((instance, opts, done) => {
console.log(instance.util('that is ', 'awesome')) // This will not throw an error
done()
})
done()
})
fastify.register((instance, opts, done) => {
console.log(instance.util('that is ', 'awesome')) // This will throw an error
done()
})
```
*Take home message: if you need a utility that is available in every part of
your application, take care that it is declared in the root scope of your
application. If that is not an option, you can use the `fastify-plugin` utility
as described [here](#distribution).*
`decorate` is not the only API that you can use to extend the server
functionality, you can also use `decorateRequest` and `decorateReply`.
*`decorateRequest` and `decorateReply`? Why do we need them if we already have
`decorate`?*
Good question, we added them to make Fastify more developer-friendly. Let's see
an example:
```js
fastify.decorate('html', payload => {
return generateHtml(payload)
})
fastify.get('/html', (request, reply) => {
reply
.type('text/html')
.send(fastify.html({ hello: 'world' }))
})
```
It works, but it could be much better!
```js
fastify.decorateReply('html', function (payload) {
this.type('text/html') // This is the 'Reply' object
this.send(generateHtml(payload))
})
fastify.get('/html', (request, reply) => {
reply.html({ hello: 'world' })
})
```
Reminder that the `this` keyword is not available on *arrow functions*,
so when passing functions in *`decorateReply`* and *`decorateRequest`* as
a utility that also needs access to the `request` and `reply` instance,
a function that is defined using the `function` keyword is needed instead
of an *arrow function expression*.
You can do the same for the `request` object:
```js
fastify.decorate('getHeader', (req, header) => {
return req.headers[header]
})
fastify.addHook('preHandler', (request, reply, done) => {
request.isHappy = fastify.getHeader(request.raw, 'happy')
done()
})
fastify.get('/happiness', (request, reply) => {
reply.send({ happy: request.isHappy })
})
```
Again, it works, but it can be much better!
```js
fastify.decorateRequest('setHeader', function (header) {
this.isHappy = this.headers[header]
})
fastify.decorateRequest('isHappy', false) // This will be added to the Request object prototype, yay speed!
fastify.addHook('preHandler', (request, reply, done) => {
request.setHeader('happy')
done()
})
fastify.get('/happiness', (request, reply) => {
reply.send({ happy: request.isHappy })
})
```
We have seen how to extend server functionality and how to handle the
encapsulation system, but what if you need to add a function that must be
executed whenever the server "[emits](../Reference/Lifecycle.md)" an
event?
## Hooks
<a id="hooks"></a>
You just built an amazing utility, but now you need to execute that for every
request, this is what you will likely do:
```js
fastify.decorate('util', (request, key, value) => { request[key] = value })
fastify.get('/plugin1', (request, reply) => {
fastify.util(request, 'timestamp', new Date())
reply.send(request)
})
fastify.get('/plugin2', (request, reply) => {
fastify.util(request, 'timestamp', new Date())
reply.send(request)
})
```
I think we all agree that this is terrible. Repeated code, awful readability and
it cannot scale.
So what can you do to avoid this annoying issue? Yes, you are right, use a
[hook](../Reference/Hooks.md)!
```js
fastify.decorate('util', (request, key, value) => { request[key] = value })
fastify.addHook('preHandler', (request, reply, done) => {
fastify.util(request, 'timestamp', new Date())
done()
})
fastify.get('/plugin1', (request, reply) => {
reply.send(request)
})
fastify.get('/plugin2', (request, reply) => {
reply.send(request)
})
```
Now for every request, you will run your utility. You can register as many hooks
as you need.
Sometimes you want a hook that should be executed for just a subset of routes,
how can you do that? Yep, encapsulation!
```js
fastify.register((instance, opts, done) => {
instance.decorate('util', (request, key, value) => { request[key] = value })
instance.addHook('preHandler', (request, reply, done) => {
instance.util(request, 'timestamp', new Date())
done()
})
instance.get('/plugin1', (request, reply) => {
reply.send(request)
})
done()
})
fastify.get('/plugin2', (request, reply) => {
reply.send(request)
})
```
Now your hook will run just for the first route!
An alternative approach is to make use of the [onRoute hook](../Reference/Hooks.md#onroute)
to customize application routes dynamically from inside the plugin. Every time
a new route is registered, you can read and modify the route options. For example,
based on a [route config option](../Reference/Routes.md#routes-options):
```js
fastify.register((instance, opts, done) => {
instance.decorate('util', (request, key, value) => { request[key] = value })
function handler(request, reply, done) {
instance.util(request, 'timestamp', new Date())
done()
}
instance.addHook('onRoute', (routeOptions) => {
if (routeOptions.config && routeOptions.config.useUtil === true) {
// set or add our handler to the route preHandler hook
if (!routeOptions.preHandler) {
routeOptions.preHandler = [handler]
return
}
if (Array.isArray(routeOptions.preHandler)) {
routeOptions.preHandler.push(handler)
return
}
routeOptions.preHandler = [routeOptions.preHandler, handler]
}
})
fastify.get('/plugin1', {config: {useUtil: true}}, (request, reply) => {
reply.send(request)
})
fastify.get('/plugin2', (request, reply) => {
reply.send(request)
})
done()
})
```
This variant becomes extremely useful if you plan to distribute your plugin, as
described in the next section.
As you probably noticed by now, `request` and `reply` are not the standard
Node.js *request* and *response* objects, but Fastify's objects.
## How to handle encapsulation and distribution
<a id="distribution"></a>
Perfect, now you know (almost) all of the tools that you can use to extend
Fastify. Nevertheless, chances are that you came across one big issue: how is
distribution handled?
The preferred way to distribute a utility is to wrap all your code inside a
`register`. Using this, your plugin can support asynchronous bootstrapping
*(since `decorate` is a synchronous API)*, in the case of a database connection
for example.
*Wait, what? Didn't you tell me that `register` creates an encapsulation and
that the stuff I create inside will not be available outside?*
Yes, I said that. However, what I didn't tell you is that you can tell Fastify
to avoid this behavior with the
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) module.
```js
const fp = require('fastify-plugin')
const dbClient = require('db-client')
function dbPlugin (fastify, opts, done) {
dbClient.connect(opts.url, (err, conn) => {
fastify.decorate('db', conn)
done()
})
}
module.exports = fp(dbPlugin)
```
You can also tell `fastify-plugin` to check the installed version of Fastify, in
case you need a specific API.
As we mentioned earlier, Fastify starts loading its plugins __after__
`.listen()`, `.inject()` or `.ready()` are called and as such, __after__ they
have been declared. This means that, even though the plugin may inject variables
to the external Fastify instance via [`decorate`](../Reference/Decorators.md),
the decorated variables will not be accessible before calling `.listen()`,
`.inject()`, or `.ready()`.
In case you rely on a variable injected by a preceding plugin and want to pass
that in the `options` argument of `register`, you can do so by using a function
instead of an object:
```js
const fastify = require('fastify')()
const fp = require('fastify-plugin')
const dbClient = require('db-client')
function dbPlugin (fastify, opts, done) {
dbClient.connect(opts.url, (err, conn) => {
fastify.decorate('db', conn)
done()
})
}
fastify.register(fp(dbPlugin), { url: 'https://example.com' })
fastify.register(require('your-plugin'), parent => {
return { connection: parent.db, otherOption: 'foo-bar' }
})
```
In the above example, the `parent` variable of the function passed in as the
second argument of `register` is a copy of the **external Fastify instance**
that the plugin was registered at. This means that we can access any
variables that were injected by preceding plugins in the order of declaration.
## ESM support
<a id="esm-support"></a>
ESM is supported as well from [Node.js
`v13.3.0`](https://nodejs.org/api/esm.html) and above! Just export your plugin
as an ESM module and you are good to go!
```js
// plugin.mjs
async function plugin (fastify, opts) {
fastify.get('/', async (req, reply) => {
return { hello: 'world' }
})
}
export default plugin
```
## Handle errors
<a id="handle-errors"></a>
One of your plugins may fail during startup. Maybe you expect it
and you have a custom logic that will be triggered in that case. How can you
implement this? The `after` API is what you need. `after` simply registers a
callback that will be executed just after a register, and it can take up to
three parameters.
The callback changes based on the parameters you are giving:
1. If no parameter is given to the callback and there is an error, that error
will be passed to the next error handler.
1. If one parameter is given to the callback, that parameter will be the error
object.
1. If two parameters are given to the callback, the first will be the error
object; the second will be the done callback.
1. If three parameters are given to the callback, the first will be the error
object, the second will be the top-level context unless you have specified
both server and override, in that case, the context will be what the override
returns, and the third the done callback.
Let's see how to use it:
```js
fastify
.register(require('./database-connector'))
.after(err => {
if (err) throw err
})
```
## Custom errors
<a id="custom-errors"></a>
If your plugin needs to expose custom errors, you can easily generate consistent
error objects across your codebase and plugins with the
[`@fastify/error`](https://github.com/fastify/fastify-error) module.
```js
const createError = require('@fastify/error')
const CustomError = createError('ERROR_CODE', 'message')
console.log(new CustomError())
```
## Emit Warnings
<a id="emit-warnings"></a>
If you want to deprecate an API, or you want to warn the user about a specific
use case, you can use the
[`process-warning`](https://github.com/fastify/process-warning) module.
```js
const warning = require('process-warning')()
warning.create('MyPluginWarning', 'MP_ERROR_CODE', 'message')
warning.emit('MP_ERROR_CODE')
```
## Let's start!
<a id="start"></a>
Awesome, now you know everything you need to know about Fastify and its plugin
system to start building your first plugin, and please if you do, tell us! We
will add it to the [*ecosystem*](https://github.com/fastify/fastify#ecosystem)
section of our documentation!
If you want to see some real-world examples, check out:
- [`@fastify/view`](https://github.com/fastify/point-of-view) Templates
rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify.
- [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify
MongoDB connection plugin, with this you can share the same MongoDB connection
pool in every part of your server.
- [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart
support for Fastify
- [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important
security headers for Fastify
*Do you feel like something is missing here? Let us know! :)*

383
node_modules/fastify/docs/Guides/Prototype-Poisoning.md generated vendored Normal file
View File

@@ -0,0 +1,383 @@
> The following is an article written by Eran Hammer.
> It is reproduced here for posterity [with permission](https://github.com/fastify/fastify/issues/1426#issuecomment-817957913).
> It has been reformatted from the original HTML source to Markdown source,
> but otherwise remains the same. The original HTML can be retrieved from the
> above permission link.
## History behind prototype poisoning
<a id="pp"></a>
Based on the article by Eran Hammer,the issue is created by a web security bug.
It is also a perfect illustration of the efforts required to maintain
open-source software and the limitations of existing communication channels.
But first, if we use a JavaScript framework to process incoming JSON data, take
a moment to read up on [Prototype Poisoning](https://medium.com/intrinsic/javascript-prototype-poisoning-vulnerabilities-in-the-wild-7bc15347c96)
in general, and the specific
[technical details](https://github.com/hapijs/hapi/issues/3916) of this issue.
This could be a critical issue so, we might need to verify your own code first.
It focuses on specific framework however, any solution that uses `JSON.parse()`
to process external data is potentially at risk.
### BOOM
<a id="pp-boom"></a>
The engineering team at Lob (long time generous supporters of my work!) reported
a critical security vulnerability they identified in our data validation
module[joi](https://github.com/hapijs/joi). They provided some technical
details and a proposed solution.
The main purpose of a data validation library is to ensure the output fully
complies with the rules defined. If it doesn't, validation fails. If it passes,
we can blindly trust that the data you are working with is safe. In fact, most
developers treat validated input as completely safe from a system integrity
perspective which is crucial!
In our case, the Lob team provided an example where some data was able to escape
by the validation logic and pass through undetected. This is the worst possible
defect a validation library can have.
### Prototype in a nutshell
<a id="pp-nutshell"></a>
To understand this, we need to understand how JavaScript works a bit.
Every object in JavaScript can have a prototype. It is a set of methods and
properties it "inherits" from another object. I have put inherits in quotes
because JavaScript isn't really an object-oriented language. It is a prototype-
based object-oriented language.
A long time ago, for a bunch of irrelevant reasons, someone decided that it
would be a good idea to use the special property name `__proto__` to access (and
set) an object's prototype. This has since been deprecated but nevertheless,
fully supported.
To demonstrate:
```
> const a = { b: 5 };
> a.b;
5
> a.__proto__ = { c: 6 };
> a.c;
6
> a;
{ b: 5 }
```
The object doesn't have a `c` property, but its prototype does.
When validating the object, the validation library ignores the prototype and
only validates the object's own properties. This allows `c` to sneak in via the
prototype.
Another important part is the way `JSON.parse()` — a utility
provided by the language to convert JSON formatted text into
objects —  handles this magic `__proto__` property name.
```
> const text = '{"b": 5, "__proto__": { "c": 6 }}';
> const a = JSON.parse(text);
> a;
{b: 5, __proto__: { c: 6 }}
```
Notice how `a` has a `__proto__` property. This is not a prototype reference. It
is a simple object property key, just like `b`. As we've seen from the first
example, we can't actually create this key through assignment as that invokes
the prototype magic and sets an actual prototype. `JSON.parse()` however, sets a
simple property with that poisonous name.
By itself, the object created by `JSON.parse()` is perfectly safe. It doesn't
have a prototype of its own. It has a seemingly harmless property that just
happens to overlap with a built-in JavaScript magic name.
However, other methods are not as lucky:
```
> const x = Object.assign({}, a);
> x;
{ b: 5}
> x.c;
6;
```
If we take the `a` object created earlier by `JSON.parse()` and pass it to the
helpful `Object.assign()` method (used to perform a shallow copy of all the top
level properties of `a` into the provided empty `{}` object), the magic
`__proto__` property "leaks" and becomes `x` 's actual prototype.
Surprise!
If you get some external text input and parse it with `JSON.parse()`
then perform some simple manipulation of that object (e.g shallow clone and add
an `id` ), and pass it to our validation library, it would sneak in undetected
via `__proto__`.
### Oh joi!
<a id="pp-oh-joi"></a>
The first question is, of course, why does the validation module **joi** ignore
the prototype and let potentially harmful data through? We asked ourselves the
same question and our instant thought was "it was an oversight". A bug - a really
big mistake. The joi module should not have allowed this to happen. But…
While joi is used primarily for validating web input data, it also has a
significant user base using it to validate internal objects, some of which have
prototypes. The fact that joi ignores the prototype is a helpful "feature". It
allows validating the object's own properties while ignoring what could be a
very complicated prototype structure (with many methods and literal properties).
Any solution at the joi level would mean breaking some currently working code.
### The right thing
<a id="pp-right-thing"></a>
At this point, we were looking at a devastatingly bad security vulnerability.
Right up there in the upper echelons of epic security failures. All we knew is
that our extremely popular data validation library fails to block harmful data,
and that this data is trivial to sneak through. All you need to do is add
`__proto__` and some crap to a JSON input and send it on its way to an
application built using our tools.
(Dramatic pause)
We knew we had to fix joi to prevent this but given the scale of this issue, we
had to do it in a way that will put a fix out without drawing too much attention
to itwithout making it too easy to exploitat least for a few days until
most systems received the update.
Sneaking a fix isn't the hardest thing to accomplish. If you combine it with an
otherwise purposeless refactor of the code, and throw in a few unrelated bug
fixes and maybe a cool new feature, you can publish a new version without
drawing attention to the real issue being fixed.
The problem was, the right fix was going to break valid use cases. You see, joi
has no way of knowing if you want it to ignore the prototype you set, or block
the prototype set by an attacker. A solution that fixes the exploit will break
code and breaking code tends to get a lot of attention.
On the other hand, if we released a proper ([semantically
versioned](https://semver.org/)) fix, mark it as a breaking change, and add a
new API to explicitly tell joi what you want it to do with the prototype, we
will share with the world how to exploit this vulnerability while also making it
more time consuming for systems to upgrade (breaking changes never get applied
automatically by build tools).
### A detour
<a id="pp-detour"></a>
While the issue at hand was about incoming request payloads, we had to pause and
check if it could also impact data coming via the query string, cookies, and
headers. Basically, anything that gets serialized into objects from text.
We quickly confirmed node default query string parser was fine as well as its
header parser. I identified one potential issue with base64-encoded JSON cookies
as well as the usage of custom query string parsers. We also wrote some tests to
confirm that the most popular third-party query string parser
[qs](https://www.npmjs.com/package/qs) — was not vulnerable (it is not!).
### A development
<a id="pp-a-development"></a>
Throughout this triage, we just assumed that the offending input with its
poisoned prototype was coming into joi from hapi, the web framework connecting
the hapi.js ecosystem. Further investigation by the Lob team found that the
problem was a bit more nuanced.
hapi used `JSON.parse()` to process incoming data. It first set the result
object as a `payload` property of the incoming request, and then passed that
same object for validation by joi before being passed to the application
business logic for processing. Since `JSON.parse()` doesn't actually leak the
`__proto__` property, it would arrive to joi with an invalid key and fail
validation.
However, hapi provides two extension points where the payload data can be
inspected (and processed) prior to validation. It is all properly documented and
well understood by most developers. The extension points are there to allow you
to interact with the raw inputs prior to validation for legitimate (and often
security related) reasons.
If during one of these two extension points, a developer used `Object.assign()`
or a similar method on the payload, the `__proto__` property would leak and
become an actual prototype.
### Sigh of relief
<a id="pp-sigh-of-relief"></a>
We were now dealing with a much different level of awfulness. Manipulating the
payload object prior to validation is not common which meant this was no longer
a doomsday scenario. It was still potentially catastrophic but the exposure
dropped from every joi user to some very specific implementations.
We were no longer looking at a secretive joi release. The issue in joi is still
there, but we can now address it properly with a new API and breaking release
over the next few weeks.
We also knew that we can easily mitigate this vulnerability at the framework
level since it knows which data is coming from the outside and which is
internally generated. The framework is really the only piece that can protect
developers against making such unexpected mistakes.
### Good news, bad news, no news?
<a id="pp-good-news-no-news"></a>
The good news was that this wasn't our fault. It wasn't a bug in hapi or joi. It
was only possible through a complex combination of actions that was not unique
to hapi or joi. This can happen with every other JavaScript framework. If hapi
is broken, then the world is broken.
Greatwe solved the blame game.
The bad news is that when there is nothing to blame (other than JavaScript
itself), it is much harder getting it fixed.
The first question people ask once a security issue is found is if there is
going to be a CVE published. A CVECommon Vulnerabilities and Exposuresis a
[database](https://cve.mitre.org/) of known security issues. It is a critical
component of web security. The benefit of publishing a CVE is that it
immediately triggers alarms and informs and often breaks automated builds until
the issue is resolved.
But what do we pin this to?
Probably, nothing. We are still debating whether we should tag some versions of
hapi with a warning. The "we" is the node security process. Since we now have a
new version of hapi that mitigate the problem by default, it can be considered a
fix. But because the fix isn't to a problem in hapi itself, it is not exactly
kosher to declare older versions harmful.
Publishing an advisory on previous versions of hapi for the sole purpose of
nudging people into awareness and upgrade is an abuse of the advisory process.
I'm personally fine with abusing it for the purpose of improving security but
that's not my call. As of this writing, it is still being debated.
### The solution business
<a id="pp-solution-business"></a>
Mitigating the issue wasn't hard. Making it scale and safe was a bit more
involved. Since we knew where harmful data can enter the system, and we knew
where we used the problematic `JSON.parse()` we could replace it with a safe
implementation.
One problem. Validating data can be costly and we are now planning on validating
every incoming JSON text. The built-in `JSON.parse()` implementation is fast.
Really really fast. It is unlikely we can build a replacement that will be more
secure and anywhere as fast. Especially not overnight and without introducing
new bugs.
It was obvious we were going to wrap the existing `JSON.parse()` method with
some additional logic. We just had to make sure it was not adding too much
overhead. This isn't just a performance consideration but also a security one.
If we make it easy to slow down a system by simply sending specific data, we
make it easy to execute a [DoS
attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) at very low
cost.
I came up with a stupidly simple solution: first parse the text using the
existing tools. If this didn't fail, scan the original raw text for the
offending string "__proto__". Only if we find it, perform an actual scan of the
object. We can't block every reference to "__proto__"sometimes it is
perfectly valid value (like when writing about it here and sending this text
over to Medium for publication).
This made the "happy path" practically as fast as before. It just added one
function call, a quick text scan (again, very fast built-in implementation), and
a conditional return. The solution had negligible impact on the vast majority of
data expected to pass through it.
Next problem. The prototype property doesn't have to be at the top level of the
incoming object. It can be nested deep inside. This means we cannot just check
for the presence of it at the top level. We need to recursively iterate through
the object.
While recursive functions are a favorite tool, they could be disastrous when
writing security-conscious code. You see, recursive function increase the size
of the runtime call stack. The more times you loop, the longer the call stack
gets. At some pointKABOOM— you reach the maximum length and the process dies.
If you cannot guarantee the shape of the incoming data, recursive iteration
becomes an open threat. An attacker only needs to craft a deep enough object to
crash your servers.
I used a flat loop implementation that is both more memory efficient (less
function calls, less passing of temporary arguments) and more secure. I am not
pointing this out to brag, but to highlight how basic engineering practices can
create (or avoid) security pitfalls.
### Putting it to the test
<a id="pp-putting-to-test"></a>
I sent the code to two people. First to [Nathan
LaFreniere](https://github.com/nlf) to double check the security properties of
the solution, and then to [Matteo Collina](https://github.com/mcollina) to
review the performance. They are among the very best at what they do and often
my go-to people.
The performance benchmarks confirmed that the "happy path" was practically
unaffected. The interesting findings was that removing the offending values was
faster then throwing an exception. This raised the question of what should be
the default behavior of the new modulewhich I called
[**bourne**](https://github.com/hapijs/bourne) — error or sanitize.
The concern, again, was exposing the application to a DoS attack. If sending a
request with `__proto__` makes things 500% slower, that could be an easy vector
to exploit. But after a bit more testing we confirmed that sending **any**
invalid JSON text was creating a very similar cost.
In other words, if you parse JSON, invalid values are going to cost you more,
regardless of what makes them invalid. It is also important to remember that
while the benchmark showed the significant % cost of scanning suspected objects,
the actual cost in CPU time was still in the fraction of milliseconds. Important
to note and measure but not actually harmful.
### hapi ever-after
<a id="pp-hapi-ever-after"></a>
There are a bunch of things to be grateful for.
The initial disclosure by the Lob team was perfect. It was reported privately,
to the right people, with the right information. They followed up with
additional findings, and gave us the time and space to resolve it the right way.
Lob also was a major sponsor of my work on hapi over the years and that
financial support is critical to allow everything else to happen. More on that
in a bit.
Triage was stressful but staffed with the right people. Having folks like
[Nicolas Morel](https://github.com/Marsup), Nathan, and Matteo, available and
eager to help is critical. This isn't easy to deal with without the pressure,
but with it, mistakes are likely without proper team collaboration.
We got lucky with the actual vulnerability. What started up looking like a
catastrophic problem, ended up being a delicate but straight-forward problem to
address.
We also got lucky by having full access to mitigate it at the sourcedidn't
need to send emails to some unknown framework maintainer and hope for a quick
answer. hapi's total control over all of its dependencies proved its usefulness
and security again. Not using [hapi](https://hapi.dev)? [Maybe you
should](https://hueniverse.com/why-you-should-consider-hapi-6163689bd7c2).
### The after in happy ever-after
<a id="pp-after-ever-after"></a>
This is where I have to take advantage of this incident to reiterate the cost
and need for sustainable and secure open source.
My time alone on this one issue exceeded 20 hours. That's half a working week.
It came at the end of a month were I already spent over 30 hours publishing a
new major release of hapi (most of the work was done in December). This puts me
at a personal financial loss of over $5000 this month (I had to cut back on paid
client work to make time for it).
If you rely on code I maintain, this is exactly the level of support, quality,
and commitment you want (and lets be honestexpect). Most of you take it for
grantednot just my work but the work of hundreds of other dedicated open
source maintainers.
Because this work is important, I decided to try and make it not just
financially sustainable but to grow and expand it. There is so much to improve.
This is exactly what motivates me to implement the new [commercial licensing
plan](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898)
coming in March. You can read more about it
[here](https://web.archive.org/web/20190201220503/https://hueniverse.com/on-hapi-licensing-a-preview-f982662ee898).

353
node_modules/fastify/docs/Guides/Recommendations.md generated vendored Normal file
View File

@@ -0,0 +1,353 @@
<h1 align="center">Fastify</h1>
## Recommendations
This document contains a set of recommendations when using Fastify.
- [Use A Reverse Proxy](#use-a-reverse-proxy)
- [HAProxy](#haproxy)
- [Nginx](#nginx)
- [Kubernetes](#kubernetes)
- [Capacity Planning For Production](#capacity)
- [Running Multiple Instances](#multiple)
## Use A Reverse Proxy
<a id="reverseproxy"></a>
Node.js is an early adopter of frameworks shipping with an easy-to-use web
server within the standard library. Previously, with languages like PHP or
Python, one would need either a web server with specific support for the
language or the ability to set up some sort of [CGI gateway][cgi] that works
with the language. With Node.js, one can write an application that _directly_
handles HTTP requests. As a result, the temptation is to write applications that
handle requests for multiple domains, listen on multiple ports (i.e. HTTP _and_
HTTPS), and then expose these applications directly to the Internet to handle
requests.
The Fastify team **strongly** considers this to be an anti-pattern and extremely
bad practice:
1. It adds unnecessary complexity to the application by diluting its focus.
2. It prevents [horizontal scalability][scale-horiz].
See [Why should I use a Reverse Proxy if Node.js is Production Ready?][why-use]
for a more thorough discussion of why one should opt to use a reverse proxy.
For a concrete example, consider the situation where:
1. The app needs multiple instances to handle load.
1. The app needs TLS termination.
1. The app needs to redirect HTTP requests to HTTPS.
1. The app needs to serve multiple domains.
1. The app needs to serve static resources, e.g. jpeg files.
There are many reverse proxy solutions available, and your environment may
dictate the solution to use, e.g. AWS or GCP. Given the above, we could use
[HAProxy][haproxy] or [Nginx][nginx] to solve these requirements:
### HAProxy
```conf
# The global section defines base HAProxy (engine) instance configuration.
global
log /dev/log syslog
maxconn 4096
chroot /var/lib/haproxy
user haproxy
group haproxy
# Set some baseline TLS options.
tune.ssl.default-dh-param 2048
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11
ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
# Each defaults section defines options that will apply to each subsequent
# subsection until another defaults section is encountered.
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
# The following option makes haproxy close connections to backend servers
# instead of keeping them open. This can alleviate unexpected connection
# reset errors in the Node process.
option http-server-close
maxconn 2000
timeout connect 5000
timeout client 50000
timeout server 50000
# Enable content compression for specific content types.
compression algo gzip
compression type text/html text/plain text/css application/javascript
# A "frontend" section defines a public listener, i.e. an "http server"
# as far as clients are concerned.
frontend proxy
# The IP address here would be the _public_ IP address of the server.
# Here, we use a private address as an example.
bind 10.0.0.10:80
# This redirect rule will redirect all traffic that is not TLS traffic
# to the same incoming request URL on the HTTPS port.
redirect scheme https code 308 if !{ ssl_fc }
# Technically this use_backend directive is useless since we are simply
# redirecting all traffic to this frontend to the HTTPS frontend. It is
# merely included here for completeness sake.
use_backend default-server
# This frontend defines our primary, TLS only, listener. It is here where
# we will define the TLS certificates to expose and how to direct incoming
# requests.
frontend proxy-ssl
# The `/etc/haproxy/certs` directory in this example contains a set of
# certificate PEM files that are named for the domains the certificates are
# issued for. When HAProxy starts, it will read this directory, load all of
# the certificates it finds here, and use SNI matching to apply the correct
# certificate to the connection.
bind 10.0.0.10:443 ssl crt /etc/haproxy/certs
# Here we define rule pairs to handle static resources. Any incoming request
# that has a path starting with `/static`, e.g.
# `https://one.example.com/static/foo.jpeg`, will be redirected to the
# static resources server.
acl is_static path -i -m beg /static
use_backend static-backend if is_static
# Here we define rule pairs to direct requests to appropriate Node.js
# servers based on the requested domain. The `acl` line is used to match
# the incoming hostname and define a boolean indicating if it is a match.
# The `use_backend` line is used to direct the traffic if the boolean is
# true.
acl example1 hdr_sub(Host) one.example.com
use_backend example1-backend if example1
acl example2 hdr_sub(Host) two.example.com
use_backend example2-backend if example2
# Finally, we have a fallback redirect if none of the requested hosts
# match the above rules.
default_backend default-server
# A "backend" is used to tell HAProxy where to request information for the
# proxied request. These sections are where we will define where our Node.js
# apps live and any other servers for things like static assets.
backend default-server
# In this example we are defaulting unmatched domain requests to a single
# backend server for all requests. Notice that the backend server does not
# have to be serving TLS requests. This is called "TLS termination": the TLS
# connection is "terminated" at the reverse proxy.
# It is possible to also proxy to backend servers that are themselves serving
# requests over TLS, but that is outside the scope of this example.
server server1 10.10.10.2:80
# This backend configuration will serve requests for `https://one.example.com`
# by proxying requests to three backend servers in a round-robin manner.
backend example1-backend
server example1-1 10.10.11.2:80
server example1-2 10.10.11.2:80
server example2-2 10.10.11.3:80
# This one serves requests for `https://two.example.com`
backend example2-backend
server example2-1 10.10.12.2:80
server example2-2 10.10.12.2:80
server example2-3 10.10.12.3:80
# This backend handles the static resources requests.
backend static-backend
server static-server1 10.10.9.2:80
```
[cgi]: https://en.wikipedia.org/wiki/Common_Gateway_Interface
[scale-horiz]: https://en.wikipedia.org/wiki/Scalability#Horizontal
[why-use]: https://web.archive.org/web/20190821102906/https://medium.com/intrinsic/why-should-i-use-a-reverse-proxy-if-node-js-is-production-ready-5a079408b2ca
[haproxy]: https://www.haproxy.org/
### Nginx
```nginx
# This upstream block groups 3 servers into one named backend fastify_app
# with 2 primary servers distributed via round-robin
# and one backup which is used when the first 2 are not reachable
# This also assumes your fastify servers are listening on port 80.
# more info: https://nginx.org/en/docs/http/ngx_http_upstream_module.html
upstream fastify_app {
server 10.10.11.1:80;
server 10.10.11.2:80;
server 10.10.11.3:80 backup;
}
# This server block asks NGINX to respond with a redirect when
# an incoming request from port 80 (typically plain HTTP), to
# the same request URL but with HTTPS as protocol.
# This block is optional, and usually used if you are handling
# SSL termination in NGINX, like in the example here.
server {
# default server is a special parameter to ask NGINX
# to set this server block to the default for this address/port
# which in this case is any address and port 80
listen 80 default_server;
listen [::]:80 default_server;
# With a server_name directive you can also ask NGINX to
# use this server block only with matching server name(s)
# listen 80;
# listen [::]:80;
# server_name example.tld;
# This matches all paths from the request and responds with
# the redirect mentioned above.
location / {
return 301 https://$host$request_uri;
}
}
# This server block asks NGINX to respond to requests from
# port 443 with SSL enabled and accept HTTP/2 connections.
# This is where the request is then proxied to the fastify_app
# server group via port 3000.
server {
# This listen directive asks NGINX to accept requests
# coming to any address, port 443, with SSL.
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
# With a server_name directive you can also ask NGINX to
# use this server block only with matching server name(s)
# listen 443 ssl;
# listen [::]:443 ssl;
# server_name example.tld;
# Enable HTTP/2 support
http2 on;
# Your SSL/TLS certificate (chain) and secret key in the PEM format
ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/private.pem;
# A generic best practice baseline for based
# on https://ssl-config.mozilla.org/
ssl_session_timeout 1d;
ssl_session_cache shared:FastifyApp:10m;
ssl_session_tickets off;
# This tells NGINX to only accept TLS 1.3, which should be fine
# with most modern browsers including IE 11 with certain updates.
# If you want to support older browsers you might need to add
# additional fallback protocols.
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
# This adds a header that tells browsers to only ever use HTTPS
# with this server.
add_header Strict-Transport-Security "max-age=63072000" always;
# The following directives are only necessary if you want to
# enable OCSP Stapling.
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /path/to/chain.pem;
# Custom nameserver to resolve upstream server names
# resolver 127.0.0.1;
# This section matches all paths and proxies it to the backend server
# group specified above. Note the additional headers that forward
# information about the original request. You might want to set
# trustProxy to the address of your NGINX server so the X-Forwarded
# fields are used by fastify.
location / {
# more info: https://nginx.org/en/docs/http/ngx_http_proxy_module.html
proxy_http_version 1.1;
proxy_cache_bypass $http_upgrade;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# This is the directive that proxies requests to the specified server.
# If you are using an upstream group, then you do not need to specify a port.
# If you are directly proxying to a server e.g.
# proxy_pass http://127.0.0.1:3000 then specify a port.
proxy_pass http://fastify_app;
}
}
```
[nginx]: https://nginx.org/
## Kubernetes
<a id="kubernetes"></a>
The `readinessProbe` uses ([by
default](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes))
the pod IP as the hostname. Fastify listens on `127.0.0.1` by default. The probe
will not be able to reach the application in this case. To make it work,
the application must listen on `0.0.0.0` or specify a custom hostname in
the `readinessProbe.httpGet` spec, as per the following example:
```yaml
readinessProbe:
httpGet:
path: /health
port: 4000
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 5
```
## Capacity Planning For Production
<a id="capacity"></a>
In order to rightsize the production environment for your Fastify application,
it is highly recommended that you perform your own measurements against
different configurations of the environment, which may
use real CPU cores, virtual CPU cores (vCPU), or even fractional
vCPU cores. We will use the term vCPU throughout this
recommendation to represent any CPU type.
Tools such as [k6](https://github.com/grafana/k6)
or [autocannon](https://github.com/mcollina/autocannon) can be used for
conducting the necessary performance tests.
That said, you may also consider the following as a rule of thumb:
* To have the lowest possible latency, 2 vCPU are recommended per app
instance (e.g., a k8s pod). The second vCPU will mostly be used by the
garbage collector (GC) and libuv threadpool. This will minimize the latency
for your users, as well as the memory usage, as the GC will be run more
frequently. Also, the main thread won't have to stop to let the GC run.
* To optimize for throughput (handling the largest possible amount of
requests per second per vCPU available), consider using a smaller amount of vCPUs
per app instance. It is totally fine to run Node.js applications with 1 vCPU.
* You may experiment with an even smaller amount of vCPU, which may provide
even better throughput in certain use-cases. There are reports of API gateway
solutions working well with 100m-200m vCPU in Kubernetes.
See [Node's Event Loop From the Inside Out ](https://www.youtube.com/watch?v=P9csgxBgaZ8)
to understand the workings of Node.js in greater detail and make a
better determination about what your specific application needs.
## Running Multiple Instances
<a id="multiple"></a>
There are several use-cases where running multiple Fastify
apps on the same server might be considered. A common example
would be exposing metrics endpoints on a separate port,
to prevent public access, when using a reverse proxy or an ingress
firewall is not an option.
It is perfectly fine to spin up several Fastify instances within the same
Node.js process and run them concurrently, even in high load systems.
Each Fastify instance only generates as much load as the traffic it receives,
plus the memory used for that Fastify instance.

586
node_modules/fastify/docs/Guides/Serverless.md generated vendored Normal file
View File

@@ -0,0 +1,586 @@
<h1 align="center">Serverless</h1>
Run serverless applications and REST APIs using your existing Fastify
application. You may need to make code changes to work on your
serverless platform of choice. This document contains a small guide
for the most popular serverless providers and how to use
Fastify with them.
#### Should you use Fastify in a serverless platform?
That is up to you! Keep in mind, functions as a service should always use
small and focused functions, but you can also run an entire web application with
them. It is important to remember that the bigger the application the slower the
initial boot will be. The best way to run Fastify applications in serverless
environments is to use platforms like Google Cloud Run, AWS Fargate, Azure
Container Instances, and Vercel where the server can handle multiple requests
at the same time and make full use of Fastify's features.
One of the best features of using Fastify in serverless applications is the ease
of development. In your local environment, you will always run the Fastify
application directly without the need for any additional tools, while the same
code will be executed in your serverless platform of choice with an additional
snippet of code.
### Contents
- [AWS](#aws)
- [Genezio](#genezio)
- [Google Cloud Functions](#google-cloud-functions)
- [Google Firebase Functions](#google-firebase-functions)
- [Google Cloud Run](#google-cloud-run)
- [Netlify Lambda](#netlify-lambda)
- [Vercel](#vercel)
## AWS
To integrate with AWS, you have two choices of library:
- Using [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify)
which only adds API Gateway support but has heavy optimizations for fastify.
- Using [@h4ad/serverless-adapter](https://github.com/H4ad/serverless-adapter)
which is a little slower as it creates an HTTP request for each AWS event but
has support for more AWS services such as: AWS SQS, AWS SNS and others.
So you can decide which option is best for you, but you can test both libraries.
### Using @fastify/aws-lambda
The sample provided allows you to easily build serverless web
applications/services and RESTful APIs using Fastify on top of AWS Lambda and
Amazon API Gateway.
#### app.js
```js
const fastify = require('fastify');
function init() {
const app = fastify();
app.get('/', (request, reply) => reply.send({ hello: 'world' }));
return app;
}
if (require.main === module) {
// called directly i.e. "node app"
init().listen({ port: 3000 }, (err) => {
if (err) console.error(err);
console.log('server listening on 3000');
});
} else {
// required as a module => executed on aws lambda
module.exports = init;
}
```
When executed in your lambda function we do not need to listen to a specific
port, so we just export the wrapper function `init` in this case. The
[`lambda.js`](#lambdajs) file will use this export.
When you execute your Fastify application like always, i.e. `node app.js` *(the
detection for this could be `require.main === module`)*, you can normally listen
to your port, so you can still run your Fastify function locally.
#### lambda.js
```js
const awsLambdaFastify = require('@fastify/aws-lambda')
const init = require('./app');
const proxy = awsLambdaFastify(init())
// or
// const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] })
exports.handler = proxy;
// or
// exports.handler = (event, context, callback) => proxy(event, context, callback);
// or
// exports.handler = (event, context) => proxy(event, context);
// or
// exports.handler = async (event, context) => proxy(event, context);
```
We just require
[@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify) (make sure
you install the dependency `npm i @fastify/aws-lambda`) and our
[`app.js`](#appjs) file and call the exported `awsLambdaFastify` function with
the `app` as the only parameter. The resulting `proxy` function has the correct
signature to be used as a lambda `handler` function. This way all the incoming
events (API Gateway requests) are passed to the `proxy` function of
[@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify).
#### Example
An example deployable with
[claudia.js](https://claudiajs.com/tutorials/serverless-express.html) can be
found
[here](https://github.com/claudiajs/example-projects/tree/master/fastify-app-lambda).
### Considerations
- API Gateway does not support streams yet, so you are not able to handle
[streams](../Reference/Reply.md#streams).
- API Gateway has a timeout of 29 seconds, so it is important to provide a reply
during this time.
#### Beyond API Gateway
If you need to integrate with more AWS services, take a look at
[@h4ad/serverless-adapter](https://viniciusl.com.br/serverless-adapter/docs/main/frameworks/fastify)
on Fastify to find out how to integrate.
## Genezio
[Genezio](https://genezio.com/) is a platform designed to simplify the deployment
of serverless applications to the cloud.
[Genezio has a dedicated guide for deploying a Fastify application.](https://genezio.com/docs/frameworks/fastify/)
## Google Cloud Functions
### Creation of Fastify instance
```js
const fastify = require("fastify")({
logger: true // you can also define the level passing an object configuration to logger: {level: 'debug'}
});
```
### Add Custom `contentTypeParser` to Fastify instance
As explained [in issue
#946](https://github.com/fastify/fastify/issues/946#issuecomment-766319521),
since the Google Cloud Functions platform parses the body of the request before
it arrives at the Fastify instance, troubling the body request in case of `POST`
and `PATCH` methods, you need to add a custom [`Content-Type
Parser`](../Reference/ContentTypeParser.md) to mitigate this behavior.
```js
fastify.addContentTypeParser('application/json', {}, (req, body, done) => {
done(null, body.body);
});
```
### Define your endpoint (examples)
A simple `GET` endpoint:
```js
fastify.get('/', async (request, reply) => {
reply.send({message: 'Hello World!'})
})
```
Or a more complete `POST` endpoint with schema validation:
```js
fastify.route({
method: 'POST',
url: '/hello',
schema: {
body: {
type: 'object',
properties: {
name: { type: 'string'}
},
required: ['name']
},
response: {
200: {
type: 'object',
properties: {
message: {type: 'string'}
}
}
},
},
handler: async (request, reply) => {
const { name } = request.body;
reply.code(200).send({
message: `Hello ${name}!`
})
}
})
```
### Implement and export the function
Final step, implement the function to handle the request and pass it to Fastify
by emitting `request` event to `fastify.server`:
```js
const fastifyFunction = async (request, reply) => {
await fastify.ready();
fastify.server.emit('request', request, reply)
}
exports.fastifyFunction = fastifyFunction;
```
### Local test
Install [Google Functions Framework for
Node.js](https://github.com/GoogleCloudPlatform/functions-framework-nodejs).
You can install it globally:
```bash
npm i -g @google-cloud/functions-framework
```
Or as a development library:
```bash
npm i -D @google-cloud/functions-framework
```
Then you can run your function locally with Functions Framework:
```bash
npx @google-cloud/functions-framework --target=fastifyFunction
```
Or add this command to your `package.json` scripts:
```json
"scripts": {
...
"dev": "npx @google-cloud/functions-framework --target=fastifyFunction"
...
}
```
and run it with `npm run dev`.
### Deploy
```bash
gcloud functions deploy fastifyFunction \
--runtime nodejs14 --trigger-http --region $GOOGLE_REGION --allow-unauthenticated
```
#### Read logs
```bash
gcloud functions logs read
```
#### Example request to `/hello` endpoint
```bash
curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \
-H "Content-Type: application/json" \
-d '{ "name": "Fastify" }'
{"message":"Hello Fastify!"}
```
### References
- [Google Cloud Functions - Node.js Quickstart
](https://cloud.google.com/functions/docs/quickstart-nodejs)
## Google Firebase Functions
Follow this guide if you want to use Fastify as the HTTP framework for
Firebase Functions instead of the vanilla JavaScript router provided with
`onRequest(async (req, res) => {}`.
### The onRequest() handler
We use the `onRequest` function to wrap our Fastify application instance.
As such, we'll begin with importing it to the code:
```js
const { onRequest } = require("firebase-functions/v2/https")
```
### Creation of Fastify instance
Create the Fastify instance and encapsulate the returned application instance
in a function that will register routes, await the server's processing of
plugins, hooks, and other settings. As follows:
```js
const fastify = require("fastify")({
logger: true,
})
const fastifyApp = async (request, reply) => {
await registerRoutes(fastify)
await fastify.ready()
fastify.server.emit("request", request, reply)
}
```
### Add Custom `contentTypeParser` to Fastify instance and define endpoints
Firebase Function's HTTP layer already parses the request
and makes a JSON payload available. It also provides access
to the raw body, unparsed, which is useful for calculating
request signatures to validate HTTP webhooks.
Add as follows to the `registerRoutes()` function:
```js
async function registerRoutes (fastify) {
fastify.addContentTypeParser("application/json", {}, (req, payload, done) => {
// useful to include the request's raw body on the `req` object that will
// later be available in your other routes so you can calculate the HMAC
// if needed
req.rawBody = payload.rawBody
// payload.body is already the parsed JSON so we just fire the done callback
// with it
done(null, payload.body)
})
// define your endpoints here...
fastify.post("/some-route-here", async (request, reply) => {})
fastify.get('/', async (request, reply) => {
reply.send({message: 'Hello World!'})
})
}
```
### Export the function using Firebase onRequest
Final step is to export the Fastify app instance to Firebase's own
`onRequest()` function so it can pass the request and reply objects to it:
```js
exports.app = onRequest(fastifyApp)
```
### Local test
Install the Firebase tools functions so you can use the CLI:
```bash
npm i -g firebase-tools
```
Then you can run your function locally with:
```bash
firebase emulators:start --only functions
```
### Deploy
Deploy your Firebase Functions with:
```bash
firebase deploy --only functions
```
#### Read logs
Use the Firebase tools CLI:
```bash
firebase functions:log
```
### References
- [Fastify on Firebase Functions](https://github.com/lirantal/lemon-squeezy-firebase-webhook-fastify/blob/main/package.json)
- [An article about HTTP webhooks on Firebase Functions and Fastify: A Practical Case Study with Lemon Squeezy](https://lirantal.com/blog/http-webhooks-firebase-functions-fastify-practical-case-study-lemon-squeezy)
## Google Cloud Run
Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless
**container** environment. Its primary purpose is to provide an
infrastructure-abstracted environment to run arbitrary containers. As a result,
Fastify can be deployed to Google Cloud Run with little-to-no code changes from
the way you would write your Fastify app normally.
*Follow the steps below to deploy to Google Cloud Run if you are already
familiar with gcloud or just follow their
[quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*.
### Adjust Fastify server
For Fastify to properly listen for requests within the container, be
sure to set the correct port and address:
```js
function build() {
const fastify = Fastify({ trustProxy: true })
return fastify
}
async function start() {
// Google Cloud Run will set this environment variable for you, so
// you can also use it to detect if you are running in Cloud Run
const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined
// You must listen on the port Cloud Run provides
const port = process.env.PORT || 3000
// You must listen on all IPV4 addresses in Cloud Run
const host = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined
try {
const server = build()
const address = await server.listen({ port, host })
console.log(`Listening on ${address}`)
} catch (err) {
console.error(err)
process.exit(1)
}
}
module.exports = build
if (require.main === module) {
start()
}
```
### Add a Dockerfile
You can add any valid `Dockerfile` that packages and runs a Node app. A basic
`Dockerfile` can be found in the official [gcloud
docs](https://github.com/knative/docs/blob/2d654d1fd6311750cc57187a86253c52f273d924/docs/serving/samples/hello-world/helloworld-nodejs/Dockerfile).
```Dockerfile
# Use the official Node.js 10 image.
# https://hub.docker.com/_/node
FROM node:10
# Create and change to the app directory.
WORKDIR /usr/src/app
# Copy application dependency manifests to the container image.
# A wildcard is used to ensure both package.json AND package-lock.json are copied.
# Copying this separately prevents re-running npm install on every code change.
COPY package*.json ./
# Install production dependencies.
RUN npm i --production
# Copy local code to the container image.
COPY . .
# Run the web service on container startup.
CMD [ "npm", "start" ]
```
### Add a .dockerignore
To keep build artifacts out of your container (which keeps it small and improves
build times) add a `.dockerignore` file like the one below:
```dockerignore
Dockerfile
README.md
node_modules
npm-debug.log
```
### Submit build
Next, submit your app to be built into a Docker image by running the following
command (replacing `PROJECT-ID` and `APP-NAME` with your GCP project id and an
app name):
```bash
gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME
```
### Deploy Image
After your image has built, you can deploy it with the following command:
```bash
gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed
```
Your app will be accessible from the URL GCP provides.
## netlify-lambda
First, please perform all preparation steps related to **AWS Lambda**.
Create a folder called `functions`, then create `server.js` (and your endpoint
path will be `server.js`) inside the `functions` folder.
### functions/server.js
```js
export { handler } from '../lambda.js'; // Change `lambda.js` path to your `lambda.js` path
```
### netlify.toml
```toml
[build]
# This will be run the site build
command = "npm run build:functions"
# This is the directory is publishing to netlify's CDN
# and this is directory of your front of your app
# publish = "build"
# functions build directory
functions = "functions-build" # always appends `-build` folder to your `functions` folder for builds
```
### webpack.config.netlify.js
**Do not forget to add this Webpack config, or else problems may occur**
```js
const nodeExternals = require('webpack-node-externals');
const dotenv = require('dotenv-safe');
const webpack = require('webpack');
const env = process.env.NODE_ENV || 'production';
const dev = env === 'development';
if (dev) {
dotenv.config({ allowEmptyValues: true });
}
module.exports = {
mode: env,
devtool: dev ? 'eval-source-map' : 'none',
externals: [nodeExternals()],
devServer: {
proxy: {
'/.netlify': {
target: 'http://localhost:9000',
pathRewrite: { '^/.netlify/functions': '' }
}
}
},
module: {
rules: []
},
plugins: [
new webpack.DefinePlugin({
'process.env.APP_ROOT_PATH': JSON.stringify('/'),
'process.env.NETLIFY_ENV': true,
'process.env.CONTEXT': env
})
]
};
```
### Scripts
Add this command to your `package.json` *scripts*
```json
"scripts": {
...
"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"
...
}
```
Then it should work fine.
## Vercel
[Vercel](https://vercel.com) fully supports deploying Fastify applications.
Additionally, with Vercel's
[Fluid compute](https://vercel.com/docs/functions/fluid-compute), you can combine
server-like concurrency with the autoscaling properties of traditional
serverless functions.
Get started with the
[Fastify Node.js template on Vercel](
https://vercel.com/templates/other/fastify-serverless-function).
[Fluid compute](https://vercel.com/docs/functions/fluid-compute) currently
requires an explicit opt-in. Learn more about enabling Fluid compute
[here](
https://vercel.com/docs/functions/fluid-compute#how-to-enable-fluid-compute).

246
node_modules/fastify/docs/Guides/Style-Guide.md generated vendored Normal file
View File

@@ -0,0 +1,246 @@
# Fastify Style Guide
## Welcome
Welcome to *Fastify Style Guide*. This guide is here to provide you with a
conventional writing style for users writing developer documentation on our Open
Source framework. Each topic is precise and well explained to help you write
documentation users can easily understand and implement.
## Who is this guide for?
This guide is for anyone who loves to build with Fastify or wants to contribute
to our documentation. You do not need to be an expert in writing technical
documentation. This guide is here to help you.
Visit the [contribute](https://fastify.dev/contribute) page on our website or
read the
[CONTRIBUTING.md](https://github.com/fastify/fastify/blob/main/CONTRIBUTING.md)
file on GitHub to join our Open Source folks.
## Before you write
You need to know the following:
* JavaScript
* Node.js
* Git
* GitHub
* Markdown
* HTTP
* NPM
### Consider your Audience
Before you start writing, think about your audience. In this case, your audience
should already know HTTP, JavaScript, NPM, and Node.js. It is necessary to keep
your readers in mind because they are the ones consuming your content. You want
to give as much useful information as possible. Consider the vital things they
need to know and how they can understand them. Use words and references that
readers can relate to easily. Ask for feedback from the community, it can help
you write better documentation that focuses on the user and what you want to
achieve.
### Get straight to the point
Give your readers a clear and precise action to take. Start with what is most
important. This way, you can help them find what they need faster. Mostly,
readers tend to read the first content on a page, and many will not scroll
further.
**Example**
Less like this: Colons are very important to register a parametric path. It lets
the framework know there is a new parameter created. You can place the colon
before the parameter name so the parametric path can be created.
More Like this: To register a parametric path, put a colon before the parameter
name. Using a colon lets the framework know it is a parametric path and not a
static path.
### Avoid adding video or image content
Do not add videos or screenshots to the documentation. It is easier to keep
under version control. Videos and images will eventually end up becoming
outdated as new updates keep developing. Instead, make a referral link or a
YouTube video. You can add links by using `[Title](www.websitename.com)` in the
markdown.
**Example**
```
To learn more about hooks, see [Fastify hooks](https://fastify.dev/docs/latest/Reference/Hooks/).
```
Result:
>To learn more about hooks, see [Fastify
>hooks](https://fastify.dev/docs/latest/Reference/Hooks/).
### Avoid plagiarism
Make sure you avoid copying other people's work. Keep it as original as
possible. You can learn from what they have done and reference where it is from
if you use a particular quote from their work.
## Word Choice
There are a few things you need to use and avoid when writing your documentation
to improve readability for readers and make documentation neat, direct, and
clean.
### When to use the second person "you" as the pronoun
When writing articles or guides, your content should communicate directly to
readers in the second person ("you") addressed form. It is easier to give them
direct instruction on what to do on a particular topic. To see an example, visit
the [Plugins Guide](./Plugins-Guide.md).
**Example**
Less like this: we can use the following plugins.
More like this: You can use the following plugins.
> According to [Wikipedia](#), ***You*** is usually a second person pronoun.
> Also, used to refer to an indeterminate person, as a more common alternative
> to a very formal indefinite pronoun.
## When to avoid the second person "you" as the pronoun
One of the main rules of formal writing such as reference documentation, or API
documentation, is to avoid the second person ("you") or directly addressing the
reader.
**Example**
Less like this: You can use the following recommendation as an example.
More like this: As an example, the following recommendations should be
referenced.
To view a live example, refer to the [Decorators](../Reference/Decorators.md)
reference document.
### Avoid using contractions
Contractions are the shortened version of written and spoken forms of a word,
i.e. using "don't" instead of "do not". Avoid contractions to provide a more
formal tone.
### Avoid using condescending terms
Condescending terms are words that include:
* Just
* Easy
* Simply
* Basically
* Obviously
The reader may not find it easy to use Fastify's framework and plugins; avoid
words that make it sound simple, easy, offensive, or insensitive. Not everyone
who reads the documentation has the same level of understanding.
### Starting with a verb
Mostly start your description with a verb, which makes it simple and precise for
the reader to follow. Prefer using present tense because it is easier to read
and understand than the past or future tense.
**Example**
Less like this: There is a need for Node.js to be installed before you can be
able to use Fastify.
More like this: Install Node.js to make use of Fastify.
### Grammatical moods
Grammatical moods are a great way to express your writing. Avoid sounding too
bossy while making a direct statement. Know when to switch between indicative,
imperative, and subjunctive moods.
**Indicative** - Use when making a factual statement or question.
Example: Since there is no testing framework available, "Fastify recommends ways
to write tests".
**Imperative** - Use when giving instructions, actions, commands, or when you
write your headings.
Example: Install dependencies before starting development.
**Subjunctive** - Use when making suggestions, hypotheses, or non-factual
statements.
Example: Reading the documentation on our website is recommended to get
comprehensive knowledge of the framework.
### Use **active** voice instead of **passive**
Using active voice is a more compact and direct way of conveying your
documentation.
**Example**
Passive: The node dependencies and packages are installed by npm.
Active: npm installs packages and node dependencies.
## Writing Style
### Documentation titles
When creating a new guide, API, or reference in the `/docs/` directory, use
short titles that best describe the topic of your documentation. Name your files
in kebab-cases and avoid Raw or camelCase. To learn more about kebab-case you
can visit this medium article on [Case
Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-snake-and-kebab-case-981407998841).
**Examples**:
>`hook-and-plugins.md`,
`adding-test-plugins.md`,
`removing-requests.md`.
### Hyperlinks
Hyperlinks should have a clear title of what they reference. Here is how your
hyperlink should look:
```MD
<!-- More like this -->
// Add clear & brief description
[Fastify Plugins] (https://fastify.dev/docs/latest/Plugins/)
<!--Less like this -->
// incomplete description
[Fastify] (https://fastify.dev/docs/latest/Plugins/)
// Adding title in link brackets
[](https://fastify.dev/docs/latest/Plugins/ "fastify plugin")
// Empty title
[](https://fastify.dev/docs/latest/Plugins/)
// Adding links localhost URLs instead of using code strings (``)
[http://localhost:3000/](http://localhost:3000/)
```
Include in your documentation as many essential references as possible, but
avoid having numerous links when writing for beginners to avoid distractions.

481
node_modules/fastify/docs/Guides/Testing.md generated vendored Normal file
View File

@@ -0,0 +1,481 @@
<h1 style="text-align: center;">Fastify</h1>
# Testing
<a id="testing"></a>
Testing is one of the most important parts of developing an application. Fastify
is very flexible when it comes to testing and is compatible with most testing
frameworks (such as [Node Test Runner](https://nodejs.org/api/test.html),
which is used in the examples below).
## Application
Let's `cd` into a fresh directory called 'testing-example' and type `npm init
-y` in our terminal.
Run `npm i fastify && npm i pino-pretty -D`
### Separating concerns makes testing easy
First, we are going to separate our application code from our server code:
**app.js**:
```js
'use strict'
const fastify = require('fastify')
function build(opts={}) {
const app = fastify(opts)
app.get('/', async function (request, reply) {
return { hello: 'world' }
})
return app
}
module.exports = build
```
**server.js**:
```js
'use strict'
const server = require('./app')({
logger: {
level: 'info',
transport: {
target: 'pino-pretty'
}
}
})
server.listen({ port: 3000 }, (err, address) => {
if (err) {
server.log.error(err)
process.exit(1)
}
})
```
### Benefits of using fastify.inject()
Fastify comes with built-in support for fake HTTP injection thanks to
[`light-my-request`](https://github.com/fastify/light-my-request).
Before introducing any tests, we will use the `.inject` method to make a fake
request to our route:
**app.test.js**:
```js
'use strict'
const build = require('./app')
const test = async () => {
const app = build()
const response = await app.inject({
method: 'GET',
url: '/'
})
console.log('status code: ', response.statusCode)
console.log('body: ', response.body)
}
test()
```
First, our code will run inside an asynchronous function, giving us access to
async/await.
`.inject` ensures all registered plugins have booted up and our application is
ready to test. Finally, we pass the request method we want to use and a route.
Using await we can store the response without a callback.
Run the test file in your terminal `node app.test.js`
```sh
status code: 200
body: {"hello":"world"}
```
### Testing with HTTP injection
Now we can replace our `console.log` calls with actual tests!
In your `package.json` change the "test" script to:
`"test": "node --test --watch"`
**app.test.js**:
```js
'use strict'
const { test } = require('node:test')
const build = require('./app')
test('requests the "/" route', async t => {
t.plan(1)
const app = build()
const response = await app.inject({
method: 'GET',
url: '/'
})
t.assert.strictEqual(response.statusCode, 200, 'returns a status code of 200')
})
```
Finally, run `npm test` in the terminal and see your test results!
The `inject` method can do much more than a simple GET request to a URL:
```js
fastify.inject({
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object
}, (error, response) => {
// your tests
})
```
`.inject` methods can also be chained by omitting the callback function:
```js
fastify
.inject()
.get('/')
.headers({ foo: 'bar' })
.query({ foo: 'bar' })
.end((err, res) => { // the .end call will trigger the request
console.log(res.payload)
})
```
or in the promisified version
```js
fastify
.inject({
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object
})
.then(response => {
// your tests
})
.catch(err => {
// handle error
})
```
Async await is supported as well!
```js
try {
const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object })
// your tests
} catch (err) {
// handle error
}
```
#### Another Example:
**app.js**
```js
const Fastify = require('fastify')
function buildFastify () {
const fastify = Fastify()
fastify.get('/', function (request, reply) {
reply.send({ hello: 'world' })
})
return fastify
}
module.exports = buildFastify
```
**test.js**
```js
const { test } = require('node:test')
const buildFastify = require('./app')
test('GET `/` route', t => {
t.plan(4)
const fastify = buildFastify()
// At the end of your tests it is highly recommended to call `.close()`
// to ensure that all connections to external services get closed.
t.after(() => fastify.close())
fastify.inject({
method: 'GET',
url: '/'
}, (err, response) => {
t.assert.ifError(err)
t.assert.strictEqual(response.statusCode, 200)
t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')
t.assert.deepStrictEqual(response.json(), { hello: 'world' })
})
})
```
### Testing with a running server
Fastify can also be tested after starting the server with `fastify.listen()` or
after initializing routes and plugins with `fastify.ready()`.
#### Example:
Uses **app.js** from the previous example.
**test-listen.js** (testing with [`undici`](https://www.npmjs.com/package/undici))
```js
const { test } = require('node:test')
const { Client } = require('undici')
const buildFastify = require('./app')
test('should work with undici', async t => {
t.plan(2)
const fastify = buildFastify()
await fastify.listen()
const client = new Client(
'http://localhost:' + fastify.server.address().port, {
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10
}
)
t.after(() => {
fastify.close()
client.close()
})
const response = await client.request({ method: 'GET', path: '/' })
t.assert.strictEqual(await response.body.text(), '{"hello":"world"}')
t.assert.strictEqual(response.statusCode, 200)
})
```
Alternatively, starting with Node.js 18,
[`fetch`](https://nodejs.org/docs/latest-v18.x/api/globals.html#fetch)
may be used without requiring any extra dependencies:
**test-listen.js**
```js
const { test } = require('node:test')
const buildFastify = require('./app')
test('should work with fetch', async t => {
t.plan(3)
const fastify = buildFastify()
t.after(() => fastify.close())
await fastify.listen()
const response = await fetch(
'http://localhost:' + fastify.server.address().port
)
t.assert.strictEqual(response.status, 200)
t.assert.strictEqual(
response.headers.get('content-type'),
'application/json; charset=utf-8'
)
const jsonResult = await response.json()
t.assert.strictEqual(jsonResult.hello, 'world')
})
```
**test-ready.js** (testing with
[`SuperTest`](https://www.npmjs.com/package/supertest))
```js
const { test } = require('node:test')
const supertest = require('supertest')
const buildFastify = require('./app')
test('GET `/` route', async (t) => {
const fastify = buildFastify()
t.after(() => fastify.close())
await fastify.ready()
const response = await supertest(fastify.server)
.get('/')
.expect(200)
.expect('Content-Type', 'application/json; charset=utf-8')
t.assert.deepStrictEqual(response.body, { hello: 'world' })
})
```
### How to inspect node tests
1. Isolate your test by passing the `{only: true}` option
```javascript
test('should ...', {only: true}, t => ...)
```
2. Run `node --test`
```bash
> node --test --test-only --inspect-brk test/<test-file.test.js>
```
- `--test-only` specifies to run tests with the `only` option enabled
- `--inspect-brk` will launch the node debugger
3. In VS Code, create and launch a `Node.js: Attach` debug configuration. No
modification should be necessary.
Now you should be able to step through your test file (and the rest of
`Fastify`) in your code editor.
## Plugins
Let's `cd` into a fresh directory called 'testing-plugin-example' and type `npm init
-y` in our terminal.
Run `npm i fastify fastify-plugin`
**plugin/myFirstPlugin.js**:
```js
const fP = require("fastify-plugin")
async function myPlugin(fastify, options) {
fastify.decorateRequest("helloRequest", "Hello World")
fastify.decorate("helloInstance", "Hello Fastify Instance")
}
module.exports = fP(myPlugin)
```
A basic example of a Plugin. See [Plugin Guide](./Plugins-Guide.md)
**test/myFirstPlugin.test.js**:
```js
const Fastify = require("fastify");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
// Create a mock fastify application to test the plugin
const fastify = Fastify()
fastify.register(myPlugin)
// Add an endpoint of your choice
fastify.get("/", async (request, reply) => {
return ({ message: request.helloRequest })
})
// Use fastify.inject to fake a HTTP Request
const fastifyResponse = await fastify.inject({
method: "GET",
url: "/"
})
console.log('status code: ', fastifyResponse.statusCode)
console.log('body: ', fastifyResponse.body)
})
```
Learn more about [```fastify.inject()```](#benefits-of-using-fastifyinject).
Run the test file in your terminal `node test/myFirstPlugin.test.js`
```sh
status code: 200
body: {"message":"Hello World"}
```
Now we can replace our `console.log` calls with actual tests!
In your `package.json` change the "test" script to:
`"test": "node --test --watch"`
Create the test for the endpoint.
**test/myFirstPlugin.test.js**:
```js
const Fastify = require("fastify");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
// Specifies the number of test
t.plan(2)
const fastify = Fastify()
fastify.register(myPlugin)
fastify.get("/", async (request, reply) => {
return ({ message: request.helloRequest })
})
const fastifyResponse = await fastify.inject({
method: "GET",
url: "/"
})
t.assert.strictEqual(fastifyResponse.statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })
})
```
Finally, run `npm test` in the terminal and see your test results!
Test the ```.decorate()``` and ```.decorateRequest()```.
**test/myFirstPlugin.test.js**:
```js
const Fastify = require("fastify");
const { test }= require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
t.plan(5)
const fastify = Fastify()
fastify.register(myPlugin)
fastify.get("/", async (request, reply) => {
// Testing the fastify decorators
t.assert.ifError(request.helloRequest)
t.assert.ok(request.helloRequest, "Hello World")
t.assert.ok(fastify.helloInstance, "Hello Fastify Instance")
return ({ message: request.helloRequest })
})
const fastifyResponse = await fastify.inject({
method: "GET",
url: "/"
})
t.assert.strictEqual(fastifyResponse.statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })
})
```

103
node_modules/fastify/docs/Guides/Write-Plugin.md generated vendored Normal file
View File

@@ -0,0 +1,103 @@
<h1 style="text-align: center;">Fastify</h1>
# How to write a good plugin
First, thank you for deciding to write a plugin for Fastify. Fastify is a
minimal framework and plugins are its strength, so thank you.
The core principles of Fastify are performance, low overhead, and providing a
good experience to our users. When writing a plugin, it is important to keep
these principles in mind. Therefore, in this document, we will analyze what
characterizes a quality plugin.
*Need some inspiration? You can use the label ["plugin
suggestion"](https://github.com/fastify/fastify/issues?q=is%3Aissue+is%3Aopen+label%3A%22plugin+suggestion%22)
in our issue tracker!*
## Code
Fastify uses different techniques to optimize its code, many of which are
documented in our Guides. We highly recommend you read [the hitchhiker's guide
to plugins](./Plugins-Guide.md) to discover all the APIs you can use to build
your plugin and learn how to use them.
Do you have a question or need some advice? We are more than happy to help you!
Just open an issue in our [help repository](https://github.com/fastify/help).
Once you submit a plugin to our [ecosystem list](./Ecosystem.md), we will review
your code and help you improve it if necessary.
## Documentation
Documentation is extremely important. If your plugin is not well documented we
will not accept it to the ecosystem list. Lack of quality documentation makes it
more difficult for people to use your plugin, and will likely result in it going
unused.
If you want to see some good examples of how to document a plugin take a look
at:
- [`@fastify/caching`](https://github.com/fastify/fastify-caching)
- [`@fastify/compress`](https://github.com/fastify/fastify-compress)
- [`@fastify/cookie`](https://github.com/fastify/fastify-cookie)
- [`@fastify/under-pressure`](https://github.com/fastify/under-pressure)
- [`@fastify/view`](https://github.com/fastify/point-of-view)
## License
You can license your plugin as you prefer, we do not enforce any kind of
license.
We prefer the [MIT license](https://choosealicense.com/licenses/mit/) because we
think it allows more people to use the code freely. For a list of alternative
licenses see the [OSI list](https://opensource.org/licenses) or GitHub's
[choosealicense.com](https://choosealicense.com/).
## Examples
Always put an example file in your repository. Examples are very helpful for
users and give a very fast way to test your plugin. Your users will be grateful.
## Test
A plugin **must** be thoroughly tested to verify that is working properly.
A plugin without tests will not be accepted to the ecosystem list. A lack of
tests does not inspire trust nor guarantee that the code will continue to work
among different versions of its dependencies.
We do not enforce any testing library. We use [`node:test`](https://nodejs.org/api/test.html)
since it offers out-of-the-box parallel testing and code coverage, but it is up
to you to choose your library of preference.
We highly recommend you read the [Plugin Testing](./Testing.md#plugins) to
learn about how to test your plugins.
## Code Linter
It is not mandatory, but we highly recommend you use a code linter in your
plugin. It will ensure a consistent code style and help you to avoid many
errors.
We use [`standard`](https://standardjs.com/) since it works without the need to
configure it and is very easy to integrate into a test suite.
## Continuous Integration
It is not mandatory, but if you release your code as open source, it helps to
use Continuous Integration to ensure contributions do not break your plugin and
to show that the plugin works as intended. Both
[CircleCI](https://circleci.com/) and [GitHub
Actions](https://github.com/features/actions) are free for open source projects
and easy to set up.
In addition, you can enable services like [Dependabot](https://github.com/dependabot),
which will help you keep your dependencies up to date and discover if a new
release of Fastify has some issues with your plugin.
## Let's start!
Awesome, now you know everything you need to know about how to write a good
plugin for Fastify! After you have built one (or more!) let us know! We will add
it to the [ecosystem](https://github.com/fastify/fastify#ecosystem) section of
our documentation!
If you want to see some real world examples, check out:
- [`@fastify/view`](https://github.com/fastify/point-of-view) Templates
rendering (*ejs, pug, handlebars, marko*) plugin support for Fastify.
- [`@fastify/mongodb`](https://github.com/fastify/fastify-mongodb) Fastify
MongoDB connection plugin, with this you can share the same MongoDB connection
pool in every part of your server.
- [`@fastify/multipart`](https://github.com/fastify/fastify-multipart) Multipart
support for Fastify.
- [`@fastify/helmet`](https://github.com/fastify/fastify-helmet) Important
security headers for Fastify.

View File

@@ -0,0 +1,34 @@
<h1 align="center">Fastify</h1>
## How to write your own type provider
Things to keep in mind when implementing a custom [type provider](../Reference/Type-Providers.md):
### Type Contravariance
Whereas exhaustive type narrowing checks normally rely on `never` to represent
an unreachable state, reduction in type provider interfaces should only be done
up to `unknown`.
The reasoning is that certain methods of `FastifyInstance` are
contravariant on `TypeProvider`, which can lead to TypeScript surfacing
assignability issues unless the custom type provider interface is
substitutable with `FastifyTypeProviderDefault`.
For example, `FastifyTypeProviderDefault` will not be assignable to the following:
```ts
export interface NotSubstitutableTypeProvider extends FastifyTypeProvider {
// bad, nothing is assignable to `never` (except for itself)
validator: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
serializer: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
}
```
Unless changed to:
```ts
export interface SubstitutableTypeProvider extends FastifyTypeProvider {
// good, anything can be assigned to `unknown`
validator: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : unknown;
serializer: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : unknown;
}
```

View File

@@ -0,0 +1,255 @@
<h1 align="center">Fastify</h1>
## `Content-Type` Parser
Fastify natively supports `'application/json'` and `'text/plain'` content types
with a default charset of `utf-8`. These default parsers can be changed or
removed.
Unsupported content types will throw an `FST_ERR_CTP_INVALID_MEDIA_TYPE` error.
To support other content types, use the `addContentTypeParser` API or an
existing [plugin](https://fastify.dev/ecosystem/).
As with other APIs, `addContentTypeParser` is encapsulated in the scope in which
it is declared. If declared in the root scope, it is available everywhere; if
declared in a plugin, it is available only in that scope and its children.
Fastify automatically adds the parsed request payload to the [Fastify
request](./Request.md) object, accessible via `request.body`.
Note that for `GET` and `HEAD` requests, the payload is never parsed. For
`OPTIONS` and `DELETE` requests, the payload is parsed only if a valid
`content-type` header is provided. Unlike `POST`, `PUT`, and `PATCH`, the
[catch-all](#catch-all) parser is not executed, and the payload is simply not
parsed.
> ⚠ Warning:
> When using regular expressions to detect `Content-Type`, it is important to
> ensure proper detection. For example, to match `application/*`, use
> `/^application\/([\w-]+);?/` to match the
> [essence MIME type](https://mimesniff.spec.whatwg.org/#mime-type-miscellaneous)
> only.
### Usage
```js
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
// Handle multiple content types with the same function
fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Async is also supported in Node versions >= 8.0.0
fastify.addContentTypeParser('application/jsoff', async function (request, payload) {
const res = await jsoffParserAsync(payload)
return res
})
// Handle all content types that matches RegExp
fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) {
imageParser(payload, function (err, body) {
done(err, body)
})
})
// Can use default JSON/Text parser for different content Types
fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefaultJsonParser('ignore', 'ignore'))
```
Fastify first tries to match a content-type parser with a `string` value before
trying to find a matching `RegExp`. For overlapping content types, it starts
with the last one configured and ends with the first (last in, first out).
To specify a general content type more precisely, first specify the general
type, then the specific one, as shown below.
```js
// Here only the second content type parser is called because its value also matches the first one
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
// Here the desired behavior is achieved because fastify first tries to match the
// `application/vnd.custom+xml` content type parser
fastify.addContentTypeParser('application/vnd.custom', (request, body, done) => {} )
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
```
### Using addContentTypeParser with fastify.register
When using `addContentTypeParser` with `fastify.register`, avoid `await`
when registering routes. Using `await` makes route registration asynchronous,
potentially registering routes before `addContentTypeParser` is set.
#### Correct Usage
```js
const fastify = require('fastify')();
fastify.register((fastify, opts) => {
fastify.addContentTypeParser('application/json', function (request, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
fastify.get('/hello', async (req, res) => {});
});
```
In addition to `addContentTypeParser`, the `hasContentTypeParser`,
`removeContentTypeParser`, and `removeAllContentTypeParsers` APIs are available.
#### hasContentTypeParser
Use the `hasContentTypeParser` API to check if a specific content type parser
exists.
```js
if (!fastify.hasContentTypeParser('application/jsoff')){
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsoffParser(payload, function (err, body) {
done(err, body)
})
})
}
```
#### removeContentTypeParser
`removeContentTypeParser` can remove a single content type or an array of
content types, supporting both `string` and `RegExp`.
```js
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
// Removes the both built-in content type parsers so that only the content type parser for text/html is available
fastify.removeContentTypeParser(['application/json', 'text/plain'])
```
#### removeAllContentTypeParsers
The `removeAllContentTypeParsers` API removes all existing content type parsers
eliminating the need to specify each one individually. This API supports
encapsulation and is useful for registering a
[catch-all content type parser](#catch-all) that should be executed for every
content type, ignoring built-in parsers.
```js
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('text/xml', function (request, payload, done) {
xmlParser(payload, function (err, body) {
done(err, body)
})
})
```
> Note: `function(req, done)` and `async function(req)` are
> still supported but deprecated.
#### Body Parser
The request body can be parsed in two ways. First, add a custom content type
parser and handle the request stream. Or second, use the `parseAs` option in the
`addContentTypeParser` API, specifying `'string'` or `'buffer'`. Fastify will
handle the stream, check the [maximum size](./Server.md#factory-body-limit) of
the body, and the content length. If the limit is exceeded, the custom parser
will not be invoked.
```js
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
try {
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
done(err, undefined)
}
})
```
See
[`example/parser.js`](https://github.com/fastify/fastify/blob/main/examples/parser.js)
for an example.
##### Custom Parser Options
+ `parseAs` (string): `'string'` or `'buffer'` to designate how the incoming
data should be collected. Default: `'buffer'`.
+ `bodyLimit` (number): The maximum payload size, in bytes, that the custom
parser will accept. Defaults to the global body limit passed to the [`Fastify
factory function`](./Server.md#bodylimit).
#### Catch-All
To catch all requests regardless of content type, use the `'*'` content type:
```js
fastify.addContentTypeParser('*', function (request, payload, done) {
let data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
```
All requests without a corresponding content type parser will be handled by
this function.
This is also useful for piping the request stream. Define a content parser like:
```js
fastify.addContentTypeParser('*', function (request, payload, done) {
done()
})
```
And then access the core HTTP request directly for piping:
```js
app.post('/hello', (request, reply) => {
reply.send(request.raw)
})
```
Here is a complete example that logs incoming [json
line](https://jsonlines.org/) objects:
```js
const split2 = require('split2')
const pump = require('pump')
fastify.addContentTypeParser('*', (request, payload, done) => {
done(null, pump(payload, split2(JSON.parse)))
})
fastify.route({
method: 'POST',
url: '/api/log/jsons',
handler: (req, res) => {
req.body.on('data', d => console.log(d)) // log every incoming object
}
})
```
For piping file uploads, check out
[`@fastify/multipart`](https://github.com/fastify/fastify-multipart).
To execute the content type parser on all content types, call
`removeAllContentTypeParsers` first.
```js
// Without this call, the request body with the content type application/json would be processed by the built-in JSON parser
fastify.removeAllContentTypeParsers()
fastify.addContentTypeParser('*', function (request, payload, done) {
const data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)
})
})
```

566
node_modules/fastify/docs/Reference/Decorators.md generated vendored Normal file
View File

@@ -0,0 +1,566 @@
<h1 align="center">Fastify</h1>
## Decorators
The decorators API customizes core Fastify objects, such as the server instance
and any request and reply objects used during the HTTP request lifecycle. It
can attach any type of property to core objects, e.g., functions, plain
objects, or native types.
This API is *synchronous*. Defining a decoration asynchronously could result in
the Fastify instance booting before the decoration completes. To register an
asynchronous decoration, use the `register` API with `fastify-plugin`. See the
[Plugins](./Plugins.md) documentation for more details.
Decorating core objects with this API allows the underlying JavaScript engine to
optimize the handling of server, request, and reply objects. This is
accomplished by defining the shape of all such object instances before they are
instantiated and used. As an example, the following is not recommended because
it will change the shape of objects during their lifecycle:
```js
// Bad example! Continue reading.
// Attach a user property to the incoming request before the request
// handler is invoked.
fastify.addHook('preHandler', function (req, reply, done) {
req.user = 'Bob Dylan'
done()
})
// Use the attached user property in the request handler.
fastify.get('/', function (req, reply) {
reply.send(`Hello, ${req.user}`)
})
```
The above example mutates the request object after instantiation, causing the
JavaScript engine to deoptimize access. Using the decoration API avoids this
deoptimization:
```js
// Decorate request with a 'user' property
fastify.decorateRequest('user', '')
// Update our property
fastify.addHook('preHandler', (req, reply, done) => {
req.user = 'Bob Dylan'
done()
})
// And finally access it
fastify.get('/', (req, reply) => {
reply.send(`Hello, ${req.user}!`)
})
```
Keep the initial shape of a decorated field close to its future dynamic value.
Initialize a decorator as `''` for strings and `null` for objects or functions.
This works only with value types; reference types will throw an error during
Fastify startup. See [decorateRequest](#decorate-request) and
[JavaScript engine fundamentals: Shapes
and Inline Caches](https://mathiasbynens.be/notes/shapes-ics)
for more information.
### Usage
<a id="usage"></a>
#### `decorate(name, value, [dependencies])`
<a id="decorate"></a>
This method customizes the Fastify [server](./Server.md) instance.
For example, to attach a new method to the server instance:
```js
fastify.decorate('utility', function () {
// Something very useful
})
```
Non-function values can also be attached to the server instance:
```js
fastify.decorate('conf', {
db: 'some.db',
port: 3000
})
```
To access decorated properties, use the name provided to the decoration API:
```js
fastify.utility()
console.log(fastify.conf.db)
```
The decorated [Fastify server](./Server.md) is bound to `this` in
[route](./Routes.md) handlers:
```js
fastify.decorate('db', new DbConnection())
fastify.get('/', async function (request, reply) {
// using return
return { hello: await this.db.query('world') }
// or
// using reply.send()
reply.send({ hello: await this.db.query('world') })
await reply
})
```
The `dependencies` parameter is an optional list of decorators that the
decorator being defined relies upon. This list contains the names of other
decorators. In the following example, the "utility" decorator depends on the
"greet" and "hi" decorators:
```js
async function greetDecorator (fastify, opts) {
fastify.decorate('greet', () => {
return 'greet message'
})
}
async function hiDecorator (fastify, opts) {
fastify.decorate('hi', () => {
return 'hi message'
})
}
async function utilityDecorator (fastify, opts) {
fastify.decorate('utility', () => {
return `${fastify.greet()} | ${fastify.hi()}`
})
}
fastify.register(fastifyPlugin(greetDecorator, { name: 'greet' }))
fastify.register(fastifyPlugin(hiDecorator, { name: 'hi' }))
fastify.register(fastifyPlugin(utilityDecorator, { dependencies: ['greet', 'hi'] }))
fastify.get('/', function (req, reply) {
// Response: {"hello":"greet message | hi message"}
reply.send({ hello: fastify.utility() })
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
})
```
Using an arrow function breaks the binding of `this` to
the `FastifyInstance`.
If a dependency is not satisfied, the `decorate` method throws an exception.
The dependency check occurs before the server instance boots, not during
runtime.
#### `decorateReply(name, value, [dependencies])`
<a id="decorate-reply"></a>
This API adds new methods/properties to the core `Reply` object:
```js
fastify.decorateReply('utility', function () {
// Something very useful
})
```
Using an arrow function will break the binding of `this` to the Fastify
`Reply` instance.
Using `decorateReply` will throw and error if used with a reference type:
```js
// Don't do this
fastify.decorateReply('foo', { bar: 'fizz'})
```
In this example, the object reference would be shared with all requests, and
**any mutation will impact all requests, potentially creating security
vulnerabilities or memory leaks**. Fastify blocks this.
To achieve proper encapsulation across requests configure a new value for each
incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest).
```js
const fp = require('fastify-plugin')
async function myPlugin (app) {
app.decorateReply('foo')
app.addHook('onRequest', async (req, reply) => {
reply.foo = { bar: 42 }
})
}
module.exports = fp(myPlugin)
```
See [`decorate`](#decorate) for information about the `dependencies` parameter.
#### `decorateRequest(name, value, [dependencies])`
<a id="decorate-request"></a>
As with [`decorateReply`](#decorate-reply), this API adds new methods/properties
to the core `Request` object:
```js
fastify.decorateRequest('utility', function () {
// something very useful
})
```
Using an arrow function will break the binding of `this` to the Fastify
`Request` instance.
Using `decorateRequest` will emit an error if used with a reference type:
```js
// Don't do this
fastify.decorateRequest('foo', { bar: 'fizz'})
```
In this example, the object reference would be shared with all requests, and
**any mutation will impact all requests, potentially creating security
vulnerabilities or memory leaks**. Fastify blocks this.
To achieve proper encapsulation across requests configure a new value for each
incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest).
Example:
```js
const fp = require('fastify-plugin')
async function myPlugin (app) {
app.decorateRequest('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
})
}
module.exports = fp(myPlugin)
```
The hook solution is more flexible and allows for more complex initialization
because more logic can be added to the `onRequest` hook.
Another approach is to use the getter/setter pattern, but it requires 2 decorators:
```js
fastify.decorateRequest('my_decorator_holder') // define the holder
fastify.decorateRequest('user', {
getter () {
this.my_decorator_holder ??= {} // initialize the holder
return this.my_decorator_holder
}
})
fastify.get('/', async function (req, reply) {
req.user.access = 'granted'
// other code
})
```
This ensures that the `user` property is always unique for each request.
See [`decorate`](#decorate) for information about the `dependencies` parameter.
#### `hasDecorator(name)`
<a id="has-decorator"></a>
Used to check for the existence of a server instance decoration:
```js
fastify.hasDecorator('utility')
```
#### hasRequestDecorator
<a id="has-request-decorator"></a>
Used to check for the existence of a Request decoration:
```js
fastify.hasRequestDecorator('utility')
```
#### hasReplyDecorator
<a id="has-reply-decorator"></a>
Used to check for the existence of a Reply decoration:
```js
fastify.hasReplyDecorator('utility')
```
### Decorators and Encapsulation
<a id="decorators-encapsulation"></a>
Defining a decorator (using `decorate`, `decorateRequest`, or `decorateReply`)
with the same name more than once in the same **encapsulated** context will
throw an exception. For example, the following will throw:
```js
const server = require('fastify')()
server.decorateReply('view', function (template, args) {
// Amazing view rendering engine
})
server.get('/', (req, reply) => {
reply.view('/index.html', { hello: 'world' })
})
// Somewhere else in our codebase, we define another
// view decorator. This throws.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})
server.listen({ port: 3000 })
```
But this will not:
```js
const server = require('fastify')()
server.decorateReply('view', function (template, args) {
// Amazing view rendering engine.
})
server.register(async function (server, opts) {
// We add a view decorator to the current encapsulated
// plugin. This will not throw as outside of this encapsulated
// plugin view is the old one, while inside it is the new one.
server.decorateReply('view', function (template, args) {
// Another rendering engine
})
server.get('/', (req, reply) => {
reply.view('/index.page', { hello: 'world' })
})
}, { prefix: '/bar' })
server.listen({ port: 3000 })
```
### Getters and Setters
<a id="getters-setters"></a>
Decorators accept special "getter/setter" objects with `getter` and optional
`setter` functions. This allows defining properties via decorators,
for example:
```js
fastify.decorate('foo', {
getter () {
return 'a getter'
}
})
```
Will define the `foo` property on the Fastify instance:
```js
console.log(fastify.foo) // 'a getter'
```
### `getDecorator<T>` API
Fastify's `getDecorator<T>` API retrieves an existing decorator from the
Fastify instance, `Request`, or `Reply`. If the decorator is not defined, an
`FST_ERR_DEC_UNDECLARED` error is thrown.
#### Use cases
**Early Plugin Dependency Validation**
`getDecorator<T>` on Fastify instance verifies that required decorators are
available at registration time.
For example:
```js
fastify.register(async function (fastify) {
const usersRepository = fastify.getDecorator('usersRepository')
fastify.get('/users', async function (request, reply) {
// We are sure `usersRepository` exists at runtime
return usersRepository.findAll()
})
})
```
**Handling Missing Decorators**
Directly accessing a decorator may lead to unexpected behavior if it is not declared:
```ts
const user = request.user;
if (user && user.isAdmin) {
// Execute admin tasks.
}
```
If `request.user` doesn't exist, then `user` will be set to `undefined`.
This makes it unclear whether the user is unauthenticated or the decorator is missing.
Using `getDecorator` enforces runtime safety:
```ts
// If the decorator is missing, an explicit `FST_ERR_DEC_UNDECLARED`
// error is thrown immediately.
const user = request.getDecorator('user');
if (user && user.isAdmin) {
// Execute admin tasks.
}
```
**Alternative to Module Augmentation**
Decorators are typically typed via module augmentation:
```ts
declare module 'fastify' {
interface FastifyInstance {
usersRepository: IUsersRepository
}
interface FastifyRequest {
session: ISession
}
interface FastifyReply {
sendSuccess: SendSuccessFn
}
}
```
This approach modifies the Fastify instance globally, which may lead to
conflicts and inconsistent behavior in multi-server setups or with plugin
encapsulation.
Using `getDecorator<T>` allows to limit types scope:
```ts
serverOne.register(async function (fastify) {
const usersRepository = fastify.getDecorator<PostgreUsersRepository>(
'usersRepository'
)
fastify.decorateRequest('session', null)
fastify.addHook('onRequest', async (req, reply) => {
// Yes, the request object has a setDecorator method.
// More information will be provided soon.
req.setDecorator('session', { user: 'Jean' })
})
fastify.get('/me', (request, reply) => {
const session = request.getDecorator<ISession>('session')
reply.send(session)
})
})
serverTwo.register(async function (fastify) {
const usersRepository = fastify.getDecorator<SqlLiteUsersRepository>(
'usersRepository'
)
fastify.decorateReply('sendSuccess', function (data) {
return this.send({ success: true })
})
fastify.get('/success', async (request, reply) => {
const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
await sendSuccess()
})
})
```
#### Bound functions inference
To save time, it's common to infer function types instead of
writing them manually:
```ts
function sendSuccess (this: FastifyReply) {
return this.send({ success: true })
}
export type SendSuccess = typeof sendSuccess
```
However, `getDecorator` returns functions with the `this`
context already **bound**, meaning the `this` parameter disappears
from the function signature.
To correctly type it, you should use `OmitThisParameter` utility:
```ts
function sendSuccess (this: FastifyReply) {
return this.send({ success: true })
}
type BoundSendSuccess = OmitThisParameter<typeof sendSuccess>
fastify.decorateReply('sendSuccess', sendSuccess)
fastify.get('/success', async (request, reply) => {
const sendSuccess = reply.getDecorator<BoundSendSuccess>('sendSuccess')
await sendSuccess()
})
```
### `Request.setDecorator<T>` Method
The `setDecorator<T>` method provides a safe and convenient way to
update the value of a `Request` decorator.
If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error
is thrown.
#### Use Cases
**Runtime Safety**
A typical way to set a `Request` decorator looks like this:
```ts
fastify.decorateRequest('user', '')
fastify.addHook('preHandler', async (req, reply) => {
req.user = 'Bob Dylan'
})
```
However, there is no guarantee that the decorator actually exists
unless you manually check beforehand.
Additionally, typos are common, e.g. `account`, `acount`, or `accout`.
By using `setDecorator`, you are always sure that the decorator exists:
```ts
fastify.decorateRequest('user', '')
fastify.addHook('preHandler', async (req, reply) => {
// Throws FST_ERR_DEC_UNDECLARED if the decorator does not exist
req.setDecorator('user-with-typo', 'Bob Dylan')
})
```
---
**Type Safety**
If the `FastifyRequest` interface does not declare the decorator, you
would typically need to use type assertions:
```ts
fastify.addHook('preHandler', async (req, reply) => {
(req as typeof req & { user: string }).user = 'Bob Dylan'
})
```
The `setDecorator<T>` method eliminates the need for explicit type
assertions while allowing type safety:
```ts
fastify.addHook('preHandler', async (req, reply) => {
req.setDecorator<string>('user', 'Bob Dylan')
})
```

190
node_modules/fastify/docs/Reference/Encapsulation.md generated vendored Normal file
View File

@@ -0,0 +1,190 @@
<h1 align="center">Fastify</h1>
## Encapsulation
<a id="encapsulation"></a>
A fundamental feature of Fastify is the "encapsulation context." It governs
which [decorators](./Decorators.md), registered [hooks](./Hooks.md), and
[plugins](./Plugins.md) are available to [routes](./Routes.md). A visual
representation of the encapsulation context is shown in the following figure:
![Figure 1](../resources/encapsulation_context.svg)
In the figure above, there are several entities:
1. The _root context_
2. Three _root plugins_
3. Two _child contexts_, each with:
* Two _child plugins_
* One _grandchild context_, each with:
- Three _child plugins_
Every _child context_ and _grandchild context_ has access to the _root plugins_.
Within each _child context_, the _grandchild contexts_ have access to the
_child plugins_ registered within the containing _child context_, but the
containing _child context_ **does not** have access to the _child plugins_
registered within its _grandchild context_.
Given that everything in Fastify is a [plugin](./Plugins.md) except for the
_root context_, every "context" and "plugin" in this example is a plugin
that can consist of decorators, hooks, plugins, and routes. To put this
example into concrete terms, consider a basic scenario of a REST API server
with three routes: the first route (`/one`) requires authentication, the
second route (`/two`) does not, and the third route (`/three`) has access to
the same context as the second route. Using [@fastify/bearer-auth][bearer] to
provide authentication, the code for this example is as follows:
```js
'use strict'
const fastify = require('fastify')()
fastify.decorateRequest('answer', 42)
fastify.register(async function authenticatedContext (childServer) {
childServer.register(require('@fastify/bearer-auth'), { keys: ['abc123'] })
childServer.route({
path: '/one',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
// request.foo will be undefined as it is only defined in publicContext
foo: request.foo,
// request.bar will be undefined as it is only defined in grandchildContext
bar: request.bar
})
}
})
})
fastify.register(async function publicContext (childServer) {
childServer.decorateRequest('foo', 'foo')
childServer.route({
path: '/two',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
// request.bar will be undefined as it is only defined in grandchildContext
bar: request.bar
})
}
})
childServer.register(async function grandchildContext (grandchildServer) {
grandchildServer.decorateRequest('bar', 'bar')
grandchildServer.route({
path: '/three',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
})
})
fastify.listen({ port: 8000 })
```
The server example above demonstrates the encapsulation concepts from the
original diagram:
1. Each _child context_ (`authenticatedContext`, `publicContext`, and
`grandchildContext`) has access to the `answer` request decorator defined in
the _root context_.
2. Only the `authenticatedContext` has access to the `@fastify/bearer-auth`
plugin.
3. Both the `publicContext` and `grandchildContext` have access to the `foo`
request decorator.
4. Only the `grandchildContext` has access to the `bar` request decorator.
To see this, start the server and issue requests:
```sh
# curl -H 'authorization: Bearer abc123' http://127.0.0.1:8000/one
{"answer":42}
# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}
```
[bearer]: https://github.com/fastify/fastify-bearer-auth
## Sharing Between Contexts
<a id="shared-context"></a>
Each context in the prior example inherits _only_ from its parent contexts. Parent
contexts cannot access entities within their descendant contexts. If needed,
encapsulation can be broken using [fastify-plugin][fastify-plugin], making
anything registered in a descendant context available to the parent context.
To allow `publicContext` access to the `bar` decorator in `grandchildContext`,
rewrite the code as follows:
```js
'use strict'
const fastify = require('fastify')()
const fastifyPlugin = require('fastify-plugin')
fastify.decorateRequest('answer', 42)
// `authenticatedContext` omitted for clarity
fastify.register(async function publicContext (childServer) {
childServer.decorateRequest('foo', 'foo')
childServer.route({
path: '/two',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
childServer.register(fastifyPlugin(grandchildContext))
async function grandchildContext (grandchildServer) {
grandchildServer.decorateRequest('bar', 'bar')
grandchildServer.route({
path: '/three',
method: 'GET',
handler (request, response) {
response.send({
answer: request.answer,
foo: request.foo,
bar: request.bar
})
}
})
}
})
fastify.listen({ port: 8000 })
```
Restarting the server and re-issuing the requests for `/two` and `/three`:
```sh
# curl http://127.0.0.1:8000/two
{"answer":42,"foo":"foo","bar":"bar"}
# curl http://127.0.0.1:8000/three
{"answer":42,"foo":"foo","bar":"bar"}
```
[fastify-plugin]: https://github.com/fastify/fastify-plugin

372
node_modules/fastify/docs/Reference/Errors.md generated vendored Normal file
View File

@@ -0,0 +1,372 @@
<h1 align="center">Fastify</h1>
## Errors
<a id="errors"></a>
**Table of contents**
- [Errors](#errors)
- [Error Handling In Node.js](#error-handling-in-nodejs)
- [Uncaught Errors](#uncaught-errors)
- [Catching Errors In Promises](#catching-errors-in-promises)
- [Errors In Fastify](#errors-in-fastify)
- [Errors In Input Data](#errors-in-input-data)
- [Catching Uncaught Errors In Fastify](#catching-uncaught-errors-in-fastify)
- [Errors In Fastify Lifecycle Hooks And A Custom Error Handler](#errors-in-fastify-lifecycle-hooks-and-a-custom-error-handler)
- [Fastify Error Codes](#fastify-error-codes)
- [FST_ERR_NOT_FOUND](#fst_err_not_found)
- [FST_ERR_OPTIONS_NOT_OBJ](#fst_err_options_not_obj)
- [FST_ERR_QSP_NOT_FN](#fst_err_qsp_not_fn)
- [FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN](#fst_err_schema_controller_bucket_opt_not_fn)
- [FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN](#fst_err_schema_error_formatter_not_fn)
- [FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ](#fst_err_ajv_custom_options_opt_not_obj)
- [FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR](#fst_err_ajv_custom_options_opt_not_arr)
- [FST_ERR_CTP_ALREADY_PRESENT](#fst_err_ctp_already_present)
- [FST_ERR_CTP_INVALID_TYPE](#fst_err_ctp_invalid_type)
- [FST_ERR_CTP_EMPTY_TYPE](#fst_err_ctp_empty_type)
- [FST_ERR_CTP_INVALID_HANDLER](#fst_err_ctp_invalid_handler)
- [FST_ERR_CTP_INVALID_PARSE_TYPE](#fst_err_ctp_invalid_parse_type)
- [FST_ERR_CTP_BODY_TOO_LARGE](#fst_err_ctp_body_too_large)
- [FST_ERR_CTP_INVALID_MEDIA_TYPE](#fst_err_ctp_invalid_media_type)
- [FST_ERR_CTP_INVALID_CONTENT_LENGTH](#fst_err_ctp_invalid_content_length)
- [FST_ERR_CTP_EMPTY_JSON_BODY](#fst_err_ctp_empty_json_body)
- [FST_ERR_CTP_INVALID_JSON_BODY](#fst_err_ctp_invalid_json_body)
- [FST_ERR_CTP_INSTANCE_ALREADY_STARTED](#fst_err_ctp_instance_already_started)
- [FST_ERR_INSTANCE_ALREADY_LISTENING](#fst_err_instance_already_listening)
- [FST_ERR_DEC_ALREADY_PRESENT](#fst_err_dec_already_present)
- [FST_ERR_DEC_DEPENDENCY_INVALID_TYPE](#fst_err_dec_dependency_invalid_type)
- [FST_ERR_DEC_MISSING_DEPENDENCY](#fst_err_dec_missing_dependency)
- [FST_ERR_DEC_AFTER_START](#fst_err_dec_after_start)
- [FST_ERR_DEC_REFERENCE_TYPE](#fst_err_dec_reference_type)
- [FST_ERR_DEC_UNDECLARED](#fst_err_dec_undeclared)
- [FST_ERR_HOOK_INVALID_TYPE](#fst_err_hook_invalid_type)
- [FST_ERR_HOOK_INVALID_HANDLER](#fst_err_hook_invalid_handler)
- [FST_ERR_HOOK_INVALID_ASYNC_HANDLER](#fst_err_hook_invalid_async_handler)
- [FST_ERR_HOOK_NOT_SUPPORTED](#fst_err_hook_not_supported)
- [FST_ERR_MISSING_MIDDLEWARE](#fst_err_missing_middleware)
- [FST_ERR_HOOK_TIMEOUT](#fst_err_hook_timeout)
- [FST_ERR_LOG_INVALID_DESTINATION](#fst_err_log_invalid_destination)
- [FST_ERR_LOG_INVALID_LOGGER](#fst_err_log_invalid_logger)
- [FST_ERR_LOG_INVALID_LOGGER_INSTANCE](#fst_err_log_invalid_logger_instance)
- [FST_ERR_LOG_INVALID_LOGGER_CONFIG](#fst_err_log_invalid_logger_config)
- [FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED](#fst_err_log_logger_and_logger_instance_provided)
- [FST_ERR_REP_INVALID_PAYLOAD_TYPE](#fst_err_rep_invalid_payload_type)
- [FST_ERR_REP_RESPONSE_BODY_CONSUMED](#fst_err_rep_response_body_consumed)
- [FST_ERR_REP_READABLE_STREAM_LOCKED](#fst_err_rep_readable_stream_locked)
- [FST_ERR_REP_ALREADY_SENT](#fst_err_rep_already_sent)
- [FST_ERR_REP_SENT_VALUE](#fst_err_rep_sent_value)
- [FST_ERR_SEND_INSIDE_ONERR](#fst_err_send_inside_onerr)
- [FST_ERR_SEND_UNDEFINED_ERR](#fst_err_send_undefined_err)
- [FST_ERR_BAD_STATUS_CODE](#fst_err_bad_status_code)
- [FST_ERR_BAD_TRAILER_NAME](#fst_err_bad_trailer_name)
- [FST_ERR_BAD_TRAILER_VALUE](#fst_err_bad_trailer_value)
- [FST_ERR_FAILED_ERROR_SERIALIZATION](#fst_err_failed_error_serialization)
- [FST_ERR_MISSING_SERIALIZATION_FN](#fst_err_missing_serialization_fn)
- [FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN](#fst_err_missing_contenttype_serialization_fn)
- [FST_ERR_REQ_INVALID_VALIDATION_INVOCATION](#fst_err_req_invalid_validation_invocation)
- [FST_ERR_SCH_MISSING_ID](#fst_err_sch_missing_id)
- [FST_ERR_SCH_ALREADY_PRESENT](#fst_err_sch_already_present)
- [FST_ERR_SCH_CONTENT_MISSING_SCHEMA](#fst_err_sch_content_missing_schema)
- [FST_ERR_SCH_DUPLICATE](#fst_err_sch_duplicate)
- [FST_ERR_SCH_VALIDATION_BUILD](#fst_err_sch_validation_build)
- [FST_ERR_SCH_SERIALIZATION_BUILD](#fst_err_sch_serialization_build)
- [FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX](#fst_err_sch_response_schema_not_nested_2xx)
- [FST_ERR_INIT_OPTS_INVALID](#fst_err_init_opts_invalid)
- [FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE](#fst_err_force_close_connections_idle_not_available)
- [FST_ERR_DUPLICATED_ROUTE](#fst_err_duplicated_route)
- [FST_ERR_BAD_URL](#fst_err_bad_url)
- [FST_ERR_ASYNC_CONSTRAINT](#fst_err_async_constraint)
- [FST_ERR_INVALID_URL](#fst_err_invalid_url)
- [FST_ERR_ROUTE_OPTIONS_NOT_OBJ](#fst_err_route_options_not_obj)
- [FST_ERR_ROUTE_DUPLICATED_HANDLER](#fst_err_route_duplicated_handler)
- [FST_ERR_ROUTE_HANDLER_NOT_FN](#fst_err_route_handler_not_fn)
- [FST_ERR_ROUTE_MISSING_HANDLER](#fst_err_route_missing_handler)
- [FST_ERR_ROUTE_METHOD_INVALID](#fst_err_route_method_invalid)
- [FST_ERR_ROUTE_METHOD_NOT_SUPPORTED](#fst_err_route_method_not_supported)
- [FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED](#fst_err_route_body_validation_schema_not_supported)
- [FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT](#fst_err_route_body_limit_option_not_int)
- [FST_ERR_ROUTE_REWRITE_NOT_STR](#fst_err_route_rewrite_not_str)
- [FST_ERR_REOPENED_CLOSE_SERVER](#fst_err_reopened_close_server)
- [FST_ERR_REOPENED_SERVER](#fst_err_reopened_server)
- [FST_ERR_PLUGIN_VERSION_MISMATCH](#fst_err_plugin_version_mismatch)
- [FST_ERR_PLUGIN_CALLBACK_NOT_FN](#fst_err_plugin_callback_not_fn)
- [FST_ERR_PLUGIN_NOT_VALID](#fst_err_plugin_not_valid)
- [FST_ERR_ROOT_PLG_BOOTED](#fst_err_root_plg_booted)
- [FST_ERR_PARENT_PLUGIN_BOOTED](#fst_err_parent_plugin_booted)
- [FST_ERR_PLUGIN_TIMEOUT](#fst_err_plugin_timeout)
- [FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE](#fst_err_plugin_not_present_in_instance)
- [FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER](#fst_err_plugin_invalid_async_handler)
- [FST_ERR_VALIDATION](#fst_err_validation)
- [FST_ERR_LISTEN_OPTIONS_INVALID](#fst_err_listen_options_invalid)
- [FST_ERR_ERROR_HANDLER_NOT_FN](#fst_err_error_handler_not_fn)
- [FST_ERR_ERROR_HANDLER_ALREADY_SET](#fst_err_error_handler_already_set)
### Error Handling In Node.js
<a id="error-handling"></a>
#### Uncaught Errors
In Node.js, uncaught errors can cause memory leaks, file descriptor leaks, and
other major production issues.
[Domains](https://nodejs.org/en/docs/guides/domain-postmortem/) were a failed
attempt to fix this.
Given that it is not possible to process all uncaught errors sensibly, the best
way to deal with them is to
[crash](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly).
#### Catching Errors In Promises
When using promises, attach a `.catch()` handler synchronously.
### Errors In Fastify
Fastify follows an all-or-nothing approach and aims to be lean and optimal. The
developer is responsible for ensuring errors are handled properly.
#### Errors In Input Data
Most errors result from unexpected input data, so it is recommended to
[validate input data against a JSON schema](./Validation-and-Serialization.md).
#### Catching Uncaught Errors In Fastify
Fastify tries to catch as many uncaught errors as possible without hindering
performance. This includes:
1. synchronous routes, e.g. `app.get('/', () => { throw new Error('kaboom') })`
2. `async` routes, e.g. `app.get('/', async () => { throw new Error('kaboom')
})`
In both cases, the error will be caught safely and routed to Fastify's default
error handler, resulting in a generic `500 Internal Server Error` response.
To customize this behavior, use
[`setErrorHandler`](./Server.md#seterrorhandler).
### Errors In Fastify Lifecycle Hooks And A Custom Error Handler
From the [Hooks documentation](./Hooks.md#manage-errors-from-a-hook):
> If you get an error during the execution of your hook, just pass it to
> `done()` and Fastify will automatically close the request and send the
> appropriate error code to the user.
When a custom error handler is defined through
[`setErrorHandler`](./Server.md#seterrorhandler), it will receive the error
passed to the `done()` callback or through other supported automatic error
handling mechanisms. If `setErrorHandler` is used multiple times, the error will
be routed to the most precedent handler within the error
[encapsulation context](./Encapsulation.md). Error handlers are fully
encapsulated, so a `setErrorHandler` call within a plugin will limit the error
handler to that plugin's context.
The root error handler is Fastify's generic error handler. This error handler
will use the headers and status code in the `Error` object, if they exist. The
headers and status code will not be automatically set if a custom error handler
is provided.
The following should be considered when using a custom error handler:
- `reply.send(data)` behaves as in [regular route handlers](./Reply.md#senddata)
- objects are serialized, triggering the `preSerialization` lifecycle hook if
defined
- strings, buffers, and streams are sent to the client with appropriate headers
(no serialization)
- Throwing a new error in a custom error handler will call the parent
`errorHandler`.
- The `onError` hook will be triggered once for the first error thrown
- An error will not be triggered twice from a lifecycle hook. Fastify
internally monitors error invocation to avoid infinite loops for errors
thrown in the reply phases of the lifecycle (those after the route handler)
When using Fastify's custom error handling through
[`setErrorHandler`](./Server.md#seterrorhandler), be aware of how errors are
propagated between custom and default error handlers.
If a plugin's error handler re-throws an error that is not an instance of
[Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error),
it will not propagate to the parent context error handler. Instead, it will be
caught by the default error handler. This can be seen in the `/bad` route of the
example below.
To ensure consistent error handling, throw instances of `Error`. For example,
replace `throw 'foo'` with `throw new Error('foo')` in the `/bad` route to
ensure errors propagate through the custom error handling chain as intended.
This practice helps avoid potential pitfalls when working with custom error
handling in Fastify.
For example:
```js
const Fastify = require('fastify')
// Instantiate the framework
const fastify = Fastify({
logger: true
})
// Register parent error handler
fastify.setErrorHandler((error, request, reply) => {
reply.status(500).send({ ok: false })
})
fastify.register((app, options, next) => {
// Register child error handler
fastify.setErrorHandler((error, request, reply) => {
throw error
})
fastify.get('/bad', async () => {
// Throws a non-Error type, 'bar'
throw 'foo'
})
fastify.get('/good', async () => {
// Throws an Error instance, 'bar'
throw new Error('bar')
})
next()
})
// Run the server
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is listening at ${address}
})
```
### Fastify Error Codes
<a id="fastify-error-codes"></a>
You can access `errorCodes` for mapping:
```js
// ESM
import { errorCodes } from 'fastify'
// CommonJS
const errorCodes = require('fastify').errorCodes
```
For example:
```js
const Fastify = require('fastify')
// Instantiate the framework
const fastify = Fastify({
logger: true
})
// Declare a route
fastify.get('/', function (request, reply) {
reply.code('bad status code').send({ hello: 'world' })
})
fastify.setErrorHandler(function (error, request, reply) {
if (error instanceof Fastify.errorCodes.FST_ERR_BAD_STATUS_CODE) {
// Log error
this.log.error(error)
// Send error response
reply.status(500).send({ ok: false })
} else {
// Fastify will use parent error handler to handle this
reply.send(error)
}
})
// Run the server!
fastify.listen({ port: 3000 }, function (err, address) {
if (err) {
fastify.log.error(err)
process.exit(1)
}
// Server is now listening on ${address}
})
```
Below is a table with all the error codes used by Fastify.
| Code | Description | How to solve | Discussion |
|------|-------------|--------------|------------|
| <a id="fst_err_not_found">FST_ERR_NOT_FOUND</a> | 404 Not Found | - | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_options_not_obj">FST_ERR_OPTIONS_NOT_OBJ</a> | Fastify options wrongly specified. | Fastify options should be an object. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_qsp_not_fn">FST_ERR_QSP_NOT_FN</a> | QueryStringParser wrongly specified. | QueryStringParser option should be a function. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_schema_controller_bucket_opt_not_fn">FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN</a> | SchemaController.bucket wrongly specified. | SchemaController.bucket option should be a function. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_schema_error_formatter_not_fn">FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN</a> | SchemaErrorFormatter option wrongly specified. | SchemaErrorFormatter option should be a non async function. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_ajv_custom_options_opt_not_obj">FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ</a> | ajv.customOptions wrongly specified. | ajv.customOptions option should be an object. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_ajv_custom_options_opt_not_arr">FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR</a> | ajv.plugins option wrongly specified. | ajv.plugins option should be an array. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_ctp_already_present">FST_ERR_CTP_ALREADY_PRESENT</a> | The parser for this content type was already registered. | Use a different content type or delete the already registered parser. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_invalid_type">FST_ERR_CTP_INVALID_TYPE</a> | `Content-Type` wrongly specified | The `Content-Type` should be a string. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_empty_type">FST_ERR_CTP_EMPTY_TYPE</a> | `Content-Type` is an empty string. | `Content-Type` cannot be an empty string. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_invalid_handler">FST_ERR_CTP_INVALID_HANDLER</a> | Invalid handler for the content type. | Use a different handler. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_invalid_parse_type">FST_ERR_CTP_INVALID_PARSE_TYPE</a> | The provided parse type is not supported. | Accepted values are <code>string</code> or <code>buffer</code>. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_body_too_large">FST_ERR_CTP_BODY_TOO_LARGE</a> | The request body is larger than the provided limit. | Increase the limit in the Fastify server instance setting: [bodyLimit](./Server.md#bodylimit) | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_invalid_media_type">FST_ERR_CTP_INVALID_MEDIA_TYPE</a> | The received media type is not supported (i.e. there is no suitable `Content-Type` parser for it). | Use a different content type. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_invalid_content_length">FST_ERR_CTP_INVALID_CONTENT_LENGTH</a> | Request body size did not match <code>Content-Length</code>. | Check the request body size and the <code>Content-Length</code> header. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_ctp_empty_json_body">FST_ERR_CTP_EMPTY_JSON_BODY</a> | Body is not valid JSON but content-type is set to <code>application/json</code>. | Check if the request body is valid JSON. | [#5925](https://github.com/fastify/fastify/pull/5925) |
| <a id="fst_err_ctp_invalid_json_body">FST_ERR_CTP_INVALID_JSON_BODY</a> | Body cannot be empty when content-type is set to <code>application/json</code>. | Check the request body. | [#1253](https://github.com/fastify/fastify/pull/1253) |
| <a id="fst_err_ctp_instance_already_started">FST_ERR_CTP_INSTANCE_ALREADY_STARTED</a> | Fastify is already started. | - | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_instance_already_listening">FST_ERR_INSTANCE_ALREADY_LISTENING</a> | Fastify instance is already listening. | - | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_dec_already_present">FST_ERR_DEC_ALREADY_PRESENT</a> | A decorator with the same name is already registered. | Use a different decorator name. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_dec_dependency_invalid_type">FST_ERR_DEC_DEPENDENCY_INVALID_TYPE</a> | The dependencies of decorator must be of type `Array`. | Use an array for the dependencies. | [#3090](https://github.com/fastify/fastify/pull/3090) |
| <a id="fst_err_dec_missing_dependency">FST_ERR_DEC_MISSING_DEPENDENCY</a> | The decorator cannot be registered due to a missing dependency. | Register the missing dependency. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_dec_after_start">FST_ERR_DEC_AFTER_START</a> | The decorator cannot be added after start. | Add the decorator before starting the server. | [#2128](https://github.com/fastify/fastify/pull/2128) |
| <a id="fst_err_dec_reference_type">FST_ERR_DEC_REFERENCE_TYPE</a> | The decorator cannot be a reference type. | Define the decorator with a getter/setter interface or an empty decorator with a hook. | [#5462](https://github.com/fastify/fastify/pull/5462) |
| <a id="fst_err_dec_undeclared">FST_ERR_DEC_UNDECLARED</a> | An attempt was made to access a decorator that has not been declared. | Declare the decorator before using it. | [#](https://github.com/fastify/fastify/pull/)
| <a id="fst_err_hook_invalid_type">FST_ERR_HOOK_INVALID_TYPE</a> | The hook name must be a string. | Use a string for the hook name. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_hook_invalid_handler">FST_ERR_HOOK_INVALID_HANDLER</a> | The hook callback must be a function. | Use a function for the hook callback. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_hook_invalid_async_handler">FST_ERR_HOOK_INVALID_ASYNC_HANDLER</a> | Async function has too many arguments. Async hooks should not use the `done` argument. | Remove the `done` argument from the async hook. | [#4367](https://github.com/fastify/fastify/pull/4367) |
| <a id="fst_err_hook_not_supported">FST_ERR_HOOK_NOT_SUPPORTED</a> | The hook is not supported. | Use a supported hook. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_missing_middleware">FST_ERR_MISSING_MIDDLEWARE</a> | You must register a plugin for handling middlewares, visit [`Middleware`](./Middleware.md) for more info. | Register a plugin for handling middlewares. | [#2014](https://github.com/fastify/fastify/pull/2014) |
| <a id="fst_err_hook_timeout">FST_ERR_HOOK_TIMEOUT</a> | A callback for a hook timed out. | Increase the timeout for the hook. | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_log_invalid_destination">FST_ERR_LOG_INVALID_DESTINATION</a> | The logger does not accept the specified destination. | Use a `'stream'` or a `'file'` as the destination. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_log_invalid_logger">FST_ERR_LOG_INVALID_LOGGER</a> | The logger should have all these methods: `'info'`, `'error'`, `'debug'`, `'fatal'`, `'warn'`, `'trace'`, `'child'`. | Use a logger with all the required methods. | [#4520](https://github.com/fastify/fastify/pull/4520) |
| <a id="fst_err_log_invalid_logger_instance">FST_ERR_LOG_INVALID_LOGGER_INSTANCE</a> | The `loggerInstance` only accepts a logger instance, not a configuration object. | To pass a configuration object, use `'logger'` instead. | [#5020](https://github.com/fastify/fastify/pull/5020) |
| <a id="fst_err_log_invalid_logger_config">FST_ERR_LOG_INVALID_LOGGER_CONFIG</a> | The logger option only accepts a configuration object, not a logger instance. | To pass an instance, use `'loggerInstance'` instead. | [#5020](https://github.com/fastify/fastify/pull/5020) |
| <a id="fst_err_log_logger_and_logger_instance_provided">FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED</a> | You cannot provide both `'logger'` and `'loggerInstance'`. | Please provide only one option. | [#5020](https://github.com/fastify/fastify/pull/5020) |
| <a id="fst_err_rep_invalid_payload_type">FST_ERR_REP_INVALID_PAYLOAD_TYPE</a> | Reply payload can be either a `string` or a `Buffer`. | Use a `string` or a `Buffer` for the payload. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_rep_response_body_consumed">FST_ERR_REP_RESPONSE_BODY_CONSUMED</a> | Using `Response` as reply payload, but the body is being consumed. | Make sure you don't consume the `Response.body` | [#5286](https://github.com/fastify/fastify/pull/5286) |
| <a id="fst_err_rep_readable_stream_locked">FST_ERR_REP_READABLE_STREAM_LOCKED</a> | Using `ReadableStream` as reply payload, but locked with another reader. | Make sure you don't call the `Readable.getReader` before sending or release lock with `reader.releaseLock()` before sending. | [#5920](https://github.com/fastify/fastify/pull/5920) |
| <a id="fst_err_rep_already_sent">FST_ERR_REP_ALREADY_SENT</a> | A response was already sent. | - | [#1336](https://github.com/fastify/fastify/pull/1336) |
| <a id="fst_err_rep_sent_value">FST_ERR_REP_SENT_VALUE</a> | The only possible value for `reply.sent` is `true`. | - | [#1336](https://github.com/fastify/fastify/pull/1336) |
| <a id="fst_err_send_inside_onerr">FST_ERR_SEND_INSIDE_ONERR</a> | You cannot use `send` inside the `onError` hook. | - | [#1348](https://github.com/fastify/fastify/pull/1348) |
| <a id="fst_err_send_undefined_err">FST_ERR_SEND_UNDEFINED_ERR</a> | Undefined error has occurred. | - | [#2074](https://github.com/fastify/fastify/pull/2074) |
| <a id="fst_err_bad_status_code">FST_ERR_BAD_STATUS_CODE</a> | The status code is not valid. | Use a valid status code. | [#2082](https://github.com/fastify/fastify/pull/2082) |
| <a id="fst_err_bad_trailer_name">FST_ERR_BAD_TRAILER_NAME</a> | Called `reply.trailer` with an invalid header name. | Use a valid header name. | [#3794](https://github.com/fastify/fastify/pull/3794) |
| <a id="fst_err_bad_trailer_value">FST_ERR_BAD_TRAILER_VALUE</a> | Called `reply.trailer` with an invalid type. Expected a function. | Use a function. | [#3794](https://github.com/fastify/fastify/pull/3794) |
| <a id="fst_err_failed_error_serialization">FST_ERR_FAILED_ERROR_SERIALIZATION</a> | Failed to serialize an error. | - | [#4601](https://github.com/fastify/fastify/pull/4601) |
| <a id="fst_err_missing_serialization_fn">FST_ERR_MISSING_SERIALIZATION_FN</a> | Missing serialization function. | Add a serialization function. | [#3970](https://github.com/fastify/fastify/pull/3970) |
| <a id="fst_err_missing_contenttype_serialization_fn">FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN</a> | Missing `Content-Type` serialization function. | Add a serialization function. | [#4264](https://github.com/fastify/fastify/pull/4264) |
| <a id="fst_err_req_invalid_validation_invocation">FST_ERR_REQ_INVALID_VALIDATION_INVOCATION</a> | Invalid validation invocation. Missing validation function for HTTP part nor schema provided. | Add a validation function. | [#3970](https://github.com/fastify/fastify/pull/3970) |
| <a id="fst_err_sch_missing_id">FST_ERR_SCH_MISSING_ID</a> | The schema provided does not have `$id` property. | Add a `$id` property. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_sch_already_present">FST_ERR_SCH_ALREADY_PRESENT</a> | A schema with the same `$id` already exists. | Use a different `$id`. | [#1168](https://github.com/fastify/fastify/pull/1168) |
| <a id="fst_err_sch_content_missing_schema">FST_ERR_SCH_CONTENT_MISSING_SCHEMA</a> | A schema is missing for the corresponding content type. | Add a schema. | [#4264](https://github.com/fastify/fastify/pull/4264) |
| <a id="fst_err_sch_duplicate">FST_ERR_SCH_DUPLICATE</a> | Schema with the same attribute already present! | Use a different attribute. | [#1954](https://github.com/fastify/fastify/pull/1954) |
| <a id="fst_err_sch_validation_build">FST_ERR_SCH_VALIDATION_BUILD</a> | The JSON schema provided for validation to a route is not valid. | Fix the JSON schema. | [#2023](https://github.com/fastify/fastify/pull/2023) |
| <a id="fst_err_sch_serialization_build">FST_ERR_SCH_SERIALIZATION_BUILD</a> | The JSON schema provided for serialization of a route response is not valid. | Fix the JSON schema. | [#2023](https://github.com/fastify/fastify/pull/2023) |
| <a id="fst_err_sch_response_schema_not_nested_2xx">FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX</a> | Response schemas should be nested under a valid status code (2XX). | Use a valid status code. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_init_opts_invalid">FST_ERR_INIT_OPTS_INVALID</a> | Invalid initialization options. | Use valid initialization options. | [#1471](https://github.com/fastify/fastify/pull/1471) |
| <a id="fst_err_force_close_connections_idle_not_available">FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE</a> | Cannot set forceCloseConnections to `idle` as your HTTP server does not support `closeIdleConnections` method. | Use a different value for `forceCloseConnections`. | [#3925](https://github.com/fastify/fastify/pull/3925) |
| <a id="fst_err_duplicated_route">FST_ERR_DUPLICATED_ROUTE</a> | The HTTP method already has a registered controller for that URL. | Use a different URL or register the controller for another HTTP method. | [#2954](https://github.com/fastify/fastify/pull/2954) |
| <a id="fst_err_bad_url">FST_ERR_BAD_URL</a> | The router received an invalid URL. | Use a valid URL. | [#2106](https://github.com/fastify/fastify/pull/2106) |
| <a id="fst_err_async_constraint">FST_ERR_ASYNC_CONSTRAINT</a> | The router received an error when using asynchronous constraints. | - | [#4323](https://github.com/fastify/fastify/pull/4323) |
| <a id="fst_err_invalid_url">FST_ERR_INVALID_URL</a> | URL must be a string. | Use a string for the URL. | [#3653](https://github.com/fastify/fastify/pull/3653) |
| <a id="fst_err_route_options_not_obj">FST_ERR_ROUTE_OPTIONS_NOT_OBJ</a> | Options for the route must be an object. | Use an object for the route options. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_duplicated_handler">FST_ERR_ROUTE_DUPLICATED_HANDLER</a> | Duplicate handler for the route is not allowed. | Use a different handler. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_handler_not_fn">FST_ERR_ROUTE_HANDLER_NOT_FN</a> | Handler for the route must be a function. | Use a function for the handler. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_missing_handler">FST_ERR_ROUTE_MISSING_HANDLER</a> | Missing handler function for the route. | Add a handler function. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_method_invalid">FST_ERR_ROUTE_METHOD_INVALID</a> | Method is not a valid value. | Use a valid value for the method. | [#4750](https://github.com/fastify/fastify/pull/4750) |
| <a id="fst_err_route_method_not_supported">FST_ERR_ROUTE_METHOD_NOT_SUPPORTED</a> | Method is not supported for the route. | Use a supported method. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_body_validation_schema_not_supported">FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED</a> | Body validation schema route is not supported. | Use a different different method for the route. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_body_limit_option_not_int">FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT</a> | `bodyLimit` option must be an integer. | Use an integer for the `bodyLimit` option. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_route_rewrite_not_str">FST_ERR_ROUTE_REWRITE_NOT_STR</a> | `rewriteUrl` needs to be of type `string`. | Use a string for the `rewriteUrl`. | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_reopened_close_server">FST_ERR_REOPENED_CLOSE_SERVER</a> | Fastify has already been closed and cannot be reopened. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
| <a id="fst_err_reopened_server">FST_ERR_REOPENED_SERVER</a> | Fastify is already listening. | - | [#2415](https://github.com/fastify/fastify/pull/2415) |
| <a id="fst_err_plugin_version_mismatch">FST_ERR_PLUGIN_VERSION_MISMATCH</a> | Installed Fastify plugin mismatched expected version. | Use a compatible version of the plugin. | [#2549](https://github.com/fastify/fastify/pull/2549) |
| <a id="fst_err_plugin_callback_not_fn">FST_ERR_PLUGIN_CALLBACK_NOT_FN</a> | Callback for a hook is not a function. | Use a function for the callback. | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_plugin_not_valid">FST_ERR_PLUGIN_NOT_VALID</a> | Plugin must be a function or a promise. | Use a function or a promise for the plugin. | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_root_plg_booted">FST_ERR_ROOT_PLG_BOOTED</a> | Root plugin has already booted. | - | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_parent_plugin_booted">FST_ERR_PARENT_PLUGIN_BOOTED</a> | Impossible to load plugin because the parent (mapped directly from `avvio`) | - | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_plugin_timeout">FST_ERR_PLUGIN_TIMEOUT</a> | Plugin did not start in time. | Increase the timeout for the plugin. | [#3106](https://github.com/fastify/fastify/pull/3106) |
| <a id="fst_err_plugin_not_present_in_instance">FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE</a> | The decorator is not present in the instance. | - | [#4554](https://github.com/fastify/fastify/pull/4554) |
| <a id="fst_err_plugin_invalid_async_handler">FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER</a> | The plugin being registered mixes async and callback styles. | - | [#5141](https://github.com/fastify/fastify/pull/5141) |
| <a id="fst_err_validation">FST_ERR_VALIDATION</a> | The Request failed the payload validation. | Check the request payload. | [#4824](https://github.com/fastify/fastify/pull/4824) |
| <a id="fst_err_listen_options_invalid">FST_ERR_LISTEN_OPTIONS_INVALID</a> | Invalid listen options. | Check the listen options. | [#4886](https://github.com/fastify/fastify/pull/4886) |
| <a id="fst_err_error_handler_not_fn">FST_ERR_ERROR_HANDLER_NOT_FN</a> | Error Handler must be a function | Provide a function to `setErrorHandler`. | [#5317](https://github.com/fastify/fastify/pull/5317) | <a id="fst_err_error_handler_already_set">FST_ERR_ERROR_HANDLER_ALREADY_SET</a> | Error Handler already set in this scope. Set `allowErrorHandlerOverride: true` to allow overriding. | By default, `setErrorHandler` can only be called once per encapsulation context. | [#6097](https://github.com/fastify/fastify/pull/6098) |

94
node_modules/fastify/docs/Reference/HTTP2.md generated vendored Normal file
View File

@@ -0,0 +1,94 @@
<h1 align="center">Fastify</h1>
## HTTP2
_Fastify_ supports HTTP2 over HTTPS (h2) or plaintext (h2c).
Currently, none of the HTTP2-specific APIs are available through _Fastify_, but
Node's `req` and `res` can be accessed through the `Request` and `Reply`
interfaces. PRs are welcome.
### Secure (HTTPS)
HTTP2 is supported in all modern browsers __only over a secure connection__:
```js
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const fastify = require('fastify')({
http2: true,
https: {
key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
}
})
fastify.get('/', function (request, reply) {
reply.code(200).send({ hello: 'world' })
})
fastify.listen({ port: 3000 })
```
[ALPN negotiation](https://datatracker.ietf.org/doc/html/rfc7301) allows
support for both HTTPS and HTTP/2 over the same socket.
Node core `req` and `res` objects can be either
[HTTP/1](https://nodejs.org/api/http.html) or
[HTTP/2](https://nodejs.org/api/http2.html). _Fastify_ supports this out of the
box:
```js
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const fastify = require('fastify')({
http2: true,
https: {
allowHTTP1: true, // fallback support for HTTP1
key: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.key')),
cert: fs.readFileSync(path.join(__dirname, '..', 'https', 'fastify.cert'))
}
})
// this route can be accessed through both protocols
fastify.get('/', function (request, reply) {
reply.code(200).send({ hello: 'world' })
})
fastify.listen({ port: 3000 })
```
Test the new server with:
```
$ npx h2url https://localhost:3000
```
### Plain or insecure
For microservices, HTTP2 can connect in plain text, but this is not
supported by browsers.
```js
'use strict'
const fastify = require('fastify')({
http2: true
})
fastify.get('/', function (request, reply) {
reply.code(200).send({ hello: 'world' })
})
fastify.listen({ port: 3000 })
```
Test the new server with:
```
$ npx h2url http://localhost:3000
```

896
node_modules/fastify/docs/Reference/Hooks.md generated vendored Normal file
View File

@@ -0,0 +1,896 @@
<h1 align="center">Fastify</h1>
## Hooks
Hooks are registered with the `fastify.addHook` method and allow you to listen
to specific events in the application or request/response lifecycle. You have to
register a hook before the event is triggered, otherwise, the event is lost.
By using hooks you can interact directly with the lifecycle of Fastify. There
are Request/Reply hooks and application hooks:
- [Request/Reply Hooks](#requestreply-hooks)
- [onRequest](#onrequest)
- [preParsing](#preparsing)
- [preValidation](#prevalidation)
- [preHandler](#prehandler)
- [preSerialization](#preserialization)
- [onError](#onerror)
- [onSend](#onsend)
- [onResponse](#onresponse)
- [onTimeout](#ontimeout)
- [onRequestAbort](#onrequestabort)
- [Manage Errors from a hook](#manage-errors-from-a-hook)
- [Respond to a request from a hook](#respond-to-a-request-from-a-hook)
- [Application Hooks](#application-hooks)
- [onReady](#onready)
- [onListen](#onlisten)
- [onClose](#onclose)
- [preClose](#preclose)
- [onRoute](#onroute)
- [onRegister](#onregister)
- [Scope](#scope)
- [Route level hooks](#route-level-hooks)
- [Using Hooks to Inject Custom Properties](#using-hooks-to-inject-custom-properties)
- [Diagnostics Channel Hooks](#diagnostics-channel-hooks)
> Note: The `done` callback is not available when using `async`/`await` or
> returning a `Promise`. If you do invoke a `done` callback in this situation
> unexpected behavior may occur, e.g. duplicate invocation of handlers.
## Request/Reply Hooks
[Request](./Request.md) and [Reply](./Reply.md) are the core Fastify objects.
`done` is the function to continue with the [lifecycle](./Lifecycle.md).
It is easy to understand where each hook is executed by looking at the
[lifecycle page](./Lifecycle.md).
Hooks are affected by Fastify's encapsulation, and can thus be applied to
selected routes. See the [Scopes](#scope) section for more information.
There are eight different hooks that you can use in Request/Reply *(in order of
execution)*:
### onRequest
```js
fastify.addHook('onRequest', (request, reply, done) => {
// Some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('onRequest', async (request, reply) => {
// Some code
await asyncMethod()
})
```
> Note: In the [onRequest](#onrequest) hook, `request.body` will always be
> `undefined`, because the body parsing happens before the
> [preValidation](#prevalidation) hook.
### preParsing
If you are using the `preParsing` hook, you can transform the request payload
stream before it is parsed. It receives the request and reply objects as other
hooks, and a stream with the current request payload.
If it returns a value (via `return` or via the callback function), it must
return a stream.
For instance, you can decompress the request body:
```js
fastify.addHook('preParsing', (request, reply, payload, done) => {
// Some code
done(null, newPayload)
})
```
Or `async/await`:
```js
fastify.addHook('preParsing', async (request, reply, payload) => {
// Some code
await asyncMethod()
return newPayload
})
```
> Note: In the [preParsing](#preparsing) hook, `request.body` will always be
> `undefined`, because the body parsing happens before the
> [preValidation](#prevalidation) hook.
> Note: You should also add a `receivedEncodedLength` property to the
> returned stream. This property is used to correctly match the request payload
> with the `Content-Length` header value. Ideally, this property should be updated
> on each received chunk.
> Note: The size of the returned stream is checked to not exceed the limit
> set in [`bodyLimit`](./Server.md#bodylimit) option.
### preValidation
If you are using the `preValidation` hook, you can change the payload before it
is validated. For example:
```js
fastify.addHook('preValidation', (request, reply, done) => {
request.body = { ...request.body, importantKey: 'randomString' }
done()
})
```
Or `async/await`:
```js
fastify.addHook('preValidation', async (request, reply) => {
const importantKey = await generateRandomString()
request.body = { ...request.body, importantKey }
})
```
### preHandler
The `preHandler` hook allows you to specify a function that is executed before
a routes's handler.
```js
fastify.addHook('preHandler', (request, reply, done) => {
// some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('preHandler', async (request, reply) => {
// Some code
await asyncMethod()
})
```
### preSerialization
If you are using the `preSerialization` hook, you can change (or replace) the
payload before it is serialized. For example:
```js
fastify.addHook('preSerialization', (request, reply, payload, done) => {
const err = null
const newPayload = { wrapped: payload }
done(err, newPayload)
})
```
Or `async/await`:
```js
fastify.addHook('preSerialization', async (request, reply, payload) => {
return { wrapped: payload }
})
```
> Note: The hook is NOT called if the payload is a `string`, a `Buffer`, a
> `stream`, or `null`.
### onError
```js
fastify.addHook('onError', (request, reply, error, done) => {
// Some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('onError', async (request, reply, error) => {
// Useful for custom error logging
// You should not use this hook to update the error
})
```
This hook is useful if you need to do some custom error logging or add some
specific header in case of error.
It is not intended for changing the error, and calling `reply.send` will throw
an exception.
This hook will be executed before
the [Custom Error Handler set by `setErrorHandler`](./Server.md#seterrorhandler).
> Note: Unlike the other hooks, passing an error to the `done` function is not
> supported.
### onSend
If you are using the `onSend` hook, you can change the payload. For example:
```js
fastify.addHook('onSend', (request, reply, payload, done) => {
const err = null;
const newPayload = payload.replace('some-text', 'some-new-text')
done(err, newPayload)
})
```
Or `async/await`:
```js
fastify.addHook('onSend', async (request, reply, payload) => {
const newPayload = payload.replace('some-text', 'some-new-text')
return newPayload
})
```
You can also clear the payload to send a response with an empty body by
replacing the payload with `null`:
```js
fastify.addHook('onSend', (request, reply, payload, done) => {
reply.code(304)
const newPayload = null
done(null, newPayload)
})
```
> You can also send an empty body by replacing the payload with the empty string
> `''`, but be aware that this will cause the `Content-Length` header to be set
> to `0`, whereas the `Content-Length` header will not be set if the payload is
> `null`.
> Note: If you change the payload, you may only change it to a `string`, a
> `Buffer`, a `stream`, a `ReadableStream`, a `Response`, or `null`.
### onResponse
```js
fastify.addHook('onResponse', (request, reply, done) => {
// Some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('onResponse', async (request, reply) => {
// Some code
await asyncMethod()
})
```
The `onResponse` hook is executed when a response has been sent, so you will not
be able to send more data to the client. It can however be useful for sending
data to external services, for example, to gather statistics.
> Note: Setting `disableRequestLogging` to `true` will disable any error log
> inside the `onResponse` hook. In this case use `try - catch` to log errors.
### onTimeout
```js
fastify.addHook('onTimeout', (request, reply, done) => {
// Some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('onTimeout', async (request, reply) => {
// Some code
await asyncMethod()
})
```
`onTimeout` is useful if you need to monitor the request timed out in your
service (if the `connectionTimeout` property is set on the Fastify instance).
The `onTimeout` hook is executed when a request is timed out and the HTTP socket
has been hung up. Therefore, you will not be able to send data to the client.
### onRequestAbort
```js
fastify.addHook('onRequestAbort', (request, done) => {
// Some code
done()
})
```
Or `async/await`:
```js
fastify.addHook('onRequestAbort', async (request) => {
// Some code
await asyncMethod()
})
```
The `onRequestAbort` hook is executed when a client closes the connection before
the entire request has been processed. Therefore, you will not be able to send
data to the client.
> Note: Client abort detection is not completely reliable.
> See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
### Manage Errors from a hook
If you get an error during the execution of your hook, just pass it to `done()`
and Fastify will automatically close the request and send the appropriate error
code to the user.
```js
fastify.addHook('onRequest', (request, reply, done) => {
done(new Error('Some error'))
})
```
If you want to pass a custom error code to the user, just use `reply.code()`:
```js
fastify.addHook('preHandler', (request, reply, done) => {
reply.code(400)
done(new Error('Some error'))
})
```
*The error will be handled by [`Reply`](./Reply.md#errors).*
Or if you're using `async/await` you can just throw an error:
```js
fastify.addHook('onRequest', async (request, reply) => {
throw new Error('Some error')
})
```
### Respond to a request from a hook
If needed, you can respond to a request before you reach the route handler, for
example when implementing an authentication hook. Replying from a hook implies
that the hook chain is __stopped__ and the rest of the hooks and handlers are
not executed. If the hook is using the callback approach, i.e. it is not an
`async` function or it returns a `Promise`, it is as simple as calling
`reply.send()` and avoiding calling the callback. If the hook is `async`,
`reply.send()` __must__ be called _before_ the function returns or the promise
resolves, otherwise, the request will proceed. When `reply.send()` is called
outside of the promise chain, it is important to `return reply` otherwise the
request will be executed twice.
It is important to __not mix callbacks and `async`/`Promise`__, otherwise the
hook chain will be executed twice.
If you are using `onRequest` or `preHandler` use `reply.send`.
```js
fastify.addHook('onRequest', (request, reply, done) => {
reply.send('Early response')
})
// Works with async functions too
fastify.addHook('preHandler', async (request, reply) => {
setTimeout(() => {
reply.send({ hello: 'from prehandler' })
})
return reply // mandatory, so the request is not executed further
// Commenting the line above will allow the hooks to continue and fail with FST_ERR_REP_ALREADY_SENT
})
```
If you want to respond with a stream, you should avoid using an `async` function
for the hook. If you must use an `async` function, your code will need to follow
the pattern in
[test/hooks-async.js](https://github.com/fastify/fastify/blob/94ea67ef2d8dce8a955d510cd9081aabd036fa85/test/hooks-async.js#L269-L275).
```js
fastify.addHook('onRequest', (request, reply, done) => {
const stream = fs.createReadStream('some-file', 'utf8')
reply.send(stream)
})
```
If you are sending a response without `await` on it, make sure to always `return
reply`:
```js
fastify.addHook('preHandler', async (request, reply) => {
setImmediate(() => { reply.send('hello') })
// This is needed to signal the handler to wait for a response
// to be sent outside of the promise chain
return reply
})
fastify.addHook('preHandler', async (request, reply) => {
// the @fastify/static plugin will send a file asynchronously,
// so we should return reply
reply.sendFile('myfile')
return reply
})
```
## Application Hooks
You can hook into the application-lifecycle as well.
- [onReady](#onready)
- [onListen](#onlisten)
- [onClose](#onclose)
- [preClose](#preclose)
- [onRoute](#onroute)
- [onRegister](#onregister)
### onReady
Triggered before the server starts listening for requests and when `.ready()` is
invoked. It cannot change the routes or add new hooks. Registered hook functions
are executed serially. Only after all `onReady` hook functions have completed
will the server start listening for requests. Hook functions accept one
argument: a callback, `done`, to be invoked after the hook function is complete.
Hook functions are invoked with `this` bound to the associated Fastify instance.
```js
// callback style
fastify.addHook('onReady', function (done) {
// Some code
const err = null;
done(err)
})
// or async/await style
fastify.addHook('onReady', async function () {
// Some async code
await loadCacheFromDatabase()
})
```
### onListen
Triggered when the server starts listening for requests. The hooks run one
after another. If a hook function causes an error, it is logged and
ignored, allowing the queue of hooks to continue. Hook functions accept one
argument: a callback, `done`, to be invoked after the hook function is
complete. Hook functions are invoked with `this` bound to the associated
Fastify instance.
This is an alternative to `fastify.server.on('listening', () => {})`.
```js
// callback style
fastify.addHook('onListen', function (done) {
// Some code
const err = null;
done(err)
})
// or async/await style
fastify.addHook('onListen', async function () {
// Some async code
})
```
> Note: This hook will not run when the server is started using
> fastify.inject()` or `fastify.ready()`.
### onClose
<a id="on-close"></a>
Triggered when `fastify.close()` is invoked to stop the server, after all in-flight
HTTP requests have been completed.
It is useful when [plugins](./Plugins.md) need a "shutdown" event, for example,
to close an open connection to a database.
The hook function takes the Fastify instance as a first argument,
and a `done` callback for synchronous hook functions.
```js
// callback style
fastify.addHook('onClose', (instance, done) => {
// Some code
done()
})
// or async/await style
fastify.addHook('onClose', async (instance) => {
// Some async code
await closeDatabaseConnections()
})
```
### preClose
<a id="pre-close"></a>
Triggered when `fastify.close()` is invoked to stop the server, before all in-flight
HTTP requests have been completed.
It is useful when [plugins](./Plugins.md) have set up some state attached
to the HTTP server that would prevent the server to close.
_It is unlikely you will need to use this hook_,
use the [`onClose`](#onclose) for the most common case.
```js
// callback style
fastify.addHook('preClose', (done) => {
// Some code
done()
})
// or async/await style
fastify.addHook('preClose', async () => {
// Some async code
await removeSomeServerState()
})
```
### onRoute
<a id="on-route"></a>
Triggered when a new route is registered. Listeners are passed a [`routeOptions`](./Routes.md#routes-options)
object as the sole parameter. The interface is synchronous, and, as such, the
listeners are not passed a callback. This hook is encapsulated.
```js
fastify.addHook('onRoute', (routeOptions) => {
//Some code
routeOptions.method
routeOptions.schema
routeOptions.url // the complete URL of the route, it will include the prefix if any
routeOptions.path // `url` alias
routeOptions.routePath // the URL of the route without the prefix
routeOptions.bodyLimit
routeOptions.logLevel
routeOptions.logSerializers
routeOptions.prefix
})
```
If you are authoring a plugin and you need to customize application routes, like
modifying the options or adding new route hooks, this is the right place.
```js
fastify.addHook('onRoute', (routeOptions) => {
function onPreSerialization(request, reply, payload, done) {
// Your code
done(null, payload)
}
// preSerialization can be an array or undefined
routeOptions.preSerialization = [...(routeOptions.preSerialization || []), onPreSerialization]
})
```
To add more routes within an onRoute hook, the routes must
be tagged correctly. The hook will run into an infinite loop if
not tagged. The recommended approach is shown below.
```js
const kRouteAlreadyProcessed = Symbol('route-already-processed')
fastify.addHook('onRoute', function (routeOptions) {
const { url, method } = routeOptions
const isAlreadyProcessed = (routeOptions.custom && routeOptions.custom[kRouteAlreadyProcessed]) || false
if (!isAlreadyProcessed) {
this.route({
url,
method,
custom: {
[kRouteAlreadyProcessed]: true
},
handler: () => {}
})
}
})
```
For more details, see this [issue](https://github.com/fastify/fastify/issues/4319).
### onRegister
<a id="on-register"></a>
Triggered when a new plugin is registered and a new encapsulation context is
created. The hook will be executed **before** the registered code.
This hook can be useful if you are developing a plugin that needs to know when a
plugin context is formed, and you want to operate in that specific context, thus
this hook is encapsulated.
> Note: This hook will not be called if a plugin is wrapped inside
> [`fastify-plugin`](https://github.com/fastify/fastify-plugin).
```js
fastify.decorate('data', [])
fastify.register(async (instance, opts) => {
instance.data.push('hello')
console.log(instance.data) // ['hello']
instance.register(async (instance, opts) => {
instance.data.push('world')
console.log(instance.data) // ['hello', 'world']
}, { prefix: '/hola' })
}, { prefix: '/ciao' })
fastify.register(async (instance, opts) => {
console.log(instance.data) // []
}, { prefix: '/hello' })
fastify.addHook('onRegister', (instance, opts) => {
// Create a new array from the old one
// but without keeping the reference
// allowing the user to have encapsulated
// instances of the `data` property
instance.data = instance.data.slice()
// the options of the new registered instance
console.log(opts.prefix)
})
```
## Scope
<a id="scope"></a>
Except for [onClose](#onclose), all hooks are encapsulated. This means that you
can decide where your hooks should run by using `register` as explained in the
[plugins guide](../Guides/Plugins-Guide.md). If you pass a function, that
function is bound to the right Fastify context and from there you have full
access to the Fastify API.
```js
fastify.addHook('onRequest', function (request, reply, done) {
const self = this // Fastify context
done()
})
```
Note that the Fastify context in each hook is the same as the plugin where the
route was registered, for example:
```js
fastify.addHook('onRequest', async function (req, reply) {
if (req.raw.url === '/nested') {
assert.strictEqual(this.foo, 'bar')
} else {
assert.strictEqual(this.foo, undefined)
}
})
fastify.get('/', async function (req, reply) {
assert.strictEqual(this.foo, undefined)
return { hello: 'world' }
})
fastify.register(async function plugin (fastify, opts) {
fastify.decorate('foo', 'bar')
fastify.get('/nested', async function (req, reply) {
assert.strictEqual(this.foo, 'bar')
return { hello: 'world' }
})
})
```
Warn: if you declare the function with an [arrow
function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions),
the `this` will not be Fastify, but the one of the current scope.
## Route level hooks
<a id="route-hooks"></a>
You can declare one or more custom lifecycle hooks ([onRequest](#onrequest),
[onResponse](#onresponse), [preParsing](#preparsing),
[preValidation](#prevalidation), [preHandler](#prehandler),
[preSerialization](#preserialization), [onSend](#onsend),
[onTimeout](#ontimeout), and [onError](#onerror)) hook(s) that will be
**unique** for the route. If you do so, those hooks are always executed as the
last hook in their category.
This can be useful if you need to implement authentication, where the
[preParsing](#preparsing) or [preValidation](#prevalidation) hooks are exactly
what you need. Multiple route-level hooks can also be specified as an array.
```js
fastify.addHook('onRequest', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
// your code
done()
})
fastify.addHook('preParsing', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preValidation', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preHandler', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('preSerialization', (request, reply, payload, done) => {
// Your code
done(null, payload)
})
fastify.addHook('onSend', (request, reply, payload, done) => {
// Your code
done(null, payload)
})
fastify.addHook('onTimeout', (request, reply, done) => {
// Your code
done()
})
fastify.addHook('onError', (request, reply, error, done) => {
// Your code
done()
})
fastify.route({
method: 'GET',
url: '/',
schema: { ... },
onRequest: function (request, reply, done) {
// This hook will always be executed after the shared `onRequest` hooks
done()
},
// // Example with an async hook. All hooks support this syntax
//
// onRequest: async function (request, reply) {
// // This hook will always be executed after the shared `onRequest` hooks
// await ...
// }
onResponse: function (request, reply, done) {
// this hook will always be executed after the shared `onResponse` hooks
done()
},
preParsing: function (request, reply, done) {
// This hook will always be executed after the shared `preParsing` hooks
done()
},
preValidation: function (request, reply, done) {
// This hook will always be executed after the shared `preValidation` hooks
done()
},
preHandler: function (request, reply, done) {
// This hook will always be executed after the shared `preHandler` hooks
done()
},
// // Example with an array. All hooks support this syntax.
//
// preHandler: [function (request, reply, done) {
// // This hook will always be executed after the shared `preHandler` hooks
// done()
// }],
preSerialization: (request, reply, payload, done) => {
// This hook will always be executed after the shared `preSerialization` hooks
done(null, payload)
},
onSend: (request, reply, payload, done) => {
// This hook will always be executed after the shared `onSend` hooks
done(null, payload)
},
onTimeout: (request, reply, done) => {
// This hook will always be executed after the shared `onTimeout` hooks
done()
},
onError: (request, reply, error, done) => {
// This hook will always be executed after the shared `onError` hooks
done()
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
```
> Note: Both options also accept an array of functions.
## Using Hooks to Inject Custom Properties
<a id="using-hooks-to-inject-custom-properties"></a>
You can use a hook to inject custom properties into incoming requests.
This is useful for reusing processed data from hooks in controllers.
A very common use case is, for example, checking user authentication based
on their token and then storing their recovered data into
the [Request](./Request.md) instance. This way, your controllers can read it
easily with `request.authenticatedUser` or whatever you want to call it.
That's how it might look like:
```js
fastify.addHook('preParsing', async (request) => {
request.authenticatedUser = {
id: 42,
name: 'Jane Doe',
role: 'admin'
}
})
fastify.get('/me/is-admin', async function (req, reply) {
return { isAdmin: req.authenticatedUser?.role === 'admin' || false }
})
```
Note that `.authenticatedUser` could actually be any property name
chosen by yourself. Using your own custom property prevents you
from mutating existing properties, which
would be a dangerous and destructive operation. So be careful and
make sure your property is entirely new, also using this approach
only for very specific and small cases like this example.
Regarding TypeScript in this example, you'd need to update the
`FastifyRequest` core interface to include your new property typing
(for more about it, see [TypeScript](./TypeScript.md) page), like:
```ts
interface AuthenticatedUser { /* ... */ }
declare module 'fastify' {
export interface FastifyRequest {
authenticatedUser?: AuthenticatedUser;
}
}
```
Although this is a very pragmatic approach, if you're trying to do
something more complex that changes these core objects, then
consider creating a custom [Plugin](./Plugins.md) instead.
## Diagnostics Channel Hooks
One [`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html)
publish event, `'fastify.initialization'`, happens at initialization time. The
Fastify instance is passed into the hook as a property of the object passed in.
At this point, the instance can be interacted with to add hooks, plugins,
routes, or any other sort of modification.
For example, a tracing package might do something like the following (which is,
of course, a simplification). This would be in a file loaded in the
initialization of the tracking package, in the typical "require instrumentation
tools first" fashion.
```js
const tracer = /* retrieved from elsewhere in the package */
const dc = require('node:diagnostics_channel')
const channel = dc.channel('fastify.initialization')
const spans = new WeakMap()
channel.subscribe(function ({ fastify }) {
fastify.addHook('onRequest', (request, reply, done) => {
const span = tracer.startSpan('fastify.request.handler')
spans.set(request, span)
done()
})
fastify.addHook('onResponse', (request, reply, done) => {
const span = spans.get(request)
span.finish()
done()
})
})
```
> Note: The TracingChannel class API is currently experimental and may undergo
> breaking changes even in semver-patch releases of Node.js.
Five other events are published on a per-request basis following the
[Tracing Channel](https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel)
nomenclature. The list of the channel names and the event they receive is:
- `tracing:fastify.request.handler:start`: Always fires
- `{ request: Request, reply: Reply, route: { url, method } }`
- `tracing:fastify.request.handler:end`: Always fires
- `{ request: Request, reply: Reply, route: { url, method }, async: Bool }`
- `tracing:fastify.request.handler:asyncStart`: Fires for promise/async handlers
- `{ request: Request, reply: Reply, route: { url, method } }`
- `tracing:fastify.request.handler:asyncEnd`: Fires for promise/async handlers
- `{ request: Request, reply: Reply, route: { url, method } }`
- `tracing:fastify.request.handler:error`: Fires when an error occurs
- `{ request: Request, reply: Reply, route: { url, method }, error: Error }`
The object instance remains the same for all events associated with a given
request. All payloads include a `request` and `reply` property which are an
instance of Fastify's `Request` and `Reply` instances. They also include a
`route` property which is an object with the matched `url` pattern (e.g.
`/collection/:id`) and the `method` HTTP method (e.g. `GET`). The `:start` and
`:end` events always fire for requests. If a request handler is an `async`
function or one that returns a `Promise` then the `:asyncStart` and `:asyncEnd`
events also fire. Finally, the `:error` event contains an `error` property
associated with the request's failure.
These events can be received like so:
```js
const dc = require('node:diagnostics_channel')
const channel = dc.channel('tracing:fastify.request.handler:start')
channel.subscribe((msg) => {
console.log(msg.request, msg.reply)
})
```

73
node_modules/fastify/docs/Reference/Index.md generated vendored Normal file
View File

@@ -0,0 +1,73 @@
<h1 align="center">Fastify</h1>
## Core Documents
<a id="reference-core-docs"></a>
For the full table of contents (TOC), see [below](#reference-toc). The following
list is a subset of the full TOC that detail core Fastify APIs and concepts in
order of most likely importance to the reader:
+ [Server](./Server.md): Documents the core Fastify API. Includes documentation
for the factory function and the object returned by the factory function.
+ [Lifecycle](./Lifecycle.md): Explains the Fastify request lifecycle and
illustrates where [Hooks](./Hooks.md) are available for integrating with it.
+ [Routes](./Routes.md): Details how to register routes with Fastify and how
Fastify builds and evaluates the routing trie.
+ [Request](./Request.md): Details Fastify's request object that is passed into
each request handler.
+ [Reply](./Reply.md): Details Fastify's response object available to each
request handler.
+ [Validation and Serialization](./Validation-and-Serialization.md): Details
Fastify's support for validating incoming data and how Fastify serializes data
for responses.
+ [Plugins](./Plugins.md): Explains Fastify's plugin architecture and API.
+ [Encapsulation](./Encapsulation.md): Explains a core concept upon which all
Fastify plugins are built.
+ [Decorators](./Decorators.md): Explains the server, request, and response
decorator APIs.
+ [Hooks](./Hooks.md): Details the API by which Fastify plugins can inject
themselves into Fastify's handling of the request lifecycle.
## Reference Documentation Table Of Contents
<a id="reference-toc"></a>
This table of contents is in alphabetical order.
+ [Content Type Parser](./ContentTypeParser.md): Documents Fastify's default
content type parser and how to add support for new content types.
+ [Decorators](./Decorators.md): Explains the server, request, and response
decorator APIs.
+ [Encapsulation](./Encapsulation.md): Explains a core concept upon which all
Fastify plugins are built.
+ [Errors](./Errors.md): Details how Fastify handles errors and lists the
standard set of errors Fastify generates.
+ [Hooks](./Hooks.md): Details the API by which Fastify plugins can inject
themselves into Fastify's handling of the request lifecycle.
+ [HTTP2](./HTTP2.md): Details Fastify's HTTP2 support.
+ [Lifecycle](./Lifecycle.md): Explains the Fastify request lifecycle and
illustrates where [Hooks](./Hooks.md) are available for integrating with it.
+ [Logging](./Logging.md): Details Fastify's included logging and how to
customize it.
+ [Long Term Support](./LTS.md): Explains Fastify's long term support (LTS)
guarantee and the exceptions possible to the [semver](https://semver.org)
contract.
+ [Middleware](./Middleware.md): Details Fastify's support for Express.js style
middleware.
+ [Plugins](./Plugins.md): Explains Fastify's plugin architecture and API.
+ [Reply](./Reply.md): Details Fastify's response object available to each
request handler.
+ [Request](./Request.md): Details Fastify's request object that is passed into
each request handler.
+ [Routes](./Routes.md): Details how to register routes with Fastify and how
Fastify builds and evaluates the routing trie.
+ [Server](./Server.md): Documents the core Fastify API. Includes documentation
for the factory function and the object returned by the factory function.
+ [TypeScript](./TypeScript.md): Documents Fastify's TypeScript support and
provides recommendations for writing applications in TypeScript that utilize
Fastify.
+ [Validation and Serialization](./Validation-and-Serialization.md): Details
Fastify's support for validating incoming data and how Fastify serializes data
for responses.
+ [Warnings](./Warnings.md): Details the warnings Fastify emits and how to
solve them.

86
node_modules/fastify/docs/Reference/LTS.md generated vendored Normal file
View File

@@ -0,0 +1,86 @@
<h1 align="center">Fastify</h1>
## Long Term Support
<a id="lts"></a>
Fastify's Long Term Support (LTS) is provided according to the schedule laid out
in this document:
1. Major releases, "X" release of [semantic versioning][semver] X.Y.Z release
versions, are supported for a minimum period of six months from their release
date. The release date of any specific version can be found at
[https://github.com/fastify/fastify/releases](https://github.com/fastify/fastify/releases).
2. Major releases will receive security updates for an additional six months
from the release of the next major release. After this period we will still
review and release security fixes as long as they are provided by the
community and they do not violate other constraints, e.g. minimum supported
Node.js version.
3. Major releases will be tested and verified against all Node.js release lines
that are supported by the [Node.js LTS
policy](https://github.com/nodejs/Release) within the LTS period of that
given Fastify release line. This implies that only the latest Node.js release
of a given line is supported.
4. In addition to Node.js runtime, major releases of Fastify will also be tested
and verified against alternative runtimes that are compatible with Node.js.
The maintenance teams of these alternative runtimes are responsible for ensuring
and guaranteeing these tests work properly.
1. [N|Solid](https://docs.nodesource.com/docs/product_suite) tests and
verifies each Fastify major release against current N|Solid LTS versions.
NodeSource ensures Fastify compatibility with N|Solid, aligning with the
support scope of N|Solid LTS versions at the time of the Fastify release.
This guarantees N|Solid users can confidently use Fastify.
A "month" is defined as 30 consecutive days.
> ## Security Releases and Semver
>
> As a consequence of providing long-term support for major releases, there are
> occasions where we need to release breaking changes as a _minor_ version
> release. Such changes will _always_ be noted in the [release
> notes](https://github.com/fastify/fastify/releases).
>
> To avoid automatically receiving breaking security updates it is possible to
> use the tilde (`~`) range qualifier. For example, to get patches for the 3.15
> release, and avoid automatically updating to the 3.16 release, specify the
> dependency as `"fastify": "~3.15.x"`. This will leave your application
> vulnerable, so please use it with caution.
### Security Support Beyond LTS
Fastify's partner, HeroDevs, provides commercial security support through the
OpenJS Ecosystem Sustainability Program for versions of Fastify that are EOL.
For more information, see their [Never Ending Support][hd-link] service.
### Schedule
<a id="lts-schedule"></a>
| Version | Release Date | End Of LTS Date | Node.js | Nsolid(Node) |
| :------ | :----------- | :-------------- | :----------------- | :------------- |
| 1.0.0 | 2018-03-06 | 2019-09-01 | 6, 8, 9, 10, 11 | |
| 2.0.0 | 2019-02-25 | 2021-01-31 | 6, 8, 10, 12, 14 | |
| 3.0.0 | 2020-07-07 | 2023-06-30 | 10, 12, 14, 16, 18 | v5(18) |
| 4.0.0 | 2022-06-08 | 2025-06-30 | 14, 16, 18, 20, 22 | v5(18), v5(20) |
| 5.0.0 | 2024-09-17 | TBD | 20, 22 | v5(20) |
### CI tested operating systems
<a id="supported-os"></a>
Fastify uses GitHub Actions for CI testing, please refer to [GitHub&#39;s
documentation regarding workflow
runners](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources)
for further details on what the latest virtual environment is in relation to the
YAML workflow labels below:
| OS | YAML Workflow Label | Package Manager | Node.js | Nsolid(Node) |
| ------- | ------------------- | --------------- | ----------- | ------------- |
| Linux | `ubuntu-latest` | npm | 20 | v5(20) |
| Linux | `ubuntu-latest` | yarn,pnpm | 20 | v5(20) |
| Windows | `windows-latest` | npm | 20 | v5(20) |
| MacOS | `macos-latest` | npm | 20 | v5(20) |
Using [yarn](https://yarnpkg.com/) might require passing the `--ignore-engines`
flag.
[semver]: https://semver.org/
[hd-link]: https://www.herodevs.com/support/fastify-nes?utm_source=fastify&utm_medium=link&utm_campaign=eol_support_fastify

84
node_modules/fastify/docs/Reference/Lifecycle.md generated vendored Normal file
View File

@@ -0,0 +1,84 @@
<h1 align="center">Fastify</h1>
## Lifecycle
<a id="lifecycle"></a>
This schema shows the internal lifecycle of Fastify.
The right branch of each section shows the next phase of the lifecycle. The left
branch shows the corresponding error code generated if the parent throws an
error. All errors are automatically handled by Fastify.
```
Incoming Request
└─▶ Routing
└─▶ Instance Logger
4**/5** ◀─┴─▶ onRequest Hook
4**/5** ◀─┴─▶ preParsing Hook
4**/5** ◀─┴─▶ Parsing
4**/5** ◀─┴─▶ preValidation Hook
400 ◀─┴─▶ Validation
4**/5** ◀─┴─▶ preHandler Hook
4**/5** ◀─┴─▶ User Handler
└─▶ Reply
4**/5** ◀─┴─▶ preSerialization Hook
└─▶ onSend Hook
4**/5** ◀─┴─▶ Outgoing Response
└─▶ onResponse Hook
```
Before or during the `User Handler`, `reply.hijack()` can be called to:
- Prevent Fastify from running subsequent hooks and the user handler
- Prevent Fastify from sending the response automatically
If `reply.raw` is used to send a response, `onResponse` hooks will still
be executed.
## Reply Lifecycle
<a id="reply-lifecycle"></a>
When the user handles the request, the result may be:
- In an async handler: it returns a payload or throws an `Error`
- In a sync handler: it sends a payload or an `Error` instance
If the reply was hijacked, all subsequent steps are skipped. Otherwise, when
submitted, the data flow is as follows:
```
★ schema validation Error
└─▶ schemaErrorFormatter
reply sent ◀── JSON ─┴─ Error instance
│ ★ throw an Error
★ send or return │ │
│ │ │
│ ▼ │
reply sent ◀── JSON ─┴─ Error instance ──▶ onError Hook ◀───────┘
reply sent ◀── JSON ─┴─ Error instance ──▶ setErrorHandler
└─▶ reply sent
```
`reply sent` means the JSON payload will be serialized by one of the following:
- The [reply serializer](./Server.md#setreplyserializer) if set
- The [serializer compiler](./Server.md#setserializercompiler) if a JSON schema
is set for the HTTP status code
- The default `JSON.stringify` function

260
node_modules/fastify/docs/Reference/Logging.md generated vendored Normal file
View File

@@ -0,0 +1,260 @@
<h1 align="center">Fastify</h1>
## Logging
### Enable Logging
Logging is disabled by default. Enable it by passing `{ logger: true }` or
`{ logger: { level: 'info' } }` when creating a Fastify instance. Note that if
the logger is disabled, it cannot be enabled at runtime.
[abstract-logging](https://www.npmjs.com/package/abstract-logging) is used for
this purpose.
As Fastify is focused on performance, it uses
[pino](https://github.com/pinojs/pino) as its logger, with the default log
level set to `'info'` when enabled.
#### Basic logging setup
Enabling the production JSON logger:
```js
const fastify = require('fastify')({
logger: true
})
```
#### Environment-Specific Configuration
Enabling the logger with appropriate configuration for local development,
production, and test environments requires more configuration:
```js
const envToLogger = {
development: {
transport: {
target: 'pino-pretty',
options: {
translateTime: 'HH:MM:ss Z',
ignore: 'pid,hostname',
},
},
},
production: true,
test: false,
}
const fastify = require('fastify')({
logger: envToLogger[environment] ?? true // defaults to true if no entry matches in the map
})
```
⚠️ `pino-pretty` needs to be installed as a dev dependency. It is not included
by default for performance reasons.
### Usage
The logger can be used in route handlers as follows:
```js
fastify.get('/', options, function (request, reply) {
request.log.info('Some info about the current request')
reply.send({ hello: 'world' })
})
```
Trigger new logs outside route handlers using the Pino instance from the Fastify
instance:
```js
fastify.log.info('Something important happened!');
```
#### Passing Logger Options
To pass options to the logger, provide them to Fastify. See the
[Pino documentation](https://github.com/pinojs/pino/blob/master/docs/api.md#options)
for available options. To specify a file destination, use:
```js
const fastify = require('fastify')({
logger: {
level: 'info',
file: '/path/to/file' // Will use pino.destination()
}
})
fastify.get('/', options, function (request, reply) {
request.log.info('Some info about the current request')
reply.send({ hello: 'world' })
})
```
To pass a custom stream to the Pino instance, add a `stream` field to the logger
object:
```js
const split = require('split2')
const stream = split(JSON.parse)
const fastify = require('fastify')({
logger: {
level: 'info',
stream: stream
}
})
```
### Advanced Logger Configuration
<a id="logging-request-id"></a>
#### Request ID Tracking
By default, Fastify adds an ID to every request for easier tracking. If the
`requestIdHeader` option is set and the corresponding header is present, its
value is used; otherwise, a new incremental ID is generated. See Fastify Factory
[`requestIdHeader`](./Server.md#factory-request-id-header) and Fastify Factory
[`genReqId`](./Server.md#genreqid) for customization options.
#### Serializers
The default logger uses standard serializers for objects with `req`, `res`, and
`err` properties. The `req` object is the Fastify [`Request`](./Request.md)
object, and the `res` object is the Fastify [`Reply`](./Reply.md) object. This
behavior can be customized with custom serializers.
```js
const fastify = require('fastify')({
logger: {
serializers: {
req (request) {
return { url: request.url }
}
}
}
})
```
For example, the response payload and headers could be logged using the approach
below (not recommended):
```js
const fastify = require('fastify')({
logger: {
transport: {
target: 'pino-pretty'
},
serializers: {
res (reply) {
// The default
return {
statusCode: reply.statusCode
}
},
req (request) {
return {
method: request.method,
url: request.url,
path: request.routeOptions.url,
parameters: request.params,
// Including headers in the log could violate privacy laws,
// e.g., GDPR. Use the "redact" option to remove sensitive
// fields. It could also leak authentication data in the logs.
headers: request.headers
};
}
}
}
});
```
> Note: In some cases, the [`Reply`](./Reply.md) object passed to the `res`
> serializer cannot be fully constructed. When writing a custom `res`
> serializer, check for the existence of any properties on `reply` aside from
> `statusCode`, which is always present. For example, verify the existence of
> `getHeaders` before calling it:
```js
const fastify = require('fastify')({
logger: {
transport: {
target: 'pino-pretty'
},
serializers: {
res (reply) {
// The default
return {
statusCode: reply.statusCode,
headers: typeof reply.getHeaders === 'function'
? reply.getHeaders()
: {}
}
},
}
}
});
```
> Note: The body cannot be serialized inside a `req` method because the
request is serialized when the child logger is created. At that time, the body
is not yet parsed.
See the following approach to log `req.body`:
```js
app.addHook('preHandler', function (req, reply, done) {
if (req.body) {
req.log.info({ body: req.body }, 'parsed body')
}
done()
})
```
> Note: Ensure serializers never throw errors, as this can cause the Node
> process to exit. See the
> [Pino documentation](https://getpino.io/#/docs/api?id=opt-serializers) for more
> information.
*Any logger other than Pino will ignore this option.*
### Using Custom Loggers
A custom logger instance can be supplied by passing it as `loggerInstance`. The
logger must conform to the Pino interface, with methods: `info`, `error`,
`debug`, `fatal`, `warn`, `trace`, `silent`, `child`, and a string property
`level`.
Example:
```js
const log = require('pino')({ level: 'info' })
const fastify = require('fastify')({ loggerInstance: log })
log.info('does not have request information')
fastify.get('/', function (request, reply) {
request.log.info('includes request information, but is the same logger instance as `log`')
reply.send({ hello: 'world' })
})
```
*The logger instance for the current request is available in every part of the
[lifecycle](./Lifecycle.md).*
### Log Redaction
[Pino](https://getpino.io) supports low-overhead log redaction for obscuring
values of specific properties in recorded logs. For example, log all HTTP
headers except the `Authorization` header for security:
```js
const fastify = Fastify({
logger: {
stream: stream,
redact: ['req.headers.authorization'],
level: 'info',
serializers: {
req (request) {
return {
method: request.method,
url: request.url,
headers: request.headers,
host: request.host,
remoteAddress: request.ip,
remotePort: request.socket.remotePort
}
}
}
}
})
```
See https://getpino.io/#/docs/redaction for more details.

78
node_modules/fastify/docs/Reference/Middleware.md generated vendored Normal file
View File

@@ -0,0 +1,78 @@
<h1 align="center">Fastify</h1>
## Middleware
Starting with Fastify v3.0.0, middleware is not supported out of the box and
requires an external plugin such as
[`@fastify/express`](https://github.com/fastify/fastify-express) or
[`@fastify/middie`](https://github.com/fastify/middie).
An example of registering the
[`@fastify/express`](https://github.com/fastify/fastify-express) plugin to `use`
Express middleware:
```js
await fastify.register(require('@fastify/express'))
fastify.use(require('cors')())
fastify.use(require('dns-prefetch-control')())
fastify.use(require('frameguard')())
fastify.use(require('hsts')())
fastify.use(require('ienoopen')())
fastify.use(require('x-xss-protection')())
```
[`@fastify/middie`](https://github.com/fastify/middie) can also be used,
which provides support for simple Express-style middleware with improved
performance:
```js
await fastify.register(require('@fastify/middie'))
fastify.use(require('cors')())
```
Middleware can be encapsulated, allowing control over where it runs using
`register` as explained in the [plugins guide](../Guides/Plugins-Guide.md).
Fastify middleware does not expose the `send` method or other methods specific
to the Fastify [Reply](./Reply.md#reply) instance. This is because Fastify wraps
the incoming `req` and `res` Node instances using the
[Request](./Request.md#request) and [Reply](./Reply.md#reply) objects
internally, but this is done after the middleware phase. To create middleware,
use the Node `req` and `res` instances. Alternatively, use the `preHandler` hook
that already has the Fastify [Request](./Request.md#request) and
[Reply](./Reply.md#reply) instances. For more information, see
[Hooks](./Hooks.md#hooks).
#### Restrict middleware execution to certain paths
<a id="restrict-usage"></a>
To run middleware under certain paths, pass the path as the first parameter to
`use`.
> Note: This does not support routes with parameters
> (e.g. `/user/:id/comments`) and wildcards are not supported in multiple paths.
```js
const path = require('node:path')
const serveStatic = require('serve-static')
// Single path
fastify.use('/css', serveStatic(path.join(__dirname, '/assets')))
// Wildcard path
fastify.use('/css/(.*)', serveStatic(path.join(__dirname, '/assets')))
// Multiple paths
fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets')))
```
### Alternatives
Fastify offers alternatives to commonly used middleware, such as
[`@fastify/helmet`](https://github.com/fastify/fastify-helmet) for
[`helmet`](https://github.com/helmetjs/helmet),
[`@fastify/cors`](https://github.com/fastify/fastify-cors) for
[`cors`](https://github.com/expressjs/cors), and
[`@fastify/static`](https://github.com/fastify/fastify-static) for
[`serve-static`](https://github.com/expressjs/serve-static).

236
node_modules/fastify/docs/Reference/Plugins.md generated vendored Normal file
View File

@@ -0,0 +1,236 @@
<h1 align="center">Fastify</h1>
## Plugins
Fastify can be extended with plugins, which can be a set of routes, a server
[decorator](./Decorators.md), or other functionality. Use the `register` API to
add one or more plugins.
By default, `register` creates a *new scope*, meaning changes to the Fastify
instance (via `decorate`) will not affect the current context ancestors, only
its descendants. This feature enables plugin *encapsulation* and *inheritance*,
creating a *directed acyclic graph* (DAG) and avoiding cross-dependency issues.
The [Getting Started](../Guides/Getting-Started.md#your-first-plugin) guide
includes an example of using this API:
```js
fastify.register(plugin, [options])
```
### Plugin Options
<a id="plugin-options"></a>
The optional `options` parameter for `fastify.register` supports a predefined
set of options that Fastify itself will use, except when the plugin has been
wrapped with [fastify-plugin](https://github.com/fastify/fastify-plugin). This
options object will also be passed to the plugin upon invocation, regardless of
whether or not the plugin has been wrapped. The currently supported list of
Fastify specific options is:
+ [`logLevel`](./Routes.md#custom-log-level)
+ [`logSerializers`](./Routes.md#custom-log-serializer)
+ [`prefix`](#route-prefixing-option)
These options will be ignored when used with fastify-plugin.
To avoid collisions, a plugin should consider namespacing its options. For
example, a plugin `foo` might be registered like so:
```js
fastify.register(require('fastify-foo'), {
prefix: '/foo',
foo: {
fooOption1: 'value',
fooOption2: 'value'
}
})
```
If collisions are not a concern, the plugin may accept the options object as-is:
```js
fastify.register(require('fastify-foo'), {
prefix: '/foo',
fooOption1: 'value',
fooOption2: 'value'
})
```
The `options` parameter can also be a `Function` evaluated at plugin registration,
providing access to the Fastify instance via the first argument:
```js
const fp = require('fastify-plugin')
fastify.register(fp((fastify, opts, done) => {
fastify.decorate('foo_bar', { hello: 'world' })
done()
}))
// The opts argument of fastify-foo will be { hello: 'world' }
fastify.register(require('fastify-foo'), parent => parent.foo_bar)
```
The Fastify instance passed to the function is the latest state of the **external
Fastify instance** the plugin was declared on, allowing access to variables
injected via [`decorate`](./Decorators.md) by preceding plugins according to the
**order of registration**. This is useful if a plugin depends on changes made to
the Fastify instance by a preceding plugin, such as utilizing an existing database
connection.
Keep in mind that the Fastify instance passed to the function is the same as the
one passed into the plugin, a copy of the external Fastify instance rather than a
reference. Any usage of the instance will behave the same as it would if called
within the plugin's function. For example, if `decorate` is called, the decorated
variables will be available within the plugin's function unless it was wrapped
with [`fastify-plugin`](https://github.com/fastify/fastify-plugin).
#### Route Prefixing option
<a id="route-prefixing-option"></a>
If an option with the key `prefix` and a `string` value is passed, Fastify will
use it to prefix all the routes inside the register. For more info, check
[here](./Routes.md#route-prefixing).
Be aware that if routes are wrapped with
[`fastify-plugin`](https://github.com/fastify/fastify-plugin), this option will
not work (see the [workaround](./Routes.md#fastify-plugin)).
#### Error handling
<a id="error-handling"></a>
Error handling is done by [avvio](https://github.com/mcollina/avvio#error-handling).
As a general rule, handle errors in the next `after` or `ready` block, otherwise
they will be caught inside the `listen` callback.
```js
fastify.register(require('my-plugin'))
// `after` will be executed once
// the previous declared `register` has finished
fastify.after(err => console.log(err))
// `ready` will be executed once all the registers declared
// have finished their execution
fastify.ready(err => console.log(err))
// `listen` is a special ready,
// so it behaves in the same way
fastify.listen({ port: 3000 }, (err, address) => {
if (err) console.log(err)
})
```
### async/await
<a id="async-await"></a>
*async/await* is supported by `after`, `ready`, and `listen`, as well as
`fastify` being a Thenable.
```js
await fastify.register(require('my-plugin'))
await fastify.after()
await fastify.ready()
await fastify.listen({ port: 3000 })
```
Using `await` when registering a plugin loads the plugin and its dependencies,
"finalizing" the encapsulation process. Any mutations to the plugin after it and
its dependencies have been loaded will not be reflected in the parent instance.
#### ESM support
<a id="esm-support"></a>
ESM is supported from [Node.js `v13.3.0`](https://nodejs.org/api/esm.html)
and above.
```js
// main.mjs
import Fastify from 'fastify'
const fastify = Fastify()
fastify.register(import('./plugin.mjs'))
fastify.listen({ port: 3000 }, console.log)
// plugin.mjs
async function plugin (fastify, opts) {
fastify.get('/', async (req, reply) => {
return { hello: 'world' }
})
}
export default plugin
```
### Create a plugin
<a id="create-plugin"></a>
Creating a plugin is easy. Create a function that takes three parameters: the
`fastify` instance, an `options` object, and the `done` callback.
Example:
```js
module.exports = function (fastify, opts, done) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
done()
}
```
`register` can also be used inside another `register`:
```js
module.exports = function (fastify, opts, done) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
fastify.register(require('./other-plugin'))
done()
}
```
Remember, `register` always creates a new Fastify scope. If this is not needed,
read the following section.
### Handle the scope
<a id="handle-scope"></a>
If `register` is used only to extend server functionality with
[`decorate`](./Decorators.md), tell Fastify not to create a new scope. Otherwise,
changes will not be accessible in the upper scope.
There are two ways to avoid creating a new context:
- Use the [`fastify-plugin`](https://github.com/fastify/fastify-plugin) module
- Use the `'skip-override'` hidden property
Using the `fastify-plugin` module is recommended, as it solves this problem and
allows passing a version range of Fastify that the plugin will support:
```js
const fp = require('fastify-plugin')
module.exports = fp(function (fastify, opts, done) {
fastify.decorate('utility', function () {})
done()
}, '0.x')
```
Check the [`fastify-plugin`](https://github.com/fastify/fastify-plugin)
documentation to learn more about how to use this module.
If not using `fastify-plugin`, the `'skip-override'` hidden property can be used,
but it is not recommended. Future Fastify API changes will be your responsibility
to update, whilst `fastify-plugin` ensures backward compatibility.
```js
function yourPlugin (fastify, opts, done) {
fastify.decorate('utility', function () {})
done()
}
yourPlugin[Symbol.for('skip-override')] = true
module.exports = yourPlugin
```

73
node_modules/fastify/docs/Reference/Principles.md generated vendored Normal file
View File

@@ -0,0 +1,73 @@
# Technical Principles
Every decision in the Fastify framework and its official plugins is guided by
the following technical principles:
1. “Zero” overhead in production
2. “Good” developer experience
3. Works great for small & big projects alike
4. Easy to migrate to microservices (or even serverless) and back
5. Security & data validation
6. If something could be a plugin, it likely should be
7. Easily testable
8. Do not monkeypatch core
9. Semantic versioning & Long Term Support
10. Specification adherence
## "Zero" Overhead in Production
Fastify aims to implement features with minimal overhead. This is achieved by
using fast algorithms, data structures, and JavaScript-specific features.
Since JavaScript does not offer zero-overhead data structures, this principle
can conflict with providing a great developer experience and additional features,
as these usually incur some overhead.
## "Good" Developer Experience
Fastify aims to provide the best developer experience at its performance point.
It offers a great out-of-the-box experience that is flexible enough to adapt to
various situations.
For example, binary addons are forbidden because most JavaScript developers do
not have access to a compiler.
## Works great for small and big projects alike
Most applications start small and become more complex over time. Fastify aims to
grow with this complexity, providing advanced features to structure codebases.
## Easy to migrate to microservices (or even serverless) and back
Route deployment should not matter. The framework should "just work".
## Security and Data Validation
A web framework is the first point of contact with untrusted data and must act
as the first line of defense for the system.
## If something could be a plugin, it likely should
Recognizing the infinite use cases for an HTTP framework, catering to all in a
single module would make the codebase unmaintainable. Therefore, hooks and
options are provided to customize the framework as needed.
## Easily testable
Testing Fastify applications should be a first-class concern.
## Do not monkeypatch core
Monkeypatching Node.js APIs or installing globals that alter the runtime makes
building modular applications harder and limits Fastify's use cases. Other
frameworks do this; Fastify does not.
## Semantic Versioning and Long Term Support
A clear [Long Term Support strategy is provided](./LTS.md) to inform developers when
to upgrade.
## Specification adherence
In doubt, we chose the strict behavior as defined by the relevant
Specifications.

979
node_modules/fastify/docs/Reference/Reply.md generated vendored Normal file
View File

@@ -0,0 +1,979 @@
<h1 align="center">Fastify</h1>
## Reply
- [Reply](#reply)
- [Introduction](#introduction)
- [.code(statusCode)](#codestatuscode)
- [.elapsedTime](#elapsedtime)
- [.statusCode](#statuscode)
- [.server](#server)
- [.header(key, value)](#headerkey-value)
- [.headers(object)](#headersobject)
- [.getHeader(key)](#getheaderkey)
- [.getHeaders()](#getheaders)
- [.removeHeader(key)](#removeheaderkey)
- [.hasHeader(key)](#hasheaderkey)
- [.writeEarlyHints(hints, callback)](#writeearlyhintshints-callback)
- [.trailer(key, function)](#trailerkey-function)
- [.hasTrailer(key)](#hastrailerkey)
- [.removeTrailer(key)](#removetrailerkey)
- [.redirect(dest, [code ,])](#redirectdest--code)
- [.callNotFound()](#callnotfound)
- [.type(contentType)](#typecontenttype)
- [.getSerializationFunction(schema | httpStatus, [contentType])](#getserializationfunctionschema--httpstatus)
- [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschemaschema-httpstatus)
- [.serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])](#serializeinputdata-schema--httpstatus-httpstatus)
- [.serializer(func)](#serializerfunc)
- [.raw](#raw)
- [.sent](#sent)
- [.hijack()](#hijack)
- [.send(data)](#senddata)
- [Objects](#objects)
- [Strings](#strings)
- [Streams](#streams)
- [Buffers](#buffers)
- [TypedArrays](#typedarrays)
- [ReadableStream](#readablestream)
- [Response](#response)
- [Errors](#errors)
- [Type of the final payload](#type-of-the-final-payload)
- [Async-Await and Promises](#async-await-and-promises)
- [.then(fulfilled, rejected)](#thenfulfilled-rejected)
### Introduction
<a id="introduction"></a>
The second parameter of the handler function is `Reply`. Reply is a core Fastify
object that exposes the following functions and properties:
- `.code(statusCode)` - Sets the status code.
- `.status(statusCode)` - An alias for `.code(statusCode)`.
- `.statusCode` - Read and set the HTTP status code.
- `.elapsedTime` - Returns the amount of time passed
since the request was received by Fastify.
- `.server` - A reference to the fastify instance object.
- `.header(name, value)` - Sets a response header.
- `.headers(object)` - Sets all the keys of the object as response headers.
- `.getHeader(name)` - Retrieve value of already set header.
- `.getHeaders()` - Gets a shallow copy of all current response headers.
- `.removeHeader(key)` - Remove the value of a previously set header.
- `.hasHeader(name)` - Determine if a header has been set.
- `.writeEarlyHints(hints, callback)` - Sends early hints to the user
while the response is being prepared.
- `.trailer(key, function)` - Sets a response trailer.
- `.hasTrailer(key)` - Determine if a trailer has been set.
- `.removeTrailer(key)` - Remove the value of a previously set trailer.
- `.type(value)` - Sets the header `Content-Type`.
- `.redirect(dest, [code,])` - Redirect to the specified URL, the status code is
optional (defaults to `302`).
- `.callNotFound()` - Invokes the custom not found handler.
- `.serialize(payload)` - Serializes the specified payload using the default
JSON serializer or using the custom serializer (if one is set) and returns the
serialized payload.
- `.getSerializationFunction(schema | httpStatus, [contentType])` - Returns the serialization
function for the specified schema or http status, if any of either are set.
- `.compileSerializationSchema(schema, [httpStatus], [contentType])` - Compiles
the specified schema and returns a serialization function using the default
(or customized) `SerializerCompiler`. The optional `httpStatus` is forwarded
to the `SerializerCompiler` if provided, default to `undefined`.
- `.serializeInput(data, schema, [,httpStatus], [contentType])` - Serializes
the specified data using the specified schema and returns the serialized payload.
If the optional `httpStatus`, and `contentType` are provided, the function
will use the serializer function given for that specific content type and
HTTP Status Code. Default to `undefined`.
- `.serializer(function)` - Sets a custom serializer for the payload.
- `.send(payload)` - Sends the payload to the user, could be a plain text, a
buffer, JSON, stream, or an Error object.
- `.sent` - A boolean value that you can use if you need to know if `send` has
already been called.
- `.hijack()` - interrupt the normal request lifecycle.
- `.raw` - The
[`http.ServerResponse`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_class_http_serverresponse)
from Node core.
- `.log` - The logger instance of the incoming request.
- `.request` - The incoming request.
```js
fastify.get('/', options, function (request, reply) {
// Your code
reply
.code(200)
.header('Content-Type', 'application/json; charset=utf-8')
.send({ hello: 'world' })
})
```
### .code(statusCode)
<a id="code"></a>
If not set via `reply.code`, the resulting `statusCode` will be `200`.
### .elapsedTime
<a id="elapsedTime"></a>
Invokes the custom response time getter to calculate the amount of time passed
since the request was received by Fastify.
```js
const milliseconds = reply.elapsedTime
```
### .statusCode
<a id="statusCode"></a>
This property reads and sets the HTTP status code. It is an alias for
`reply.code()` when used as a setter.
```js
if (reply.statusCode >= 299) {
reply.statusCode = 500
}
```
### .server
<a id="server"></a>
The Fastify server instance, scoped to the current [encapsulation
context](./Encapsulation.md).
```js
fastify.decorate('util', function util () {
return 'foo'
})
fastify.get('/', async function (req, rep) {
return rep.server.util() // foo
})
```
### .header(key, value)
<a id="header"></a>
Sets a response header. If the value is omitted or undefined, it is coerced to
`''`.
> Note: The header's value must be properly encoded using
> [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
> or similar modules such as
> [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid characters
> will result in a 500 `TypeError` response.
For more information, see
[`http.ServerResponse#setHeader`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_response_setheader_name_value).
- ### set-cookie
<a id="set-cookie"></a>
- When sending different values as a cookie with `set-cookie` as the key,
every value will be sent as a cookie instead of replacing the previous
value.
```js
reply.header('set-cookie', 'foo');
reply.header('set-cookie', 'bar');
```
- The browser will only consider the latest reference of a key for the
`set-cookie` header. This is done to avoid parsing the `set-cookie` header
when added to a reply and speeds up the serialization of the reply.
- To reset the `set-cookie` header, you need to make an explicit call to
`reply.removeHeader('set-cookie')`, read more about `.removeHeader(key)`
[here](#removeheaderkey).
### .headers(object)
<a id="headers"></a>
Sets all the keys of the object as response headers.
[`.header`](#headerkey-value) will be called under the hood.
```js
reply.headers({
'x-foo': 'foo',
'x-bar': 'bar'
})
```
### .getHeader(key)
<a id="getHeader"></a>
Retrieves the value of a previously set header.
```js
reply.header('x-foo', 'foo') // setHeader: key, value
reply.getHeader('x-foo') // 'foo'
```
### .getHeaders()
<a id="getHeaders"></a>
Gets a shallow copy of all current response headers, including those set via the
raw `http.ServerResponse`. Note that headers set via Fastify take precedence
over those set via `http.ServerResponse`.
```js
reply.header('x-foo', 'foo')
reply.header('x-bar', 'bar')
reply.raw.setHeader('x-foo', 'foo2')
reply.getHeaders() // { 'x-foo': 'foo', 'x-bar': 'bar' }
```
### .removeHeader(key)
<a id="getHeader"></a>
Remove the value of a previously set header.
```js
reply.header('x-foo', 'foo')
reply.removeHeader('x-foo')
reply.getHeader('x-foo') // undefined
```
### .hasHeader(key)
<a id="hasHeader"></a>
Returns a boolean indicating if the specified header has been set.
### .writeEarlyHints(hints, callback)
<a id="writeEarlyHints"></a>
Sends early hints to the client. Early hints allow the client to
start processing resources before the final response is sent.
This can improve performance by allowing the client to preload
or preconnect to resources while the server is still generating the response.
The hints parameter is an object containing the early hint key-value pairs.
Example:
```js
reply.writeEarlyHints({
Link: '</styles.css>; rel=preload; as=style'
});
```
The optional callback parameter is a function that will be called
once the hint is sent or if an error occurs.
### .trailer(key, function)
<a id="trailer"></a>
Sets a response trailer. Trailer is usually used when you need a header that
requires heavy resources to be sent after the `data`, for example,
`Server-Timing` and `Etag`. It can ensure the client receives the response data
as soon as possible.
> Note: The header `Transfer-Encoding: chunked` will be added once you use
> the trailer. It is a hard requirement for using trailer in Node.js.
> Note: Any error passed to `done` callback will be ignored. If you interested
> in the error, you can turn on `debug` level logging.*
```js
reply.trailer('server-timing', function() {
return 'db;dur=53, app;dur=47.2'
})
const { createHash } = require('node:crypto')
// trailer function also receive two argument
// @param {object} reply fastify reply
// @param {string|Buffer|null} payload payload that already sent, note that it will be null when stream is sent
// @param {function} done callback to set trailer value
reply.trailer('content-md5', function(reply, payload, done) {
const hash = createHash('md5')
hash.update(payload)
done(null, hash.digest('hex'))
})
// when you prefer async-await
reply.trailer('content-md5', async function(reply, payload) {
const hash = createHash('md5')
hash.update(payload)
return hash.digest('hex')
})
```
### .hasTrailer(key)
<a id="hasTrailer"></a>
Returns a boolean indicating if the specified trailer has been set.
### .removeTrailer(key)
<a id="removeTrailer"></a>
Remove the value of a previously set trailer.
```js
reply.trailer('server-timing', function() {
return 'db;dur=53, app;dur=47.2'
})
reply.removeTrailer('server-timing')
reply.getTrailer('server-timing') // undefined
```
### .redirect(dest, [code ,])
<a id="redirect"></a>
Redirects a request to the specified URL, the status code is optional, default
to `302` (if status code is not already set by calling `code`).
> Note: The input URL must be properly encoded using
> [`encodeURI`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI)
> or similar modules such as
> [`encodeurl`](https://www.npmjs.com/package/encodeurl). Invalid URLs will
> result in a 500 `TypeError` response.
Example (no `reply.code()` call) sets status code to `302` and redirects to
`/home`
```js
reply.redirect('/home')
```
Example (no `reply.code()` call) sets status code to `303` and redirects to
`/home`
```js
reply.redirect('/home', 303)
```
Example (`reply.code()` call) sets status code to `303` and redirects to `/home`
```js
reply.code(303).redirect('/home')
```
Example (`reply.code()` call) sets status code to `302` and redirects to `/home`
```js
reply.code(303).redirect('/home', 302)
```
### .callNotFound()
<a id="call-not-found"></a>
Invokes the custom not found handler. Note that it will only call `preHandler`
hook specified in [`setNotFoundHandler`](./Server.md#set-not-found-handler).
```js
reply.callNotFound()
```
### .type(contentType)
<a id="type"></a>
Sets the content type for the response. This is a shortcut for
`reply.header('Content-Type', 'the/type')`.
```js
reply.type('text/html')
```
If the `Content-Type` has a JSON subtype, and the charset parameter is not set,
`utf-8` will be used as the charset by default. For other content types, the
charset must be set explicitly.
### .getSerializationFunction(schema | httpStatus, [contentType])
<a id="getserializationfunction"></a>
By calling this function using a provided `schema` or `httpStatus`,
and the optional `contentType`, it will return a `serialzation` function
that can be used to serialize diverse inputs. It returns `undefined` if no
serialization function was found using either of the provided inputs.
This heavily depends of the `schema#responses` attached to the route, or
the serialization functions compiled by using `compileSerializationSchema`.
```js
const serialize = reply
.getSerializationFunction({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
// or
const serialize = reply
.getSerializationFunction(200)
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
// or
const serialize = reply
.getSerializationFunction(200, 'application/json')
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
```
See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
for more information on how to compile serialization schemas.
### .compileSerializationSchema(schema, [httpStatus], [contentType])
<a id="compileserializationschema"></a>
This function will compile a serialization schema and
return a function that can be used to serialize data.
The function returned (a.k.a. _serialization function_) returned is compiled
by using the provided `SerializerCompiler`. Also this is cached by using
a `WeakMap` for reducing compilation calls.
The optional parameters `httpStatus` and `contentType`, if provided,
are forwarded directly to the `SerializerCompiler`, so it can be used
to compile the serialization function if a custom `SerializerCompiler` is used.
This heavily depends of the `schema#responses` attached to the route, or
the serialization functions compiled by using `compileSerializationSchema`.
```js
const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
// or
const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200)
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
// or
const serialize = reply
.compileSerializationSchema({
'3xx': {
content: {
'application/json': {
schema: {
name: { type: 'string' },
phone: { type: 'number' }
}
}
}
}
}, '3xx', 'application/json')
serialize({ name: 'Jone', phone: 201090909090 }) // '{"name":"Jone", "phone":201090909090}'
```
Note that you should be careful when using this function, as it will cache
the compiled serialization functions based on the schema provided. If the
schemas provided is mutated or changed, the serialization functions will not
detect that the schema has been altered and for instance it will reuse the
previously compiled serialization function based on the reference of the schema
previously provided.
If there's a need to change the properties of a schema, always opt to create
a totally new object, otherwise the implementation won't benefit from the cache
mechanism.
:Using the following schema as example:
```js
const schema1 = {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}
```
*Not*
```js
const serialize = reply.compileSerializationSchema(schema1)
// Later on...
schema1.properties.foo.type. = 'integer'
const newSerialize = reply.compileSerializationSchema(schema1)
console.log(newSerialize === serialize) // true
```
*Instead*
```js
const serialize = reply.compileSerializationSchema(schema1)
// Later on...
const newSchema = Object.assign({}, schema1)
newSchema.properties.foo.type = 'integer'
const newSerialize = reply.compileSerializationSchema(newSchema)
console.log(newSerialize === serialize) // false
```
### .serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])
<a id="serializeinput"></a>
This function will serialize the input data based on the provided schema
or HTTP status code. If both are provided the `httpStatus` will take precedence.
If there is not a serialization function for a given `schema` a new serialization
function will be compiled, forwarding the `httpStatus` and `contentType` if provided.
```js
reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}) // '{"foo":"bar"}'
// or
reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200) // '{"foo":"bar"}'
// or
reply
.serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'
// or
reply
.serializeInput({ name: 'Jone', age: 18 }, '200', 'application/vnd.v1+json') // '{"name": "Jone", "age": 18}'
```
See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
for more information on how to compile serialization schemas.
### .serializer(func)
<a id="serializer"></a>
By default, `.send()` will JSON-serialize any value that is not one of `Buffer`,
`stream`, `string`, `undefined`, or `Error`. If you need to replace the default
serializer with a custom serializer for a particular request, you can do so with
the `.serializer()` utility. Be aware that if you are using a custom serializer,
you must set a custom `'Content-Type'` header.
```js
reply
.header('Content-Type', 'application/x-protobuf')
.serializer(protoBuf.serialize)
```
Note that you don't need to use this utility inside a `handler` because Buffers,
streams, and strings (unless a serializer is set) are considered to already be
serialized.
```js
reply
.header('Content-Type', 'application/x-protobuf')
.send(protoBuf.serialize(data))
```
See [`.send()`](#send) for more information on sending different types of
values.
### .raw
<a id="raw"></a>
This is the
[`http.ServerResponse`](https://nodejs.org/dist/latest-v20.x/docs/api/http.html#http_class_http_serverresponse)
from Node core. Whilst you are using the Fastify `Reply` object, the use of
`Reply.raw` functions is at your own risk as you are skipping all the Fastify
logic of handling the HTTP response. e.g.:
```js
app.get('/cookie-2', (req, reply) => {
reply.setCookie('session', 'value', { secure: false }) // this will not be used
// in this case we are using only the nodejs http server response object
reply.raw.writeHead(200, { 'Content-Type': 'text/plain' })
reply.raw.write('ok')
reply.raw.end()
})
```
Another example of the misuse of `Reply.raw` is explained in
[Reply](#getheaders).
### .sent
<a id="sent"></a>
As the name suggests, `.sent` is a property to indicate if a response has been
sent via `reply.send()`. It will also be `true` in case `reply.hijack()` was
used.
In case a route handler is defined as an async function or it returns a promise,
it is possible to call `reply.hijack()` to indicate that the automatic
invocation of `reply.send()` once the handler promise resolve should be skipped.
By calling `reply.hijack()`, an application claims full responsibility for the
low-level request and response. Moreover, hooks will not be invoked.
*Modifying the `.sent` property directly is deprecated. Please use the
aforementioned `.hijack()` method to achieve the same effect.*
### .hijack()
<a name="hijack"></a>
Sometimes you might need to halt the execution of the normal request lifecycle
and handle sending the response manually.
To achieve this, Fastify provides the `reply.hijack()` method that can be called
during the request lifecycle (At any point before `reply.send()` is called), and
allows you to prevent Fastify from sending the response, and from running the
remaining hooks (and user handler if the reply was hijacked before).
```js
app.get('/', (req, reply) => {
reply.hijack()
reply.raw.end('hello world')
return Promise.resolve('this will be skipped')
})
```
If `reply.raw` is used to send a response back to the user, the `onResponse`
hooks will still be executed.
### .send(data)
<a id="send"></a>
As the name suggests, `.send()` is the function that sends the payload to the
end user.
#### Objects
<a id="send-object"></a>
As noted above, if you are sending JSON objects, `send` will serialize the
object with
[fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) if you
set an output schema, otherwise, `JSON.stringify()` will be used.
```js
fastify.get('/json', options, function (request, reply) {
reply.send({ hello: 'world' })
})
```
#### Strings
<a id="send-string"></a>
If you pass a string to `send` without a `Content-Type`, it will be sent as
`text/plain; charset=utf-8`. If you set the `Content-Type` header and pass a
string to `send`, it will be serialized with the custom serializer if one is
set, otherwise, it will be sent unmodified (unless the `Content-Type` header is
set to `application/json; charset=utf-8`, in which case it will be
JSON-serialized like an object — see the section above).
```js
fastify.get('/json', options, function (request, reply) {
reply.send('plain string')
})
```
#### Streams
<a id="send-streams"></a>
If you are sending a stream and you have not set a `'Content-Type'` header,
*send* will set it to `'application/octet-stream'`.
As noted above, streams are considered to be pre-serialized, so they will be
sent unmodified without response validation.
```js
const fs = require('node:fs')
fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
reply.send(stream)
})
```
When using async-await you will need to return or await the reply object:
```js
const fs = require('node:fs')
fastify.get('/streams', async function (request, reply) {
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
return reply.send(stream)
})
```
#### Buffers
<a id="send-buffers"></a>
If you are sending a buffer and you have not set a `'Content-Type'` header,
*send* will set it to `'application/octet-stream'`.
As noted above, Buffers are considered to be pre-serialized, so they will be
sent unmodified without response validation.
```js
const fs = require('node:fs')
fastify.get('/streams', function (request, reply) {
fs.readFile('some-file', (err, fileBuffer) => {
reply.send(err || fileBuffer)
})
})
```
When using async-await you will need to return or await the reply object:
```js
const fs = require('node:fs')
fastify.get('/streams', async function (request, reply) {
fs.readFile('some-file', (err, fileBuffer) => {
reply.send(err || fileBuffer)
})
return reply
})
```
#### TypedArrays
<a id="send-typedarrays"></a>
`send` manages TypedArray like a Buffer, and sets the `'Content-Type'`
header to `'application/octet-stream'` if not already set.
As noted above, TypedArray/Buffers are considered to be pre-serialized, so they
will be sent unmodified without response validation.
```js
const fs = require('node:fs')
fastify.get('/streams', function (request, reply) {
const typedArray = new Uint16Array(10)
reply.send(typedArray)
})
```
#### ReadableStream
<a id="send-readablestream"></a>
`ReadableStream` will be treated as a node stream mentioned above,
the content is considered to be pre-serialized, so they will be
sent unmodified without response validation.
```js
const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')
fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
reply.header('Content-Type', 'application/octet-stream')
reply.send(ReadableStream.from(stream))
})
```
#### Response
<a id="send-response"></a>
`Response` allows to manage the reply payload, status code and
headers in one place. The payload provided inside `Response` is
considered to be pre-serialized, so they will be sent unmodified
without response validation.
Please be aware when using `Response`, the status code and headers
will not directly reflect to `reply.statusCode` and `reply.getHeaders()`.
Such behavior is based on `Response` only allow `readonly` status
code and headers. The data is not allow to be bi-direction editing,
and may confuse when checking the `payload` in `onSend` hooks.
```js
const fs = require('node:fs')
const { ReadableStream } = require('node:stream/web')
fastify.get('/streams', function (request, reply) {
const stream = fs.createReadStream('some-file')
const readableStream = ReadableStream.from(stream)
const response = new Response(readableStream, {
status: 200,
headers: { 'content-type': 'application/octet-stream' }
})
reply.send(response)
})
```
#### Errors
<a id="errors"></a>
If you pass to *send* an object that is an instance of *Error*, Fastify will
automatically create an error structured as the following:
```js
{
error: String // the HTTP error message
code: String // the Fastify error code
message: String // the user error message
statusCode: Number // the HTTP status code
}
```
You can add custom properties to the Error object, such as `headers`, that will
be used to enhance the HTTP response.
> Note: If you are passing an error to `send` and the statusCode is less than
> 400, Fastify will automatically set it at 500.
Tip: you can simplify errors by using the
[`http-errors`](https://npm.im/http-errors) module or
[`@fastify/sensible`](https://github.com/fastify/fastify-sensible) plugin to
generate errors:
```js
fastify.get('/', function (request, reply) {
reply.send(httpErrors.Gone())
})
```
To customize the JSON error output you can do it by:
- setting a response JSON schema for the status code you need
- add the additional properties to the `Error` instance
Notice that if the returned status code is not in the response schema list, the
default behavior will be applied.
```js
fastify.get('/', {
schema: {
response: {
501: {
type: 'object',
properties: {
statusCode: { type: 'number' },
code: { type: 'string' },
error: { type: 'string' },
message: { type: 'string' },
time: { type: 'string' }
}
}
}
}
}, function (request, reply) {
const error = new Error('This endpoint has not been implemented')
error.time = 'it will be implemented in two weeks'
reply.code(501).send(error)
})
```
If you want to customize error handling, check out
[`setErrorHandler`](./Server.md#seterrorhandler) API.
> Note: you are responsible for logging when customizing the error handler.
API:
```js
fastify.setErrorHandler(function (error, request, reply) {
request.log.warn(error)
const statusCode = error.statusCode >= 400 ? error.statusCode : 500
reply
.code(statusCode)
.type('text/plain')
.send(statusCode >= 500 ? 'Internal server error' : error.message)
})
```
Beware that calling `reply.send(error)` in your custom error handler will send
the error to the default error handler.
Check out the [Reply Lifecycle](./Lifecycle.md#reply-lifecycle)
for more information.
The not found errors generated by the router will use the
[`setNotFoundHandler`](./Server.md#setnotfoundhandler)
API:
```js
fastify.setNotFoundHandler(function (request, reply) {
reply
.code(404)
.type('text/plain')
.send('a custom not found')
})
```
#### Type of the final payload
<a id="payload-type"></a>
The type of the sent payload (after serialization and going through any
[`onSend` hooks](./Hooks.md#onsend)) must be one of the following types,
otherwise, an error will be thrown:
- `string`
- `Buffer`
- `stream`
- `undefined`
- `null`
#### Async-Await and Promises
<a id="async-await-promise"></a>
Fastify natively handles promises and supports async-await.
*Note that in the following examples we are not using reply.send.*
```js
const { promisify } = require('node:util')
const delay = promisify(setTimeout)
fastify.get('/promises', options, function (request, reply) {
return delay(200).then(() => { return { hello: 'world' }})
})
fastify.get('/async-await', options, async function (request, reply) {
await delay(200)
return { hello: 'world' }
})
```
Rejected promises default to a `500` HTTP status code. Reject the promise, or
`throw` in an `async function`, with an object that has `statusCode` (or
`status`) and `message` properties to modify the reply.
```js
fastify.get('/teapot', async function (request, reply) {
const err = new Error()
err.statusCode = 418
err.message = 'short and stout'
throw err
})
fastify.get('/botnet', async function (request, reply) {
throw { statusCode: 418, message: 'short and stout' }
// will return to the client the same json
})
```
If you want to know more please review
[Routes#async-await](./Routes.md#async-await).
### .then(fulfilled, rejected)
<a id="then"></a>
As the name suggests, a `Reply` object can be awaited upon, i.e. `await reply`
will wait until the reply is sent. The `await` syntax calls the `reply.then()`.
`reply.then(fulfilled, rejected)` accepts two parameters:
- `fulfilled` will be called when a response has been fully sent,
- `rejected` will be called if the underlying stream had an error, e.g. the
socket has been destroyed.
For more details, see:
- https://github.com/fastify/fastify/issues/1864 for the discussion about this
feature
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
for the signature

279
node_modules/fastify/docs/Reference/Request.md generated vendored Normal file
View File

@@ -0,0 +1,279 @@
<h1 align="center">Fastify</h1>
## Request
The first parameter of the handler function is `Request`.
Request is a core Fastify object containing the following fields:
- `query` - The parsed querystring, its format is specified by
[`querystringParser`](./Server.md#querystringparser).
- `body` - The request payload, see [Content-Type Parser](./ContentTypeParser.md)
for details on what request payloads Fastify natively parses and how to support
other content types.
- `params` - The params matching the URL.
- [`headers`](#headers) - The headers getter and setter.
- `raw` - The incoming HTTP request from Node core.
- `server` - The Fastify server instance, scoped to the current
[encapsulation context](./Encapsulation.md).
- `id` - The request ID.
- `log` - The logger instance of the incoming request.
- `ip` - The IP address of the incoming request.
- `ips` - An array of the IP addresses, ordered from closest to furthest, in the
`X-Forwarded-For` header of the incoming request (only when the
[`trustProxy`](./Server.md#factory-trust-proxy) option is enabled).
- `host` - The host of the incoming request (derived from `X-Forwarded-Host`
header when the [`trustProxy`](./Server.md#factory-trust-proxy) option is
enabled). For HTTP/2 compatibility, it returns `:authority` if no host header
exists. The host header may return an empty string if `requireHostHeader` is
`false`, not provided with HTTP/1.0, or removed by schema validation.
- `hostname` - The hostname derived from the `host` property of the incoming request.
- `port` - The port from the `host` property, which may refer to the port the
server is listening on.
- `protocol` - The protocol of the incoming request (`https` or `http`).
- `method` - The method of the incoming request.
- `url` - The URL of the incoming request.
- `originalUrl` - Similar to `url`, allows access to the original `url` in
case of internal re-routing.
- `is404` - `true` if request is being handled by 404 handler, `false` otherwise.
- `socket` - The underlying connection of the incoming request.
- `context` - Deprecated, use `request.routeOptions.config` instead. A Fastify
internal object. Do not use or modify it directly. It is useful to access one
special key:
- `context.config` - The route [`config`](./Routes.md#routes-config) object.
- `routeOptions` - The route [`option`](./Routes.md#routes-options) object.
- `bodyLimit` - Either server limit or route limit.
- `config` - The [`config`](./Routes.md#routes-config) object for this route.
- `method` - The HTTP method for the route.
- `url` - The path of the URL to match this route.
- `handler` - The handler for this route.
- `attachValidation` - Attach `validationError` to request (if there is
a schema defined).
- `logLevel` - Log level defined for this route.
- `schema` - The JSON schemas definition for this route.
- `version` - A semver compatible string that defines the version of the endpoint.
- `exposeHeadRoute` - Creates a sibling HEAD route for any GET routes.
- `prefixTrailingSlash` - String used to determine how to handle passing `/`
as a route with a prefix.
- [.getValidationFunction(schema | httpPart)](#getvalidationfunction) -
Returns a validation function for the specified schema or HTTP part, if
set or cached.
- [.compileValidationSchema(schema, [httpPart])](#compilevalidationschema) -
Compiles the specified schema and returns a validation function using the
default (or customized) `ValidationCompiler`. The optional `httpPart` is
forwarded to the `ValidationCompiler` if provided, defaults to `null`.
- [.validateInput(data, schema | httpPart, [httpPart])](#validate) -
Validates the input using the specified schema and returns the serialized
payload. If `httpPart` is provided, the function uses the serializer for
that HTTP Status Code. Defaults to `null`.
### Headers
The `request.headers` is a getter that returns an object with the headers of the
incoming request. Set custom headers as follows:
```js
request.headers = {
'foo': 'bar',
'baz': 'qux'
}
```
This operation adds new values to the request headers, accessible via
`request.headers.bar`. Standard request headers remain accessible via
`request.raw.headers`.
For performance reasons, `Symbol('fastify.RequestAcceptVersion')` may be added
to headers on `not found` routes.
> Note: Schema validation may mutate the `request.headers` and
> `request.raw.headers` objects, causing the headers to become empty.
```js
fastify.post('/:params', options, function (request, reply) {
console.log(request.body)
console.log(request.query)
console.log(request.params)
console.log(request.headers)
console.log(request.raw)
console.log(request.server)
console.log(request.id)
console.log(request.ip)
console.log(request.ips)
console.log(request.host)
console.log(request.hostname)
console.log(request.port)
console.log(request.protocol)
console.log(request.url)
console.log(request.routeOptions.method)
console.log(request.routeOptions.bodyLimit)
console.log(request.routeOptions.method)
console.log(request.routeOptions.url)
console.log(request.routeOptions.attachValidation)
console.log(request.routeOptions.logLevel)
console.log(request.routeOptions.version)
console.log(request.routeOptions.exposeHeadRoute)
console.log(request.routeOptions.prefixTrailingSlash)
console.log(request.routeOptions.logLevel)
request.log.info('some info')
})
```
### .getValidationFunction(schema | httpPart)
<a id="getvalidationfunction"></a>
By calling this function with a provided `schema` or `httpPart`, it returns a
`validation` function to validate diverse inputs. It returns `undefined` if no
serialization function is found using the provided inputs.
This function has an `errors` property. Errors encountered during the last
validation are assigned to `errors`.
```js
const validate = request
.getValidationFunction({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null
// or
const validate = request
.getValidationFunction('body')
console.log(validate({ foo: 0.5 })) // false
console.log(validate.errors) // validation errors
```
See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on compiling validation schemas.
### .compileValidationSchema(schema, [httpPart])
<a id="compilevalidationschema"></a>
This function compiles a validation schema and returns a function to validate data.
The returned function (a.k.a. _validation function_) is compiled using the provided
[`SchemaController#ValidationCompiler`](./Server.md#schema-controller). A `WeakMap`
is used to cache this, reducing compilation calls.
The optional parameter `httpPart`, if provided, is forwarded to the
`ValidationCompiler`, allowing it to compile the validation function if a custom
`ValidationCompiler` is provided for the route.
This function has an `errors` property. Errors encountered during the last
validation are assigned to `errors`.
```js
const validate = request
.compileValidationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null
// or
const validate = request
.compileValidationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200)
console.log(validate({ hello: 'world' })) // false
console.log(validate.errors) // validation errors
```
Be careful when using this function, as it caches compiled validation functions
based on the provided schema. If schemas are mutated or changed, the validation
functions will not detect the alterations and will reuse the previously compiled
validation function, as the cache is based on the schema's reference.
If schema properties need to be changed, create a new schema object to benefit
from the cache mechanism.
Using the following schema as an example:
```js
const schema1 = {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}
```
*Not*
```js
const validate = request.compileValidationSchema(schema1)
// Later on...
schema1.properties.foo.type. = 'integer'
const newValidate = request.compileValidationSchema(schema1)
console.log(newValidate === validate) // true
```
*Instead*
```js
const validate = request.compileValidationSchema(schema1)
// Later on...
const newSchema = Object.assign({}, schema1)
newSchema.properties.foo.type = 'integer'
const newValidate = request.compileValidationSchema(newSchema)
console.log(newValidate === validate) // false
```
### .validateInput(data, [schema | httpPart], [httpPart])
<a id="validate"></a>
This function validates the input based on the provided schema or HTTP part. If
both are provided, the `httpPart` parameter takes precedence.
If no validation function exists for a given `schema`, a new validation function
will be compiled, forwarding the `httpPart` if provided.
```js
request
.validateInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}) // true
// or
request
.validateInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 'body') // true
// or
request
.validateInput({ hello: 'world'}, 'query') // false
```
See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on compiling validation schemas.

793
node_modules/fastify/docs/Reference/Routes.md generated vendored Normal file
View File

@@ -0,0 +1,793 @@
<h1 align="center">Fastify</h1>
## Routes
The route methods configure the endpoints of the application. Routes can be
declared using the shorthand method or the full declaration.
- [Full declaration](#full-declaration)
- [Routes options](#routes-options)
- [Shorthand declaration](#shorthand-declaration)
- [Url building](#url-building)
- [Async Await](#async-await)
- [Promise resolution](#promise-resolution)
- [Route Prefixing](#route-prefixing)
- [Handling of / route inside prefixed
plugins](#handling-of--route-inside-prefixed-plugins)
- [Custom Log Level](#custom-log-level)
- [Custom Log Serializer](#custom-log-serializer)
- [Config](#config)
- [Constraints](#constraints)
- [Version Constraints](#version-constraints)
- [Host Constraints](#host-constraints)
### Full declaration
<a id="full-declaration"></a>
```js
fastify.route(options)
```
### Routes options
<a id="options"></a>
* `method`: currently it supports `GET`, `HEAD`, `TRACE`, `DELETE`,
`OPTIONS`, `PATCH`, `PUT` and `POST`. To accept more methods,
the [`addHttpMethod`](./Server.md#addHttpMethod) must be used.
It could also be an array of methods.
* `url`: the path of the URL to match this route (alias: `path`).
* `schema`: an object containing the schemas for the request and response. They
need to be in [JSON Schema](https://json-schema.org/) format, check
[here](./Validation-and-Serialization.md) for more info.
* `body`: validates the body of the request if it is a POST, PUT, PATCH,
TRACE, SEARCH, PROPFIND, PROPPATCH or LOCK method.
* `querystring` or `query`: validates the querystring. This can be a complete
JSON Schema object, with the property `type` of `object` and `properties`
object of parameters, or simply the values of what would be contained in the
`properties` object as shown below.
* `params`: validates the params.
* `response`: filter and generate a schema for the response, setting a schema
allows us to have 10-20% more throughput.
* `exposeHeadRoute`: creates a sibling `HEAD` route for any `GET` routes.
Defaults to the value of [`exposeHeadRoutes`](./Server.md#exposeHeadRoutes)
instance option. If you want a custom `HEAD` handler without disabling this
option, make sure to define it before the `GET` route.
* `attachValidation`: attach `validationError` to request, if there is a schema
validation error, instead of sending the error to the error handler. The
default [error format](https://ajv.js.org/api.html#error-objects) is the Ajv
one.
* `onRequest(request, reply, done)`: a [function](./Hooks.md#onrequest) called
as soon as a request is received, it could also be an array of functions.
* `preParsing(request, reply, payload, done)`: a
[function](./Hooks.md#preparsing) called before parsing the request, it could
also be an array of functions.
* `preValidation(request, reply, done)`: a [function](./Hooks.md#prevalidation)
called after the shared `preValidation` hooks, useful if you need to perform
authentication at route level for example, it could also be an array of
functions.
* `preHandler(request, reply, done)`: a [function](./Hooks.md#prehandler) called
just before the request handler, it could also be an array of functions.
* `preSerialization(request, reply, payload, done)`: a
[function](./Hooks.md#preserialization) called just before the serialization,
it could also be an array of functions.
* `onSend(request, reply, payload, done)`: a [function](./Hooks.md#route-hooks)
called right before a response is sent, it could also be an array of
functions.
* `onResponse(request, reply, done)`: a [function](./Hooks.md#onresponse) called
when a response has been sent, so you will not be able to send more data to
the client. It could also be an array of functions.
* `onTimeout(request, reply, done)`: a [function](./Hooks.md#ontimeout) called
when a request is timed out and the HTTP socket has been hung up.
* `onError(request, reply, error, done)`: a [function](./Hooks.md#onerror)
called when an Error is thrown or sent to the client by the route handler.
* `handler(request, reply)`: the function that will handle this request. The
[Fastify server](./Server.md) will be bound to `this` when the handler is
called. Note: using an arrow function will break the binding of `this`.
* `errorHandler(error, request, reply)`: a custom error handler for the scope of
the request. Overrides the default error global handler, and anything set by
[`setErrorHandler`](./Server.md#seterrorhandler), for requests to the route.
To access the default handler, you can access `instance.errorHandler`. Note
that this will point to fastify's default `errorHandler` only if a plugin
hasn't overridden it already.
* `childLoggerFactory(logger, binding, opts, rawReq)`: a custom factory function
that will be called to produce a child logger instance for every request.
See [`childLoggerFactory`](./Server.md#childloggerfactory) for more info.
Overrides the default logger factory, and anything set by
[`setChildLoggerFactory`](./Server.md#setchildloggerfactory), for requests to
the route. To access the default factory, you can access
`instance.childLoggerFactory`. Note that this will point to Fastify's default
`childLoggerFactory` only if a plugin hasn't overridden it already.
* `validatorCompiler({ schema, method, url, httpPart })`: function that builds
schemas for request validations. See the [Validation and
Serialization](./Validation-and-Serialization.md#schema-validator)
documentation.
* `serializerCompiler({ { schema, method, url, httpStatus, contentType } })`:
function that builds schemas for response serialization. See the [Validation and
Serialization](./Validation-and-Serialization.md#schema-serializer)
documentation.
* `schemaErrorFormatter(errors, dataVar)`: function that formats the errors from
the validation compiler. See the [Validation and
Serialization](./Validation-and-Serialization.md#error-handling)
documentation. Overrides the global schema error formatter handler, and
anything set by `setSchemaErrorFormatter`, for requests to the route.
* `bodyLimit`: prevents the default JSON body parser from parsing request bodies
larger than this number of bytes. Must be an integer. You may also set this
option globally when first creating the Fastify instance with
`fastify(options)`. Defaults to `1048576` (1 MiB).
* `logLevel`: set log level for this route. See below.
* `logSerializers`: set serializers to log for this route.
* `config`: object used to store custom configuration.
* `version`: a [semver](https://semver.org/) compatible string that defined the
version of the endpoint. [Example](#version-constraints).
* `constraints`: defines route restrictions based on request properties or
values, enabling customized matching using
[find-my-way](https://github.com/delvedor/find-my-way) constraints. Includes
built-in `version` and `host` constraints, with support for custom constraint
strategies.
* `prefixTrailingSlash`: string used to determine how to handle passing `/` as a
route with a prefix.
* `both` (default): Will register both `/prefix` and `/prefix/`.
* `slash`: Will register only `/prefix/`.
* `no-slash`: Will register only `/prefix`.
Note: this option does not override `ignoreTrailingSlash` in
[Server](./Server.md) configuration.
* `request` is defined in [Request](./Request.md).
* `reply` is defined in [Reply](./Reply.md).
> Note: The documentation for `onRequest`, `preParsing`, `preValidation`,
> `preHandler`, `preSerialization`, `onSend`, and `onResponse` is detailed in
> [Hooks](./Hooks.md). To send a response before the request is handled by the
> `handler`, see [Respond to a request from
> a hook](./Hooks.md#respond-to-a-request-from-a-hook).
Example:
```js
fastify.route({
method: 'GET',
url: '/',
schema: {
querystring: {
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
},
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
```
### Shorthand declaration
<a id="shorthand-declaration"></a>
The above route declaration is more *Hapi*-like, but if you prefer an
*Express/Restify* approach, we support it as well:
`fastify.get(path, [options], handler)`
`fastify.head(path, [options], handler)`
`fastify.post(path, [options], handler)`
`fastify.put(path, [options], handler)`
`fastify.delete(path, [options], handler)`
`fastify.options(path, [options], handler)`
`fastify.patch(path, [options], handler)`
Example:
```js
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
}
}
fastify.get('/', opts, (request, reply) => {
reply.send({ hello: 'world' })
})
```
`fastify.all(path, [options], handler)` will add the same handler to all the
supported methods.
The handler may also be supplied via the `options` object:
```js
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: { type: 'string' }
}
}
}
},
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
}
fastify.get('/', opts)
```
> Note: Specifying the handler in both `options` and as the third parameter to
> the shortcut method throws a duplicate `handler` error.
### Url building
<a id="url-building"></a>
Fastify supports both static and dynamic URLs.
To register a **parametric** path, use a *colon* before the parameter name. For
**wildcard**, use a *star*. Static routes are always checked before parametric
and wildcard routes.
```js
// parametric
fastify.get('/example/:userId', function (request, reply) {
// curl ${app-url}/example/12345
// userId === '12345'
const { userId } = request.params;
// your code here
})
fastify.get('/example/:userId/:secretToken', function (request, reply) {
// curl ${app-url}/example/12345/abc.zHi
// userId === '12345'
// secretToken === 'abc.zHi'
const { userId, secretToken } = request.params;
// your code here
})
// wildcard
fastify.get('/example/*', function (request, reply) {})
```
Regular expression routes are supported, but slashes must be escaped.
Take note that RegExp is also very expensive in terms of performance!
```js
// parametric with regexp
fastify.get('/example/:file(^\\d+).png', function (request, reply) {
// curl ${app-url}/example/12345.png
// file === '12345'
const { file } = request.params;
// your code here
})
```
It is possible to define more than one parameter within the same couple of slash
("/"). Such as:
```js
fastify.get('/example/near/:lat-:lng/radius/:r', function (request, reply) {
// curl ${app-url}/example/near/15°N-30°E/radius/20
// lat === "15°N"
// lng === "30°E"
// r ==="20"
const { lat, lng, r } = request.params;
// your code here
})
```
*Remember in this case to use the dash ("-") as parameters separator.*
Finally, it is possible to have multiple parameters with RegExp:
```js
fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, reply) {
// curl ${app-url}/example/at/08h24m
// hour === "08"
// minute === "24"
const { hour, minute } = request.params;
// your code here
})
```
In this case as parameter separator it is possible to use whatever character is
not matched by the regular expression.
The last parameter can be made optional by adding a question mark ("?") to the
end of the parameter name.
```js
fastify.get('/example/posts/:id?', function (request, reply) {
const { id } = request.params;
// your code here
})
```
In this case, `/example/posts` and `/example/posts/1` are both valid. The
optional param will be `undefined` if not specified.
Having a route with multiple parameters may negatively affect performance.
Prefer a single parameter approach, especially on routes that are on the hot
path of your application. For more details, see
[find-my-way](https://github.com/delvedor/find-my-way).
To include a colon in a path without declaring a parameter, use a double colon.
For example:
```js
fastify.post('/name::verb') // will be interpreted as /name:verb
```
### Async Await
<a id="async-await"></a>
Are you an `async/await` user? We have you covered!
```js
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return processed
})
```
As shown, `reply.send` is not called to send data back to the user. Simply
return the body and you are done!
If needed, you can also send data back with `reply.send`. In this case, do not
forget to `return reply` or `await reply` in your `async` handler to avoid race
conditions.
```js
fastify.get('/', options, async function (request, reply) {
const data = await getData()
const processed = await processData(data)
return reply.send(processed)
})
```
If the route is wrapping a callback-based API that will call `reply.send()`
outside of the promise chain, it is possible to `await reply`:
```js
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
await reply
})
```
Returning reply also works:
```js
fastify.get('/', options, async function (request, reply) {
setImmediate(() => {
reply.send({ hello: 'world' })
})
return reply
})
```
> ⚠ Warning:
> * When using both `return value` and `reply.send(value)`, the first one takes
> precedence, the second is discarded, and a *warn* log is emitted.
> * Calling `reply.send()` outside of the promise is possible but requires special
> attention. See [promise-resolution](#promise-resolution).
> * `undefined` cannot be returned. See [promise-resolution](#promise-resolution).
### Promise resolution
<a id="promise-resolution"></a>
If the handler is an `async` function or returns a promise, be aware of the
special behavior to support callback and promise control-flow. When the
handler's promise resolves, the reply is automatically sent with its value
unless you explicitly await or return `reply` in the handler.
1. If using `async/await` or promises but responding with `reply.send`:
- **Do** `return reply` / `await reply`.
- **Do not** forget to call `reply.send`.
2. If using `async/await` or promises:
- **Do not** use `reply.send`.
- **Do** return the value to send.
This approach supports both `callback-style` and `async-await` with minimal
trade-off. However, it is recommended to use only one style for consistent
error handling within your application.
> Note: Every async function returns a promise by itself.
### Route Prefixing
<a id="route-prefixing"></a>
Sometimes maintaining multiple versions of the same API is necessary. A common
approach is to prefix routes with the API version number, e.g., `/v1/user`.
Fastify offers a fast and smart way to create different versions of the same API
without changing all the route names by hand, called *route prefixing*. Here is
how it works:
```js
// server.js
const fastify = require('fastify')()
fastify.register(require('./routes/v1/users'), { prefix: '/v1' })
fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
fastify.listen({ port: 3000 })
```
```js
// routes/v1/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v1)
done()
}
```
```js
// routes/v2/users.js
module.exports = function (fastify, opts, done) {
fastify.get('/user', handler_v2)
done()
}
```
Fastify will not complain about using the same name for two different routes
because it handles the prefix automatically at compilation time. This ensures
performance is not affected.
Now clients will have access to the following routes:
- `/v1/user`
- `/v2/user`
This can be done multiple times and works for nested `register`. Route
parameters are also supported.
To use a prefix for all routes, place them inside a plugin:
```js
const fastify = require('fastify')()
const route = {
method: 'POST',
url: '/login',
handler: () => {},
schema: {},
}
fastify.register(function (app, _, done) {
app.get('/users', () => {})
app.route(route)
done()
}, { prefix: '/v1' }) // global route prefix
await fastify.listen({ port: 3000 })
```
### Route Prefixing and fastify-plugin
<a id="fastify-plugin"></a>
If using [`fastify-plugin`](https://github.com/fastify/fastify-plugin) to wrap
routes, this option will not work. To make it work, wrap a plugin in a plugin:
```js
const fp = require('fastify-plugin')
const routes = require('./lib/routes')
module.exports = fp(async function (app, opts) {
app.register(routes, {
prefix: '/v1',
})
}, {
name: 'my-routes'
})
```
#### Handling of / route inside prefixed plugins
The `/` route behaves differently based on whether the prefix ends with `/`.
For example, with a prefix `/something/`, adding a `/` route matches only
`/something/`. With a prefix `/something`, adding a `/` route matches both
`/something` and `/something/`.
See the `prefixTrailingSlash` route option above to change this behavior.
### Custom Log Level
<a id="custom-log-level"></a>
Different log levels can be set for routes in Fastify by passing the `logLevel`
option to the plugin or route with the desired
[value](https://github.com/pinojs/pino/blob/master/docs/api.md#level-string).
Be aware that setting `logLevel` at the plugin level also affects
[`setNotFoundHandler`](./Server.md#setnotfoundhandler) and
[`setErrorHandler`](./Server.md#seterrorhandler).
```js
// server.js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), { logLevel: 'warn' })
fastify.register(require('./routes/events'), { logLevel: 'debug' })
fastify.listen({ port: 3000 })
```
Or pass it directly to a route:
```js
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
```
*Remember that the custom log level applies only to routes, not to the global
Fastify Logger, accessible with `fastify.log`.*
### Custom Log Serializer
<a id="custom-log-serializer"></a>
In some contexts, logging a large object may waste resources. Define custom
[`serializers`](https://github.com/pinojs/pino/blob/master/docs/api.md#serializers-object)
and attach them in the appropriate context.
```js
const fastify = require('fastify')({ logger: true })
fastify.register(require('./routes/user'), {
logSerializers: {
user: (value) => `My serializer one - ${value.name}`
}
})
fastify.register(require('./routes/events'), {
logSerializers: {
user: (value) => `My serializer two - ${value.name} ${value.surname}`
}
})
fastify.listen({ port: 3000 })
```
Serializers can be inherited by context:
```js
const fastify = Fastify({
logger: {
level: 'info',
serializers: {
user (req) {
return {
method: req.method,
url: req.url,
headers: req.headers,
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket.remotePort
}
}
}
}
})
fastify.register(context1, {
logSerializers: {
user: value => `My serializer father - ${value}`
}
})
async function context1 (fastify, opts) {
fastify.get('/', (req, reply) => {
req.log.info({ user: 'call father serializer', key: 'another key' })
// shows: { user: 'My serializer father - call father serializer', key: 'another key' }
reply.send({})
})
}
fastify.listen({ port: 3000 })
```
### Config
<a id="routes-config"></a>
Registering a new handler, you can pass a configuration object to it and
retrieve it in the handler.
```js
// server.js
const fastify = require('fastify')()
function handler (req, reply) {
reply.send(reply.routeOptions.config.output)
}
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
fastify.get('/it', { config: { output: 'ciao mondo!' } }, handler)
fastify.listen({ port: 3000 })
```
### Constraints
<a id="constraints"></a>
Fastify supports constraining routes to match certain requests based on
properties like the `Host` header or any other value via
[`find-my-way`](https://github.com/delvedor/find-my-way) constraints.
Constraints are specified in the `constraints` property of the route options.
Fastify has two built-in constraints: `version` and `host`. Custom constraint
strategies can be added to inspect other parts of a request to decide if a route
should be executed.
#### Version Constraints
You can provide a `version` key in the `constraints` option to a route.
Versioned routes allows multiple handlers to be declared for the same HTTP
route path, matched according to the request's `Accept-Version` header.
The `Accept-Version` header value should follow the
[semver](https://semver.org/) specification, and routes should be declared
with exact semver versions for matching.
Fastify will require a request `Accept-Version` header to be set if the route
has a version set, and will prefer a versioned route to a non-versioned route
for the same path. Advanced version ranges and pre-releases currently are not
supported.
> **Note:** using this feature can degrade the routers performance.
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { version: '1.2.0' },
handler: function (request, reply) {
reply.send({ hello: 'world' })
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Accept-Version': '1.x' // it could also be '1.2.0' or '1.2.x'
}
}, (err, res) => {
// { hello: 'world' }
})
```
> ⚠ Warning:
> Set a
> [`Vary`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary)
> header in responses with the value used for versioning
> (e.g., `'Accept-Version'`) to prevent cache poisoning attacks.
> This can also be configured in a Proxy/CDN.
>
> ```js
> const append = require('vary').append
> fastify.addHook('onSend', (req, reply, payload, done) => {
> if (req.headers['accept-version']) { // or the custom header being used
> let value = reply.getHeader('Vary') || ''
> const header = Array.isArray(value) ? value.join(', ') : String(value)
> if ((value = append(header, 'Accept-Version'))) { // or the custom header being used
> reply.header('Vary', value)
> }
> }
> done()
> })
> ```
If multiple versions with the same major or minor are declared, Fastify will
always choose the highest compatible with the `Accept-Version` header value.
If the request lacks an `Accept-Version` header, a 404 error will be returned.
Custom version matching logic can be defined through the
[`constraints`](./Server.md#constraints) configuration when creating a Fastify
server instance.
#### Host Constraints
Provide a `host` key in the `constraints` route option to limit the route to
certain values of the request `Host` header. `host` constraint values can be
specified as strings for exact matches or RegExps for arbitrary host matching.
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { host: 'auth.fastify.dev' },
handler: function (request, reply) {
reply.send('hello world from auth.fastify.dev')
}
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'example.com'
}
}, (err, res) => {
// 404 because the host doesn't match the constraint
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'Host': 'auth.fastify.dev'
}
}, (err, res) => {
// => 'hello world from auth.fastify.dev'
})
```
RegExp `host` constraints can also be specified allowing constraining to hosts
matching wildcard subdomains (or any other pattern):
```js
fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.dev/ }, // will match any subdomain of fastify.dev
handler: function (request, reply) {
reply.send('hello world from ' + request.headers.host)
}
})
```
#### Asynchronous Custom Constraints
Custom constraints can be provided, and the `constraint` criteria can be
fetched from another source such as a database. Use asynchronous custom
constraints as a last resort, as they impact router performance.
```js
function databaseOperation(field, done) {
done(null, field)
}
const secret = {
// strategy name for referencing in the route handler `constraints` options
name: 'secret',
// storage factory for storing routes in the find-my-way route tree
storage: function () {
let handlers = {}
return {
get: (type) => { return handlers[type] || null },
set: (type, store) => { handlers[type] = store }
}
},
// function to get the value of the constraint from each incoming request
deriveConstraint: (req, ctx, done) => {
databaseOperation(req.headers['secret'], done)
},
// optional flag marking if handlers without constraints can match requests that have a value for this constraint
mustMatchWhenDerived: true
}
```
> ⚠ Warning:
> When using asynchronous constraints, avoid returning errors inside the
> callback. If errors are unavoidable, provide a custom `frameworkErrors`
> handler to manage them. Otherwise, route selection may break or expose
> sensitive information.
>
> ```js
> const Fastify = require('fastify')
>
> const fastify = Fastify({
> frameworkErrors: function (err, req, res) {
> if (err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
> res.code(400)
> return res.send("Invalid header provided")
> } else {
> res.send(err)
> }
> }
> })
> ```

2192
node_modules/fastify/docs/Reference/Server.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

256
node_modules/fastify/docs/Reference/Type-Providers.md generated vendored Normal file
View File

@@ -0,0 +1,256 @@
<h1 align="center">Fastify</h1>
## Type Providers
Type Providers are a TypeScript feature that enables Fastify to infer type
information from inline JSON Schema. They are an alternative to specifying
generic arguments on routes and can reduce the need to keep associated types for
each schema in a project.
### Providers
Official Type Provider packages follow the
`@fastify/type-provider-{provider-name}` naming convention.
Several community providers are also available.
The following inference packages are supported:
- [`json-schema-to-ts`](https://github.com/ThomasAribart/json-schema-to-ts)
- [`typebox`](https://github.com/sinclairzx81/typebox)
- [`zod`](https://github.com/colinhacks/zod)
See also the Type Provider wrapper packages for each of the packages respectively:
- [`@fastify/type-provider-json-schema-to-ts`](https://github.com/fastify/fastify-type-provider-json-schema-to-ts)
- [`@fastify/type-provider-typebox`](https://github.com/fastify/fastify-type-provider-typebox)
- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
(3rd party)
### Json Schema to Ts
The following sets up a `json-schema-to-ts` Type Provider:
```bash
$ npm i @fastify/type-provider-json-schema-to-ts
```
```typescript
import fastify from 'fastify'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
const server = fastify().withTypeProvider<JsonSchemaToTsProvider>()
server.get('/route', {
schema: {
querystring: {
type: 'object',
properties: {
foo: { type: 'number' },
bar: { type: 'string' },
},
required: ['foo', 'bar']
}
}
}, (request, reply) => {
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
```
### TypeBox
The following sets up a TypeBox Type Provider:
```bash
$ npm i @fastify/type-provider-typebox
```
```typescript
import fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'
const server = fastify().withTypeProvider<TypeBoxTypeProvider>()
server.get('/route', {
schema: {
querystring: Type.Object({
foo: Type.Number(),
bar: Type.String()
})
}
}, (request, reply) => {
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
```
See the [TypeBox
documentation](https://github.com/sinclairzx81/typebox#validation)
for setting up AJV to work with TypeBox.
### Zod
See [official documentation](https://github.com/turkerdev/fastify-type-provider-zod)
for Zod Type Provider instructions.
### Scoped Type-Provider
The provider types don't propagate globally. In encapsulated usage, one can
remap the context to use one or more providers (for example, `typebox` and
`json-schema-to-ts` can be used in the same application).
Example:
```ts
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
import { Type } from '@sinclair/typebox'
const fastify = Fastify()
function pluginWithTypebox(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<TypeBoxTypeProvider>()
.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}
function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<JsonSchemaToTsProvider>()
.get('/', {
schema: {
body: {
type: 'object',
properties: {
x: { type: 'string' },
y: { type: 'number' },
z: { type: 'boolean' }
},
}
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}
fastify.register(pluginWithJsonSchema)
fastify.register(pluginWithTypebox)
```
It is important to note that since the types do not propagate globally, it is
currently not possible to avoid multiple registrations on routes when dealing
with several scopes, as shown below:
```ts
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'
const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()
server.register(plugin1) // wrong
server.register(plugin2) // correct
function plugin1(fastify: FastifyInstance, _opts, done): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// In a new scope, call `withTypeProvider` again to ensure it works
const { x, y, z } = req.body
});
done()
}
function plugin2(fastify: FastifyInstance, _opts, done): void {
const server = fastify.withTypeProvider<TypeBoxTypeProvider>()
server.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
done()
}
```
### Type Definition of FastifyInstance + TypeProvider
When working with modules, use `FastifyInstance` with Type Provider generics.
See the example below:
```ts
// index.ts
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { registerRoutes } from './routes'
const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()
registerRoutes(server)
server.listen({ port: 3000 })
```
```ts
// routes.ts
import { Type } from '@sinclair/typebox'
import {
FastifyInstance,
FastifyBaseLogger,
RawReplyDefaultExpression,
RawRequestDefaultExpression,
RawServerDefault
} from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
type FastifyTypebox = FastifyInstance<
RawServerDefault,
RawRequestDefaultExpression<RawServerDefault>,
RawReplyDefaultExpression<RawServerDefault>,
FastifyBaseLogger,
TypeBoxTypeProvider
>;
export function registerRoutes(fastify: FastifyTypebox): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
}
```

1588
node_modules/fastify/docs/Reference/TypeScript.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

58
node_modules/fastify/docs/Reference/Warnings.md generated vendored Normal file
View File

@@ -0,0 +1,58 @@
<h1 align="center">Fastify</h1>
**Table of contents**
- [Warnings](#warnings)
- [Warnings In Fastify](#warnings-in-fastify)
- [Fastify Warning Codes](#fastify-warning-codes)
- [FSTWRN001](#FSTWRN001)
- [FSTWRN002](#FSTWRN002)
- [Fastify Deprecation Codes](#fastify-deprecation-codes)
- [FSTDEP022](#FSTDEP022)
## Warnings
### Warnings In Fastify
Fastify uses Node.js's [warning event](https://nodejs.org/api/process.html#event-warning)
API to notify users of deprecated features and coding mistakes. Fastify's
warnings are recognizable by the `FSTWRN` and `FSTDEP` prefixes. When
encountering such a warning, it is highly recommended to determine the cause
using the [`--trace-warnings`](https://nodejs.org/api/cli.html#--trace-warnings)
and [`--trace-deprecation`](https://nodejs.org/api/cli.html#--trace-deprecation)
flags. These produce stack traces pointing to where the issue occurs in the
application's code. Issues opened about warnings without this information will
be closed due to lack of details.
Warnings can also be disabled, though it is not recommended. If necessary, use
one of the following methods:
- Set the `NODE_NO_WARNINGS` environment variable to `1`
- Pass the `--no-warnings` flag to the node process
- Set `no-warnings` in the `NODE_OPTIONS` environment variable
For more information on disabling warnings, see [Node's documentation](https://nodejs.org/api/cli.html).
Disabling warnings may cause issues when upgrading Fastify versions. Only
experienced users should consider disabling warnings.
### Fastify Warning Codes
| Code | Description | How to solve | Discussion |
| ---- | ----------- | ------------ | ---------- |
| <a id="FSTWRN001">FSTWRN001</a> | The specified schema for a route is missing. This may indicate the schema is not well specified. | Check the schema for the route. | [#4647](https://github.com/fastify/fastify/pull/4647) |
| <a id="FSTWRN002">FSTWRN002</a> | The %s plugin being registered mixes async and callback styles, which will result in an error in `fastify@5`. | Do not mix async and callback style. | [#5139](https://github.com/fastify/fastify/pull/5139) |
### Fastify Deprecation Codes
Deprecation codes are supported by the Node.js CLI options:
- [--no-deprecation](https://nodejs.org/api/cli.html#--no-deprecation)
- [--throw-deprecation](https://nodejs.org/api/cli.html#--throw-deprecation)
- [--trace-deprecation](https://nodejs.org/api/cli.html#--trace-deprecation)
| Code | Description | How to solve | Discussion |
| ---- | ----------- | ------------ | ---------- |
| <a id="FSTDEP022">FSTDEP022</a> | You are trying to access the deprecated router options on top option properties. | Use `options.routerOptions`. | [#5985](https://github.com/fastify/fastify/pull/5985)

24
node_modules/fastify/docs/index.md generated vendored Normal file
View File

@@ -0,0 +1,24 @@
<h1 align="center">Fastify</h1>
The documentation for Fastify is split into two categories:
- [Reference documentation](./Reference/Index.md)
- [Guides](./Guides/Index.md)
The reference documentation utilizes a very formal style in an effort to document
Fastify's API and implementation details thoroughly for the developer who needs
such. The guides category utilizes an informal educational style as a means to
introduce newcomers to core and advanced Fastify concepts.
## Where To Start
Complete newcomers to Fastify should first read our [Getting
Started](./Guides/Getting-Started.md) guide.
Developers experienced with Fastify should consult the [reference
documentation](./Reference/Index.md) directly to find the topic they are seeking
more information about.
## Additional Documentation
- Fastify's [Long Term Support (LTS)](./Reference/LTS.md) policy

View File

@@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2020-12-06T18:51:58.018Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36" etag="vyaguDTT1c9e-NqGeV_7" version="13.10.9" type="device"><diagram id="hZ89Y7exsLGRT07QCK17" name="Page-1">7ZpPk5owGMY/jcd2SCKIx0q3todOO+thjzsRAmQ2Eopx1f30DRKUNLCrncXN6uKMA0/+EN5feJNndICCxWZa4Dz9ySPCBtCJNgP0dQDhaDiS36WwrQQfOJWQFDSqJHAQZvSJKLGutqIRWWoVBedM0FwXQ55lJBSahouCr/VqMWf6XXOcEEOYhZiZ6h2NRKqewnUO+ndCk7S+M3BUyQLXlZWwTHHE1w0J3QxQUHAuqrPFJiCsjF0dl6rdt47S/cAKkoljGjyhO8+Z3U9YQP3bP1PHvc9+fFK9PGK2Ug98Ww4IOgHPBNkINXSxreOxTqkgsxyH5fVaIh+gSSoWTF4BeYqXeUUhphsibzyJKWMBZ7zYNUexW36k/kgKQWWYvzCaZLJM8LKngq+yqGy362wpCv5AGq293VH2KkfX0KtD6mZM6geUtyObhqRiNCV8QUSxlVVU6VDh2uqX6wN8d6y0tAF+L2I14ZJ9zwcm8kRhOQER6kL0m60SmhmE5DzLy9O84CFZytFMXmA2x+FDsov8r5VgNCNK12H8CzKOYRi2QYq8ued6rwMDOq5Gwzdp+C0wUF8shtfMYmwXC/eKWSDfLhbATFJG/MtQ5sc/+35Jx/O6B+fZmABPz9wAmUFBXktU+guLZ0QlSKncgHQvr/rcemGCdk299rX1lAn5POSXp+nbhXzUGfI3TQsRJn7cmha80CfzuBcKdan+XrS8FmfNFf4HIZOQXYjGBiJp7rIo7DF57cN/ZPLqHwrUmdQrbjO1wRYoALp9LbKOnW8OAZFLRm2Qxt4I4X5WmNqtjT7rOyEA3norZFrqq6cEkH2Y4AcmI+MhaB0m03EbYM5tLFCL2zrzLheY5vddO4thByubnAXodnOXsnE9HoOd1gJcvvv7D0SWMTL93/s3FydTsc9cmJ7PilfnFfdDJ0Oyz1zAy7eApyc4+8wFvHwPeHrGO6e5kJeHH9Z3ZY1/J6Cbvw==</diagram></mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 20 KiB

19
node_modules/fastify/eslint.config.js generated vendored Normal file
View File

@@ -0,0 +1,19 @@
'use strict'
const neostandard = require('neostandard')
module.exports = [
...neostandard({
ignores: [
'lib/configValidator.js',
'lib/error-serializer.js',
'test/same-shape.test.js',
'test/types/import.js'
],
ts: true
}),
{
rules: {
'comma-dangle': ['error', 'never']
}
}
]

38
node_modules/fastify/examples/asyncawait.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
const schema = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
function result () {
return Promise.resolve({ hello: 'world' })
}
fastify
.get('/await', schema, async function (req, reply) {
reply.header('Content-Type', 'application/json').code(200)
return result()
})
.get('/', schema, async function (req, reply) {
reply.header('Content-Type', 'application/json').code(200)
return { hello: 'world' }
})
fastify.listen({ port: 3000 }, err => {
if (err) {
throw err
}
})

3
node_modules/fastify/examples/benchmark/body.json generated vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"hello": "world"
}

View File

@@ -0,0 +1,44 @@
'use strict'
const fastify = require('../../fastify')({ logger: false })
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
function promiseFunction (resolve) {
setImmediate(resolve)
}
async function asyncHook () {
await new Promise(promiseFunction)
}
fastify
.addHook('onRequest', asyncHook)
.addHook('onRequest', asyncHook)
.addHook('preHandler', asyncHook)
.addHook('preHandler', asyncHook)
.addHook('preHandler', asyncHook)
.addHook('onSend', asyncHook)
fastify.get('/', opts, function (request, reply) {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, function (err) {
if (err) {
throw err
}
})

View File

@@ -0,0 +1,52 @@
'use strict'
const fastify = require('../../fastify')({ logger: false })
const opts = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify
.addHook('onRequest', function (request, reply, done) {
done()
})
.addHook('onRequest', function (request, reply, done) {
done()
})
fastify
.addHook('preHandler', function (request, reply, done) {
done()
})
.addHook('preHandler', function (request, reply, done) {
setImmediate(done)
})
.addHook('preHandler', function (request, reply, done) {
done()
})
fastify
.addHook('onSend', function (request, reply, payload, done) {
done()
})
fastify.get('/', opts, function (request, reply) {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, function (err) {
if (err) {
throw err
}
})

47
node_modules/fastify/examples/benchmark/parser.js generated vendored Normal file
View File

@@ -0,0 +1,47 @@
'use strict'
const fastify = require('../../fastify')({
logger: false
})
const jsonParser = require('fast-json-body')
const querystring = require('node:querystring')
// Handled by fastify
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/json' http://localhost:3000/
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/jsoff' http://localhost:3000/
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
// curl -X POST -d 'hello=world' -H'Content-type: application/x-www-form-urlencoded' http://localhost:3000/
fastify.addContentTypeParser('application/x-www-form-urlencoded', function (request, payload, done) {
let body = ''
payload.on('data', function (data) {
body += data
})
payload.on('end', function () {
try {
const parsed = querystring.parse(body)
done(null, parsed)
} catch (e) {
done(e)
}
})
payload.on('error', done)
})
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/vnd.custom+json' http://localhost:3000/
fastify.addContentTypeParser(/^application\/.+\+json$/, { parseAs: 'string' }, fastify.getDefaultJsonParser('error', 'ignore'))
fastify
.post('/', function (req, reply) {
reply.send(req.body)
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
})

30
node_modules/fastify/examples/benchmark/simple.js generated vendored Normal file
View File

@@ -0,0 +1,30 @@
'use strict'
const fastify = require('../../fastify')({
logger: false
})
const schema = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify
.get('/', schema, function (req, reply) {
reply
.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) throw err
})

91
node_modules/fastify/examples/hooks.js generated vendored Normal file
View File

@@ -0,0 +1,91 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
const optsPost = {
schema: {
body: {
type: 'object',
required: ['hello'],
properties: {
hello: {
type: 'string'
}
}
},
response: opts.response
}
}
fastify
.addHook('onRequest', function (request, reply, done) {
console.log('onRequest')
done()
})
.addHook('preParsing', function (request, reply, payload, done) {
console.log('preParsing')
done()
})
.addHook('preValidation', function (request, reply, done) {
console.log('preValidation')
done()
})
.addHook('preHandler', function (request, reply, done) {
console.log('preHandler')
done()
})
.addHook('preSerialization', function (request, reply, payload, done) {
console.log('preSerialization', payload)
done()
})
.addHook('onError', function (request, reply, error, done) {
console.log('onError', error.message)
done()
})
.addHook('onSend', function (request, reply, payload, done) {
console.log('onSend', payload)
done()
})
.addHook('onResponse', function (request, reply, done) {
console.log('onResponse')
done()
})
.addHook('onRoute', function (routeOptions) {
console.log('onRoute')
})
.addHook('onListen', async function () {
console.log('onListen')
})
.addHook('onClose', function (instance, done) {
console.log('onClose')
done()
})
fastify.get('/', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.post('/', optsPost, function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, function (err) {
if (err) {
throw err
}
})

39
node_modules/fastify/examples/http2.js generated vendored Normal file
View File

@@ -0,0 +1,39 @@
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const fastify = require('../fastify')({
http2: true,
https: {
key: fs.readFileSync(path.join(__dirname, '../test/https/fastify.key')),
cert: fs.readFileSync(path.join(__dirname, '../test/https/fastify.cert'))
},
logger: true
})
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify
.get('/', opts, function (req, reply) {
reply.header('Content-Type', 'application/json').code(200)
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, err => {
if (err) {
throw err
}
})

38
node_modules/fastify/examples/https.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict'
const fs = require('node:fs')
const path = require('node:path')
const fastify = require('../fastify')({
https: {
key: fs.readFileSync(path.join(__dirname, '../test/https/fastify.key')),
cert: fs.readFileSync(path.join(__dirname, '../test/https/fastify.cert'))
},
logger: true
})
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify
.get('/', opts, function (req, reply) {
reply.header('Content-Type', 'application/json').code(200)
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, err => {
if (err) {
throw err
}
})

53
node_modules/fastify/examples/parser.js generated vendored Normal file
View File

@@ -0,0 +1,53 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
const jsonParser = require('fast-json-body')
const querystring = require('node:querystring')
// Handled by fastify
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/json' http://localhost:3000/
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/jsoff' http://localhost:3000/
fastify.addContentTypeParser('application/jsoff', function (request, payload, done) {
jsonParser(payload, function (err, body) {
done(err, body)
})
})
// curl -X POST -d 'hello=world' -H'Content-type: application/x-www-form-urlencoded' http://localhost:3000/
fastify.addContentTypeParser('application/x-www-form-urlencoded', function (request, payload, done) {
let body = ''
payload.on('data', function (data) {
body += data
})
payload.on('end', function () {
try {
const parsed = querystring.parse(body)
done(null, parsed)
} catch (e) {
done(e)
}
})
payload.on('error', done)
})
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/vnd.custom+json' http://localhost:3000/
fastify.addContentTypeParser(/^application\/.+\+json$/, { parseAs: 'string' }, fastify.getDefaultJsonParser('error', 'ignore'))
// remove default json parser
// curl -X POST -d '{"hello":"world"}' -H'Content-type: application/json' http://localhost:3000/ is now no longer handled by fastify
fastify.removeContentTypeParser('application/json')
// This call would remove any content type parser
// fastify.removeAllContentTypeParsers()
fastify
.post('/', function (req, reply) {
reply.send(req.body)
})
fastify.listen({ port: 3000 }, err => {
if (err) {
throw err
}
})

12
node_modules/fastify/examples/plugin.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
'use strict'
module.exports = function (fastify, opts, done) {
fastify
.get('/', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
.post('/', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
done()
}

38
node_modules/fastify/examples/route-prefix.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
greet: { type: 'string' }
}
}
}
}
}
fastify.register(function (instance, options, done) {
// the route will be '/english/hello'
instance.get('/hello', opts, (req, reply) => {
reply.send({ greet: 'hello' })
})
done()
}, { prefix: '/english' })
fastify.register(function (instance, options, done) {
// the route will be '/italian/hello'
instance.get('/hello', opts, (req, reply) => {
reply.send({ greet: 'ciao' })
})
done()
}, { prefix: '/italian' })
fastify.listen({ port: 8000 }, function (err) {
if (err) {
throw err
}
})

38
node_modules/fastify/examples/shared-schema.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
fastify.addSchema({
$id: 'https://foo/common.json',
definitions: {
response: {
$id: '#reply',
type: 'object',
properties: {
hello: {
$id: '#bar',
type: 'string'
}
}
}
}
})
const opts = {
schema: {
response: {
200: { $ref: 'https://foo/common.json#reply' }
}
}
}
fastify
.get('/', opts, function (req, reply) {
reply.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, err => {
if (err) {
throw err
}
})

20
node_modules/fastify/examples/simple-stream.js generated vendored Normal file
View File

@@ -0,0 +1,20 @@
'use strict'
const fastify = require('../fastify')({
logger: false
})
const Readable = require('node:stream').Readable
fastify
.get('/', function (req, reply) {
const stream = Readable.from(['hello world'])
reply.send(stream)
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) {
throw err
}
fastify.log.info(`server listening on ${address}`)
})

32
node_modules/fastify/examples/simple.js generated vendored Normal file
View File

@@ -0,0 +1,32 @@
'use strict'
const fastify = require('../fastify')({
logger: false
})
const schema = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify
.get('/', schema, function (req, reply) {
reply
.send({ hello: 'world' })
})
fastify.listen({ port: 3000 }, (err, address) => {
if (err) {
throw err
}
})

27
node_modules/fastify/examples/simple.mjs generated vendored Normal file
View File

@@ -0,0 +1,27 @@
// works on Node v14.13.0+
import { fastify } from '../fastify.js'
const app = fastify({
logger: true
})
const schema = {
schema: {
response: {
200: {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
app.get('/', schema, async function (req, reply) {
return { hello: 'world' }
})
app.listen({ port: 3000 }).catch(console.error)

79
node_modules/fastify/examples/typescript-server.ts generated vendored Normal file
View File

@@ -0,0 +1,79 @@
/**
* Most type annotations in this file are not strictly necessary but are
* included for this example.
*
* To run this example execute the following commands to install typescript,
* transpile the code, and start the server:
*
* npm i -g typescript
* tsc examples/typescript-server.ts --target es6 --module commonjs
* node examples/typescript-server.js
*/
import fastify, { FastifyInstance, RouteShorthandOptions } from '../fastify'
import { Server, IncomingMessage, ServerResponse } from 'node:http'
// Create an http server. We pass the relevant typings for our http version used.
// By passing types we get correctly typed access to the underlying http objects in routes.
// If using http2 we'd pass <http2.Http2Server, http2.Http2ServerRequest, http2.Http2ServerResponse>
const server: FastifyInstance<
Server,
IncomingMessage,
ServerResponse
> = fastify({ logger: true })
// Define interfaces for our request. We can create these automatically
// off our JSON Schema files (See TypeScript.md) but for the purpose of this
// example we manually define them.
interface PingQuerystring {
foo?: number;
}
interface PingParams {
bar?: string;
}
interface PingHeaders {
a?: string;
}
interface PingBody {
baz?: string;
}
// Define our route options with schema validation
const opts: RouteShorthandOptions = {
schema: {
body: {
type: 'object',
properties: {
pong: {
type: 'string'
}
}
}
}
}
// Add our route handler with correct types
server.post<{
Querystring: PingQuerystring;
Params: PingParams;
Headers: PingHeaders;
Body: PingBody;
}>('/ping/:bar', opts, (request, reply) => {
console.log(request.query) // this is of type `PingQuerystring`
console.log(request.params) // this is of type `PingParams`
console.log(request.headers) // this is of type `PingHeaders`
console.log(request.body) // this is of type `PingBody`
reply.code(200).send({ pong: 'it worked!' })
})
// Start your server
server.listen({ port: 8080 }, (err, address) => {
if (err) {
console.error(err)
process.exit(1)
}
console.log(`server listening on ${address}`)
})

29
node_modules/fastify/examples/use-plugin.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
'use strict'
const fastify = require('../fastify')({ logger: true })
const opts = {
schema: {
response: {
'2xx': {
type: 'object',
properties: {
hello: {
type: 'string'
}
}
}
}
}
}
fastify.register(require('./plugin'), opts, function (err) {
if (err) {
throw err
}
})
fastify.listen({ port: 3000 }, function (err) {
if (err) {
throw err
}
})

257
node_modules/fastify/fastify.d.ts generated vendored Normal file
View File

@@ -0,0 +1,257 @@
import * as http from 'node:http'
import * as http2 from 'node:http2'
import * as https from 'node:https'
import { Socket } from 'node:net'
import { Options as AjvOptions, ValidatorFactory } from '@fastify/ajv-compiler'
import { FastifyError } from '@fastify/error'
import { Options as FJSOptions, SerializerFactory } from '@fastify/fast-json-stringify-compiler'
import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
import { InjectOptions, CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'
import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, FastifyContentTypeParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction } from './types/content-type-parser'
import { FastifyContextConfig, FastifyReplyContext, FastifyRequestContext } from './types/context'
import { FastifyErrorCodes } from './types/errors'
import { DoneFuncWithErrOrRes, HookHandlerDoneFunction, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onListenAsyncHookHandler, onListenHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preCloseAsyncHookHandler, preCloseHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, RequestPayload } from './types/hooks'
import { FastifyInstance, FastifyListenOptions, PrintRoutesOptions } from './types/instance'
import {
FastifyBaseLogger,
FastifyChildLoggerFactory,
FastifyLogFn,
FastifyLoggerInstance,
FastifyLoggerOptions,
LogLevel,
PinoLoggerOptions
} from './types/logger'
import { FastifyPlugin, FastifyPluginAsync, FastifyPluginCallback, FastifyPluginOptions } from './types/plugin'
import { FastifyRegister, FastifyRegisterOptions, RegisterOptions } from './types/register'
import { FastifyReply } from './types/reply'
import { FastifyRequest, RequestGenericInterface } from './types/request'
import { RouteGenericInterface, RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
import { FastifySchema, FastifySchemaValidationError, FastifySchemaCompiler, FastifySerializerCompiler, SchemaErrorDataVar, SchemaErrorFormatter } from './types/schema'
import { FastifyServerFactory, FastifyServerFactoryHandler } from './types/serverFactory'
import { FastifyTypeProvider, FastifyTypeProviderDefault, SafePromiseLike } from './types/type-provider'
import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault, RequestBodyDefault, RequestHeadersDefault, RequestParamsDefault, RequestQuerystringDefault } from './types/utils'
declare module '@fastify/error' {
interface FastifyError {
validationContext?: SchemaErrorDataVar;
validation?: FastifySchemaValidationError[];
}
}
type Fastify = typeof fastify
declare namespace fastify {
export const errorCodes: FastifyErrorCodes
export type FastifyHttp2SecureOptions<
Server extends http2.Http2SecureServer,
Logger extends FastifyBaseLogger = FastifyBaseLogger
> = FastifyServerOptions<Server, Logger> & {
http2: true,
https: http2.SecureServerOptions,
http2SessionTimeout?: number
}
export type FastifyHttp2Options<
Server extends http2.Http2Server,
Logger extends FastifyBaseLogger = FastifyBaseLogger
> = FastifyServerOptions<Server, Logger> & {
http2: true,
http2SessionTimeout?: number
}
export type FastifyHttpsOptions<
Server extends https.Server,
Logger extends FastifyBaseLogger = FastifyBaseLogger
> = FastifyServerOptions<Server, Logger> & {
https: https.ServerOptions | null
}
export type FastifyHttpOptions<
Server extends http.Server,
Logger extends FastifyBaseLogger = FastifyBaseLogger
> = FastifyServerOptions<Server, Logger> & {
http?: http.ServerOptions | null
}
type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server ? HTTPVersion.V1 : HTTPVersion.V2
export interface ConnectionError extends Error {
code: string,
bytesParsed: number,
rawPacket: {
type: string,
data: number[]
}
}
type TrustProxyFunction = (address: string, hop: number) => boolean
export type FastifyRouterOptions<RawServer extends RawServerBase> = {
allowUnsafeRegex?: boolean,
buildPrettyMeta?: (route: { [k: string]: unknown, store: { [k: string]: unknown } }) => object,
caseSensitive?: boolean,
constraints?: {
[name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>,
},
defaultRoute?: (req: FastifyRequest, res: FastifyReply) => void,
ignoreDuplicateSlashes?: boolean,
ignoreTrailingSlash?: boolean,
maxParamLength?: number,
onBadUrl?: (path: string, req: FastifyRequest, res: FastifyReply) => void,
querystringParser?: (str: string) => { [key: string]: unknown },
useSemicolonDelimiter?: boolean,
}
/**
* Options for a fastify server instance. Utilizes conditional logic on the generic server parameter to enforce certain https and http2
*/
export type FastifyServerOptions<
RawServer extends RawServerBase = RawServerDefault,
Logger extends FastifyBaseLogger = FastifyBaseLogger
> = {
ignoreTrailingSlash?: boolean,
ignoreDuplicateSlashes?: boolean,
connectionTimeout?: number,
keepAliveTimeout?: number,
maxRequestsPerSocket?: number,
forceCloseConnections?: boolean | 'idle',
requestTimeout?: number,
pluginTimeout?: number,
bodyLimit?: number,
maxParamLength?: number,
disableRequestLogging?: boolean,
exposeHeadRoutes?: boolean,
onProtoPoisoning?: ProtoAction,
onConstructorPoisoning?: ConstructorAction,
logger?: boolean | FastifyLoggerOptions<RawServer> & PinoLoggerOptions,
loggerInstance?: Logger
serializerOpts?: FJSOptions | Record<string, unknown>,
serverFactory?: FastifyServerFactory<RawServer>,
caseSensitive?: boolean,
allowUnsafeRegex?: boolean,
requestIdHeader?: string | false,
requestIdLogLabel?: string;
useSemicolonDelimiter?: boolean,
genReqId?: (req: RawRequestDefaultExpression<RawServer>) => string,
trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
querystringParser?: (str: string) => { [key: string]: unknown },
constraints?: {
[name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>, unknown>,
},
schemaController?: {
bucket?: (parentSchemas?: unknown) => {
add(schema: unknown): FastifyInstance;
getSchema(schemaId: string): unknown;
getSchemas(): Record<string, unknown>;
};
compilersFactory?: {
buildValidator?: ValidatorFactory;
buildSerializer?: SerializerFactory;
};
};
return503OnClosing?: boolean,
ajv?: {
customOptions?: AjvOptions,
plugins?: (Function | [Function, unknown])[]
},
frameworkErrors?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema>(
error: FastifyError,
req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, FastifySchema, TypeProvider>,
res: FastifyReply<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, FastifyContextConfig, SchemaCompiler, TypeProvider>
) => void,
rewriteUrl?: (
// The RawRequestDefaultExpression, RawReplyDefaultExpression, and FastifyTypeProviderDefault parameters
// should be narrowed further but those generic parameters are not passed to this FastifyServerOptions type
this: FastifyInstance<RawServer, RawRequestDefaultExpression<RawServer>, RawReplyDefaultExpression<RawServer>, Logger, FastifyTypeProviderDefault>,
req: RawRequestDefaultExpression<RawServer>
) => string,
schemaErrorFormatter?: SchemaErrorFormatter,
/**
* listener to error events emitted by client connections
*/
clientErrorHandler?: (error: ConnectionError, socket: Socket) => void,
childLoggerFactory?: FastifyChildLoggerFactory,
allowErrorHandlerOverride?: boolean
routerOptions?: FastifyRouterOptions<RawServer>,
}
/**
* @deprecated use {@link FastifySchemaValidationError}
*/
export type ValidationResult = FastifySchemaValidationError
/* Export additional types */
export type {
LightMyRequestChain, InjectOptions, LightMyRequestResponse, LightMyRequestCallback, // 'light-my-request'
FastifyRequest, RequestGenericInterface, // './types/request'
FastifyReply, // './types/reply'
FastifyPluginCallback, FastifyPluginAsync, FastifyPluginOptions, FastifyPlugin, // './types/plugin'
FastifyListenOptions, FastifyInstance, PrintRoutesOptions, // './types/instance'
FastifyLoggerOptions, FastifyBaseLogger, FastifyLoggerInstance, FastifyLogFn, LogLevel, // './types/logger'
FastifyRequestContext, FastifyContextConfig, FastifyReplyContext, // './types/context'
RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler, RouteGenericInterface, // './types/route'
FastifyRegister, FastifyRegisterOptions, RegisterOptions, // './types/register'
FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction, // './types/content-type-parser'
FastifyError, // '@fastify/error'
FastifySchema, FastifySchemaValidationError, FastifySchemaCompiler, FastifySerializerCompiler, // './types/schema'
HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault, // './types/utils'
DoneFuncWithErrOrRes, HookHandlerDoneFunction, RequestPayload, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, onRequestAbortHookHandler, onRequestAbortAsyncHookHandler, preCloseAsyncHookHandler, preCloseHookHandler, // './types/hooks'
FastifyServerFactory, FastifyServerFactoryHandler, // './types/serverFactory'
FastifyTypeProvider, FastifyTypeProviderDefault, SafePromiseLike, // './types/type-provider'
FastifyErrorCodes // './types/errors'
}
// named export
// import { plugin } from 'plugin'
// const { plugin } = require('plugin')
export const fastify: Fastify
// default export
// import plugin from 'plugin'
export { fastify as default }
}
/**
* Fastify factory function for the standard fastify http, https, or http2 server instance.
*
* The default function utilizes http
*
* @param opts Fastify server options
* @returns Fastify server instance
*/
declare function fastify<
Server extends http2.Http2SecureServer,
Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
> (opts: fastify.FastifyHttp2SecureOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
declare function fastify<
Server extends http2.Http2Server,
Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
> (opts: fastify.FastifyHttp2Options<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
declare function fastify<
Server extends https.Server,
Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
> (opts: fastify.FastifyHttpsOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
declare function fastify<
Server extends http.Server,
Request extends RawRequestDefaultExpression<Server> = RawRequestDefaultExpression<Server>,
Reply extends RawReplyDefaultExpression<Server> = RawReplyDefaultExpression<Server>,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault
> (opts?: fastify.FastifyHttpOptions<Server, Logger>): FastifyInstance<Server, Request, Reply, Logger, TypeProvider> & SafePromiseLike<FastifyInstance<Server, Request, Reply, Logger, TypeProvider>>
// CJS export
// const fastify = require('fastify')
export = fastify

963
node_modules/fastify/fastify.js generated vendored Normal file
View File

@@ -0,0 +1,963 @@
'use strict'
const VERSION = '5.6.0'
const Avvio = require('avvio')
const http = require('node:http')
const diagnostics = require('node:diagnostics_channel')
let lightMyRequest
const {
kAvvioBoot,
kChildren,
kServerBindings,
kBodyLimit,
kSupportedHTTPMethods,
kRoutePrefix,
kLogLevel,
kLogSerializers,
kHooks,
kSchemaController,
kRequestAcceptVersion,
kReplySerializerDefault,
kContentTypeParser,
kReply,
kRequest,
kFourOhFour,
kState,
kOptions,
kPluginNameChain,
kSchemaErrorFormatter,
kErrorHandler,
kKeepAliveConnections,
kChildLoggerFactory,
kGenReqId,
kErrorHandlerAlreadySet
} = require('./lib/symbols.js')
const { createServer } = require('./lib/server')
const Reply = require('./lib/reply')
const Request = require('./lib/request')
const Context = require('./lib/context.js')
const decorator = require('./lib/decorate')
const ContentTypeParser = require('./lib/contentTypeParser')
const SchemaController = require('./lib/schema-controller')
const { Hooks, hookRunnerApplication, supportedHooks } = require('./lib/hooks')
const { createChildLogger, defaultChildLoggerFactory, createLogger } = require('./lib/logger-factory')
const pluginUtils = require('./lib/pluginUtils')
const { getGenReqId, reqIdGenFactory } = require('./lib/reqIdGenFactory')
const { buildRouting, validateBodyLimitOption, buildRouterOptions } = require('./lib/route')
const build404 = require('./lib/fourOhFour')
const getSecuredInitialConfig = require('./lib/initialConfigValidation')
const override = require('./lib/pluginOverride')
const noopSet = require('./lib/noop-set')
const {
appendStackTrace,
AVVIO_ERRORS_MAP,
...errorCodes
} = require('./lib/errors')
const PonyPromise = require('./lib/promise')
const { defaultInitOptions } = getSecuredInitialConfig
const {
FST_ERR_ASYNC_CONSTRAINT,
FST_ERR_BAD_URL,
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE,
FST_ERR_OPTIONS_NOT_OBJ,
FST_ERR_QSP_NOT_FN,
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN,
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ,
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR,
FST_ERR_INSTANCE_ALREADY_LISTENING,
FST_ERR_REOPENED_CLOSE_SERVER,
FST_ERR_ROUTE_REWRITE_NOT_STR,
FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN,
FST_ERR_ERROR_HANDLER_NOT_FN,
FST_ERR_ERROR_HANDLER_ALREADY_SET,
FST_ERR_ROUTE_METHOD_INVALID
} = errorCodes
const { buildErrorHandler } = require('./lib/error-handler.js')
const { FSTWRN004 } = require('./lib/warnings.js')
const initChannel = diagnostics.channel('fastify.initialization')
function defaultBuildPrettyMeta (route) {
// return a shallow copy of route's sanitized context
const cleanKeys = {}
const allowedProps = ['errorHandler', 'logLevel', 'logSerializers']
allowedProps.concat(supportedHooks).forEach(k => {
cleanKeys[k] = route.store[k]
})
return Object.assign({}, cleanKeys)
}
/**
* @param {import('./fastify.js').FastifyServerOptions} options
*/
function fastify (options) {
// Options validations
if (options && typeof options !== 'object') {
throw new FST_ERR_OPTIONS_NOT_OBJ()
} else {
// Shallow copy options object to prevent mutations outside of this function
options = Object.assign({}, options)
}
if (
(options.querystringParser && typeof options.querystringParser !== 'function') ||
(
options.routerOptions?.querystringParser &&
typeof options.routerOptions.querystringParser !== 'function'
)
) {
throw new FST_ERR_QSP_NOT_FN(typeof (options.querystringParser ?? options.routerOptions.querystringParser))
}
if (options.schemaController && options.schemaController.bucket && typeof options.schemaController.bucket !== 'function') {
throw new FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN(typeof options.schemaController.bucket)
}
validateBodyLimitOption(options.bodyLimit)
const requestIdHeader = typeof options.requestIdHeader === 'string' && options.requestIdHeader.length !== 0 ? options.requestIdHeader.toLowerCase() : (options.requestIdHeader === true && 'request-id')
const genReqId = reqIdGenFactory(requestIdHeader, options.genReqId)
const requestIdLogLabel = options.requestIdLogLabel || 'reqId'
const bodyLimit = options.bodyLimit || defaultInitOptions.bodyLimit
const disableRequestLogging = options.disableRequestLogging || false
const ajvOptions = Object.assign({
customOptions: {},
plugins: []
}, options.ajv)
const frameworkErrors = options.frameworkErrors
// Ajv options
if (!ajvOptions.customOptions || Object.prototype.toString.call(ajvOptions.customOptions) !== '[object Object]') {
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ(typeof ajvOptions.customOptions)
}
if (!ajvOptions.plugins || !Array.isArray(ajvOptions.plugins)) {
throw new FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR(typeof ajvOptions.plugins)
}
// Instance Fastify components
const { logger, hasLogger } = createLogger(options)
// Update the options with the fixed values
options.connectionTimeout = options.connectionTimeout || defaultInitOptions.connectionTimeout
options.keepAliveTimeout = options.keepAliveTimeout || defaultInitOptions.keepAliveTimeout
options.maxRequestsPerSocket = options.maxRequestsPerSocket || defaultInitOptions.maxRequestsPerSocket
options.requestTimeout = options.requestTimeout || defaultInitOptions.requestTimeout
options.logger = logger
options.requestIdHeader = requestIdHeader
options.requestIdLogLabel = requestIdLogLabel
options.disableRequestLogging = disableRequestLogging
options.ajv = ajvOptions
options.clientErrorHandler = options.clientErrorHandler || defaultClientErrorHandler
options.allowErrorHandlerOverride = options.allowErrorHandlerOverride ?? defaultInitOptions.allowErrorHandlerOverride
const initialConfig = getSecuredInitialConfig(options)
// exposeHeadRoutes have its default set from the validator
options.exposeHeadRoutes = initialConfig.exposeHeadRoutes
options.routerOptions = buildRouterOptions(options, {
defaultRoute,
onBadUrl,
ignoreTrailingSlash: defaultInitOptions.ignoreTrailingSlash,
ignoreDuplicateSlashes: defaultInitOptions.ignoreDuplicateSlashes,
maxParamLength: defaultInitOptions.maxParamLength,
allowUnsafeRegex: defaultInitOptions.allowUnsafeRegex,
buildPrettyMeta: defaultBuildPrettyMeta,
useSemicolonDelimiter: defaultInitOptions.useSemicolonDelimiter
})
// Default router
const router = buildRouting({
config: options.routerOptions
})
// 404 router, used for handling encapsulated 404 handlers
const fourOhFour = build404(options)
// HTTP server and its handler
const httpHandler = wrapRouting(router, options)
// we need to set this before calling createServer
options.http2SessionTimeout = initialConfig.http2SessionTimeout
const { server, listen } = createServer(options, httpHandler)
const serverHasCloseAllConnections = typeof server.closeAllConnections === 'function'
const serverHasCloseIdleConnections = typeof server.closeIdleConnections === 'function'
let forceCloseConnections = options.forceCloseConnections
if (forceCloseConnections === 'idle' && !serverHasCloseIdleConnections) {
throw new FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE()
} else if (typeof forceCloseConnections !== 'boolean') {
/* istanbul ignore next: only one branch can be valid in a given Node.js version */
forceCloseConnections = serverHasCloseIdleConnections ? 'idle' : false
}
const keepAliveConnections = !serverHasCloseAllConnections && forceCloseConnections === true ? new Set() : noopSet()
const setupResponseListeners = Reply.setupResponseListeners
const schemaController = SchemaController.buildSchemaController(null, options.schemaController)
// Public API
const fastify = {
// Fastify internals
[kState]: {
listening: false,
closing: false,
started: false,
ready: false,
booting: false,
aborted: false,
readyResolver: null
},
[kKeepAliveConnections]: keepAliveConnections,
[kSupportedHTTPMethods]: {
bodyless: new Set([
// Standard
'GET',
'HEAD',
'TRACE'
]),
bodywith: new Set([
// Standard
'DELETE',
'OPTIONS',
'PATCH',
'PUT',
'POST'
])
},
[kOptions]: options,
[kChildren]: [],
[kServerBindings]: [],
[kBodyLimit]: bodyLimit,
[kRoutePrefix]: '',
[kLogLevel]: '',
[kLogSerializers]: null,
[kHooks]: new Hooks(),
[kSchemaController]: schemaController,
[kSchemaErrorFormatter]: null,
[kErrorHandler]: buildErrorHandler(),
[kErrorHandlerAlreadySet]: false,
[kChildLoggerFactory]: defaultChildLoggerFactory,
[kReplySerializerDefault]: null,
[kContentTypeParser]: new ContentTypeParser(
bodyLimit,
(options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning),
(options.onConstructorPoisoning || defaultInitOptions.onConstructorPoisoning)
),
[kReply]: Reply.buildReply(Reply),
[kRequest]: Request.buildRequest(Request, options.trustProxy),
[kFourOhFour]: fourOhFour,
[pluginUtils.kRegisteredPlugins]: [],
[kPluginNameChain]: ['fastify'],
[kAvvioBoot]: null,
[kGenReqId]: genReqId,
// routing method
routing: httpHandler,
// routes shorthand methods
delete: function _delete (url, options, handler) {
return router.prepareRoute.call(this, { method: 'DELETE', url, options, handler })
},
get: function _get (url, options, handler) {
return router.prepareRoute.call(this, { method: 'GET', url, options, handler })
},
head: function _head (url, options, handler) {
return router.prepareRoute.call(this, { method: 'HEAD', url, options, handler })
},
trace: function _trace (url, options, handler) {
return router.prepareRoute.call(this, { method: 'TRACE', url, options, handler })
},
patch: function _patch (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PATCH', url, options, handler })
},
post: function _post (url, options, handler) {
return router.prepareRoute.call(this, { method: 'POST', url, options, handler })
},
put: function _put (url, options, handler) {
return router.prepareRoute.call(this, { method: 'PUT', url, options, handler })
},
options: function _options (url, options, handler) {
return router.prepareRoute.call(this, { method: 'OPTIONS', url, options, handler })
},
all: function _all (url, options, handler) {
return router.prepareRoute.call(this, { method: this.supportedMethods, url, options, handler })
},
// extended route
route: function _route (options) {
// we need the fastify object that we are producing so we apply a lazy loading of the function,
// otherwise we should bind it after the declaration
return router.route.call(this, { options })
},
hasRoute: function _route (options) {
return router.hasRoute.call(this, { options })
},
findRoute: function _findRoute (options) {
return router.findRoute(options)
},
// expose logger instance
log: logger,
// type provider
withTypeProvider,
// hooks
addHook,
// schemas
addSchema,
getSchema: schemaController.getSchema.bind(schemaController),
getSchemas: schemaController.getSchemas.bind(schemaController),
setValidatorCompiler,
setSerializerCompiler,
setSchemaController,
setReplySerializer,
setSchemaErrorFormatter,
// set generated request id
setGenReqId,
// custom parsers
addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,
getDefaultJsonParser: ContentTypeParser.defaultParsers.getDefaultJsonParser,
defaultTextParser: ContentTypeParser.defaultParsers.defaultTextParser,
removeContentTypeParser: ContentTypeParser.helpers.removeContentTypeParser,
removeAllContentTypeParsers: ContentTypeParser.helpers.removeAllContentTypeParsers,
// Fastify architecture methods (initialized by Avvio)
register: null,
after: null,
ready: null,
onClose: null,
close: null,
printPlugins: null,
hasPlugin: function (name) {
return this[pluginUtils.kRegisteredPlugins].includes(name) || this[kPluginNameChain].includes(name)
},
// http server
listen,
server,
addresses: function () {
/* istanbul ignore next */
const binded = this[kServerBindings].map(b => b.address())
binded.push(this.server.address())
return binded.filter(adr => adr)
},
// extend fastify objects
decorate: decorator.add,
hasDecorator: decorator.exist,
decorateReply: decorator.decorateReply,
decorateRequest: decorator.decorateRequest,
hasRequestDecorator: decorator.existRequest,
hasReplyDecorator: decorator.existReply,
getDecorator: decorator.getInstanceDecorator,
addHttpMethod,
// fake http injection
inject,
// pretty print of the registered routes
printRoutes,
// custom error handling
setNotFoundHandler,
setErrorHandler,
// child logger
setChildLoggerFactory,
// Set fastify initial configuration options read-only object
initialConfig,
// constraint strategies
addConstraintStrategy: router.addConstraintStrategy.bind(router),
hasConstraintStrategy: router.hasConstraintStrategy.bind(router)
}
Object.defineProperties(fastify, {
listeningOrigin: {
get () {
const address = this.addresses().slice(-1).pop()
/* ignore if windows: unix socket is not testable on Windows platform */
/* c8 ignore next 3 */
if (typeof address === 'string') {
return address
}
const host = address.family === 'IPv6' ? `[${address.address}]` : address.address
return `${this[kOptions].https ? 'https' : 'http'}://${host}:${address.port}`
}
},
pluginName: {
configurable: true,
get () {
if (this[kPluginNameChain].length > 1) {
return this[kPluginNameChain].join(' -> ')
}
return this[kPluginNameChain][0]
}
},
prefix: {
configurable: true,
get () { return this[kRoutePrefix] }
},
validatorCompiler: {
configurable: true,
get () { return this[kSchemaController].getValidatorCompiler() }
},
serializerCompiler: {
configurable: true,
get () { return this[kSchemaController].getSerializerCompiler() }
},
childLoggerFactory: {
configurable: true,
get () { return this[kChildLoggerFactory] }
},
version: {
configurable: true,
get () { return VERSION }
},
errorHandler: {
configurable: true,
get () {
return this[kErrorHandler].func
}
},
genReqId: {
configurable: true,
get () { return this[kGenReqId] }
},
supportedMethods: {
configurable: false,
get () {
return [
...this[kSupportedHTTPMethods].bodyless,
...this[kSupportedHTTPMethods].bodywith
]
}
}
})
if (options.schemaErrorFormatter) {
validateSchemaErrorFormatter(options.schemaErrorFormatter)
fastify[kSchemaErrorFormatter] = options.schemaErrorFormatter.bind(fastify)
}
// Install and configure Avvio
// Avvio will update the following Fastify methods:
// - register
// - after
// - ready
// - onClose
// - close
const avvioPluginTimeout = Number(options.pluginTimeout)
const avvio = Avvio(fastify, {
autostart: false,
timeout: isNaN(avvioPluginTimeout) === false ? avvioPluginTimeout : defaultInitOptions.pluginTimeout,
expose: {
use: 'register'
}
})
// Override to allow the plugin encapsulation
avvio.override = override
avvio.on('start', () => (fastify[kState].started = true))
fastify[kAvvioBoot] = fastify.ready // the avvio ready function
fastify.ready = ready // overwrite the avvio ready function
fastify.printPlugins = avvio.prettyPrint.bind(avvio)
// cache the closing value, since we are checking it in an hot path
avvio.once('preReady', () => {
fastify.onClose((instance, done) => {
fastify[kState].closing = true
router.closeRoutes()
hookRunnerApplication('preClose', fastify[kAvvioBoot], fastify, function () {
if (fastify[kState].listening) {
/* istanbul ignore next: Cannot test this without Node.js core support */
if (forceCloseConnections === 'idle') {
// Not needed in Node 19
instance.server.closeIdleConnections()
/* istanbul ignore next: Cannot test this without Node.js core support */
} else if (serverHasCloseAllConnections && forceCloseConnections) {
instance.server.closeAllConnections()
} else if (forceCloseConnections === true) {
for (const conn of fastify[kKeepAliveConnections]) {
// We must invoke the destroy method instead of merely unreffing
// the sockets. If we only unref, then the callback passed to
// `fastify.close` will never be invoked; nor will any of the
// registered `onClose` hooks.
conn.destroy()
fastify[kKeepAliveConnections].delete(conn)
}
}
}
// No new TCP connections are accepted.
// We must call close on the server even if we are not listening
// otherwise memory will be leaked.
// https://github.com/nodejs/node/issues/48604
if (!options.serverFactory || fastify[kState].listening) {
instance.server.close(function (err) {
/* c8 ignore next 6 */
if (err && err.code !== 'ERR_SERVER_NOT_RUNNING') {
done(null)
} else {
done()
}
})
} else {
process.nextTick(done, null)
}
})
})
})
// Create bad URL context
const onBadUrlContext = new Context({
server: fastify,
config: {}
})
// Set the default 404 handler
fastify.setNotFoundHandler()
fourOhFour.arrange404(fastify)
router.setup(options, {
avvio,
fourOhFour,
logger,
hasLogger,
setupResponseListeners,
throwIfAlreadyStarted,
keepAliveConnections
})
// Delay configuring clientError handler so that it can access fastify state.
server.on('clientError', options.clientErrorHandler.bind(fastify))
if (initChannel.hasSubscribers) {
initChannel.publish({ fastify })
}
// Older nodejs versions may not have asyncDispose
if ('asyncDispose' in Symbol) {
fastify[Symbol.asyncDispose] = function dispose () {
return fastify.close()
}
}
return fastify
function throwIfAlreadyStarted (msg) {
if (fastify[kState].started) throw new FST_ERR_INSTANCE_ALREADY_LISTENING(msg)
}
// HTTP injection handling
// If the server is not ready yet, this
// utility will automatically force it.
function inject (opts, cb) {
// lightMyRequest is dynamically loaded as it seems very expensive
// because of Ajv
if (lightMyRequest === undefined) {
lightMyRequest = require('light-my-request')
}
if (fastify[kState].started) {
if (fastify[kState].closing) {
// Force to return an error
const error = new FST_ERR_REOPENED_CLOSE_SERVER()
if (cb) {
cb(error)
return
} else {
return Promise.reject(error)
}
}
return lightMyRequest(httpHandler, opts, cb)
}
if (cb) {
this.ready(err => {
if (err) cb(err, null)
else lightMyRequest(httpHandler, opts, cb)
})
} else {
return lightMyRequest((req, res) => {
this.ready(function (err) {
if (err) {
res.emit('error', err)
return
}
httpHandler(req, res)
})
}, opts)
}
}
function ready (cb) {
if (this[kState].readyResolver !== null) {
if (cb != null) {
this[kState].readyResolver.promise.then(() => cb(null, fastify), cb)
return
}
return this[kState].readyResolver.promise
}
// run the hooks after returning the promise
process.nextTick(runHooks)
// Create a promise no matter what
// It will work as a barrier for all the .ready() calls (ensuring single hook execution)
// as well as a flow control mechanism to chain cbs and further
// promises
this[kState].readyResolver = PonyPromise.withResolvers()
if (!cb) {
return this[kState].readyResolver.promise
} else {
this[kState].readyResolver.promise.then(() => cb(null, fastify), cb)
}
function runHooks () {
// start loading
fastify[kAvvioBoot]((err, done) => {
if (err || fastify[kState].started || fastify[kState].ready || fastify[kState].booting) {
manageErr(err)
} else {
fastify[kState].booting = true
hookRunnerApplication('onReady', fastify[kAvvioBoot], fastify, manageErr)
}
done()
})
}
function manageErr (err) {
// If the error comes out of Avvio's Error codes
// We create a make and preserve the previous error
// as cause
err = err != null && AVVIO_ERRORS_MAP[err.code] != null
? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
: err
if (err) {
return fastify[kState].readyResolver.reject(err)
}
fastify[kState].readyResolver.resolve(fastify)
fastify[kState].booting = false
fastify[kState].ready = true
fastify[kState].readyResolver = null
}
}
// Used exclusively in TypeScript contexts to enable auto type inference from JSON schema.
function withTypeProvider () {
return this
}
// wrapper that we expose to the user for hooks handling
function addHook (name, fn) {
throwIfAlreadyStarted('Cannot call "addHook"!')
if (fn == null) {
throw new errorCodes.FST_ERR_HOOK_INVALID_HANDLER(name, fn)
}
if (name === 'onSend' || name === 'preSerialization' || name === 'onError' || name === 'preParsing') {
if (fn.constructor.name === 'AsyncFunction' && fn.length === 4) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else if (name === 'onReady' || name === 'onListen') {
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 0) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else if (name === 'onRequestAbort') {
if (fn.constructor.name === 'AsyncFunction' && fn.length !== 1) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else {
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
throw new errorCodes.FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
}
if (name === 'onClose') {
this.onClose(fn.bind(this))
} else if (name === 'onReady' || name === 'onListen' || name === 'onRoute') {
this[kHooks].add(name, fn)
} else {
this.after((err, done) => {
try {
_addHook.call(this, name, fn)
done(err)
} catch (err) {
done(err)
}
})
}
return this
function _addHook (name, fn) {
this[kHooks].add(name, fn)
this[kChildren].forEach(child => _addHook.call(child, name, fn))
}
}
// wrapper that we expose to the user for schemas handling
function addSchema (schema) {
throwIfAlreadyStarted('Cannot call "addSchema"!')
this[kSchemaController].add(schema)
this[kChildren].forEach(child => child.addSchema(schema))
return this
}
function defaultClientErrorHandler (err, socket) {
// In case of a connection reset, the socket has been destroyed and there is nothing that needs to be done.
// https://nodejs.org/api/http.html#http_event_clienterror
if (err.code === 'ECONNRESET' || socket.destroyed) {
return
}
let body, errorCode, errorStatus, errorLabel
if (err.code === 'ERR_HTTP_REQUEST_TIMEOUT') {
errorCode = '408'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Client Timeout","statusCode":408}`
errorLabel = 'timeout'
} else if (err.code === 'HPE_HEADER_OVERFLOW') {
errorCode = '431'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Exceeded maximum allowed HTTP header size","statusCode":431}`
errorLabel = 'header_overflow'
} else {
errorCode = '400'
errorStatus = http.STATUS_CODES[errorCode]
body = `{"error":"${errorStatus}","message":"Client Error","statusCode":400}`
errorLabel = 'error'
}
// Most devs do not know what to do with this error.
// In the vast majority of cases, it's a network error and/or some
// config issue on the load balancer side.
this.log.trace({ err }, `client ${errorLabel}`)
// Copying standard node behavior
// https://github.com/nodejs/node/blob/6ca23d7846cb47e84fd344543e394e50938540be/lib/_http_server.js#L666
// If the socket is not writable, there is no reason to try to send data.
if (socket.writable) {
socket.write(`HTTP/1.1 ${errorCode} ${errorStatus}\r\nContent-Length: ${body.length}\r\nContent-Type: application/json\r\n\r\n${body}`)
}
socket.destroy(err)
}
// If the router does not match any route, every request will land here
// req and res are Node.js core objects
function defaultRoute (req, res) {
if (req.headers['accept-version'] !== undefined) {
// we remove the accept-version header for performance result
// because we do not want to go through the constraint checking
// the usage of symbol here to prevent any collision on custom header name
req.headers[kRequestAcceptVersion] = req.headers['accept-version']
req.headers['accept-version'] = undefined
}
fourOhFour.router.lookup(req, res)
}
function onBadUrl (path, req, res) {
if (frameworkErrors) {
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
const reply = new Reply(res, request, childLogger)
if (disableRequestLogging === false) {
childLogger.info({ req: request }, 'incoming request')
}
return frameworkErrors(new FST_ERR_BAD_URL(path), request, reply)
}
const body = `{"error":"Bad Request","code":"FST_ERR_BAD_URL","message":"'${path}' is not a valid url component","statusCode":400}`
res.writeHead(400, {
'Content-Type': 'application/json',
'Content-Length': body.length
})
res.end(body)
}
function buildAsyncConstraintCallback (isAsync, req, res) {
if (isAsync === false) return undefined
return function onAsyncConstraintError (err) {
if (err) {
if (frameworkErrors) {
const id = getGenReqId(onBadUrlContext.server, req)
const childLogger = createChildLogger(onBadUrlContext, logger, req, id)
const request = new Request(id, null, req, null, childLogger, onBadUrlContext)
const reply = new Reply(res, request, childLogger)
if (disableRequestLogging === false) {
childLogger.info({ req: request }, 'incoming request')
}
return frameworkErrors(new FST_ERR_ASYNC_CONSTRAINT(), request, reply)
}
const body = '{"error":"Internal Server Error","message":"Unexpected error from async constraint","statusCode":500}'
res.writeHead(500, {
'Content-Type': 'application/json',
'Content-Length': body.length
})
res.end(body)
}
}
}
function setNotFoundHandler (opts, handler) {
throwIfAlreadyStarted('Cannot call "setNotFoundHandler"!')
fourOhFour.setNotFoundHandler.call(this, opts, handler, avvio, router.routeHandler)
return this
}
function setValidatorCompiler (validatorCompiler) {
throwIfAlreadyStarted('Cannot call "setValidatorCompiler"!')
this[kSchemaController].setValidatorCompiler(validatorCompiler)
return this
}
function setSchemaErrorFormatter (errorFormatter) {
throwIfAlreadyStarted('Cannot call "setSchemaErrorFormatter"!')
validateSchemaErrorFormatter(errorFormatter)
this[kSchemaErrorFormatter] = errorFormatter.bind(this)
return this
}
function setSerializerCompiler (serializerCompiler) {
throwIfAlreadyStarted('Cannot call "setSerializerCompiler"!')
this[kSchemaController].setSerializerCompiler(serializerCompiler)
return this
}
function setSchemaController (schemaControllerOpts) {
throwIfAlreadyStarted('Cannot call "setSchemaController"!')
const old = this[kSchemaController]
const schemaController = SchemaController.buildSchemaController(old, Object.assign({}, old.opts, schemaControllerOpts))
this[kSchemaController] = schemaController
this.getSchema = schemaController.getSchema.bind(schemaController)
this.getSchemas = schemaController.getSchemas.bind(schemaController)
return this
}
function setReplySerializer (replySerializer) {
throwIfAlreadyStarted('Cannot call "setReplySerializer"!')
this[kReplySerializerDefault] = replySerializer
return this
}
// wrapper that we expose to the user for configure the custom error handler
function setErrorHandler (func) {
throwIfAlreadyStarted('Cannot call "setErrorHandler"!')
if (typeof func !== 'function') {
throw new FST_ERR_ERROR_HANDLER_NOT_FN()
}
if (!options.allowErrorHandlerOverride && this[kErrorHandlerAlreadySet]) {
throw new FST_ERR_ERROR_HANDLER_ALREADY_SET()
} else if (this[kErrorHandlerAlreadySet]) {
FSTWRN004("To disable this behavior, set 'allowErrorHandlerOverride' to false or ignore this message. For more information, visit: https://fastify.dev/docs/latest/Reference/Server/#allowerrorhandleroverride")
}
this[kErrorHandlerAlreadySet] = true
this[kErrorHandler] = buildErrorHandler(this[kErrorHandler], func.bind(this))
return this
}
function setChildLoggerFactory (factory) {
throwIfAlreadyStarted('Cannot call "setChildLoggerFactory"!')
this[kChildLoggerFactory] = factory
return this
}
function printRoutes (opts = {}) {
// includeHooks:true - shortcut to include all supported hooks exported by fastify.Hooks
opts.includeMeta = opts.includeHooks ? opts.includeMeta ? supportedHooks.concat(opts.includeMeta) : supportedHooks : opts.includeMeta
return router.printRoutes(opts)
}
function wrapRouting (router, { rewriteUrl, logger }) {
let isAsync
return function preRouting (req, res) {
// only call isAsyncConstraint once
if (isAsync === undefined) isAsync = router.isAsyncConstraint()
if (rewriteUrl) {
req.originalUrl = req.url
const url = rewriteUrl.call(fastify, req)
if (typeof url === 'string') {
req.url = url
} else {
const err = new FST_ERR_ROUTE_REWRITE_NOT_STR(req.url, typeof url)
req.destroy(err)
}
}
router.routing(req, res, buildAsyncConstraintCallback(isAsync, req, res))
}
}
function setGenReqId (func) {
throwIfAlreadyStarted('Cannot call "setGenReqId"!')
this[kGenReqId] = reqIdGenFactory(this[kOptions].requestIdHeader, func)
return this
}
function addHttpMethod (method, { hasBody = false } = {}) {
if (typeof method !== 'string' || http.METHODS.indexOf(method) === -1) {
throw new FST_ERR_ROUTE_METHOD_INVALID()
}
if (hasBody === true) {
this[kSupportedHTTPMethods].bodywith.add(method)
this[kSupportedHTTPMethods].bodyless.delete(method)
} else {
this[kSupportedHTTPMethods].bodywith.delete(method)
this[kSupportedHTTPMethods].bodyless.add(method)
}
const _method = method.toLowerCase()
if (!this.hasDecorator(_method)) {
this.decorate(_method, function (url, options, handler) {
return router.prepareRoute.call(this, { method, url, options, handler })
})
}
return this
}
}
function validateSchemaErrorFormatter (schemaErrorFormatter) {
if (typeof schemaErrorFormatter !== 'function') {
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN(typeof schemaErrorFormatter)
} else if (schemaErrorFormatter.constructor.name === 'AsyncFunction') {
throw new FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN('AsyncFunction')
}
}
/**
* These export configurations enable JS and TS developers
* to consume fastify in whatever way best suits their needs.
* Some examples of supported import syntax includes:
* - `const fastify = require('fastify')`
* - `const { fastify } = require('fastify')`
* - `import * as Fastify from 'fastify'`
* - `import { fastify, TSC_definition } from 'fastify'`
* - `import fastify from 'fastify'`
* - `import fastify, { TSC_definition } from 'fastify'`
*/
module.exports = fastify
module.exports.errorCodes = errorCodes
module.exports.fastify = fastify
module.exports.default = fastify

29
node_modules/fastify/integration/server.js generated vendored Normal file
View File

@@ -0,0 +1,29 @@
'use strict'
const Fastify = require('../fastify')
const fastify = Fastify()
fastify.listen({
host: '::',
port: 3000
})
fastify.get('/', async function (request, reply) {
reply.code(200).send({ data: 'home page' })
})
fastify.post('/post/:id', async function (request, reply) {
const { id } = request.params
reply.code(201).send({ data: `${id}` })
})
fastify.put('/put/:id', async function (request, reply) {
const { id } = request.params
reply.code(200).send({ data: `${id}` })
})
fastify.delete('/delete/:id', async function (request, reply) {
const { id } = request.params
reply.code(204).send({ data: `${id}` })
})

23
node_modules/fastify/integration/test.sh generated vendored Normal file
View File

@@ -0,0 +1,23 @@
#!/usr/bin/bash
set -e
NUMBER=$RANDOM
curl -i -X GET -H 'Content-Type: application/json' localhost:3000/ > GET
if [[ ! $(cat GET | head -1| cut -f2 -d" ") == "200" || ! $(cat GET | tail -1| cut -f4 -d"\"") == "home page" ]] ; then
exit 1
fi;
curl -i -X POST -H 'Content-Type: application/json' localhost:3000/post/$NUMBER --data {} > POST
if [[ ! $(cat POST | head -1| cut -f2 -d" ") == "201" || ! $(cat POST | tail -1| cut -f4 -d"\"") == $(echo $NUMBER) ]]; then
exit 1
fi;
curl -i -X PUT -H 'Content-Type: application/json' localhost:3000/put/$NUMBER --data {} > PUT
if [[ ! $(cat PUT | head -1| cut -f2 -d" ") == "200" || ! $(cat PUT | tail -1| cut -f4 -d"\"") == $(echo $NUMBER) ]]; then
exit 1
fi;
curl -i -X DELETE -H 'Content-Type: application/json' localhost:3000/delete/$NUMBER --data {} > DELETE
if [[ ! $(cat DELETE | head -1| cut -f2 -d" ") == "204" ]]; then
exit 1
fi;
rm -f GET POST PUT DELETE

1272
node_modules/fastify/lib/configValidator.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

402
node_modules/fastify/lib/contentTypeParser.js generated vendored Normal file
View File

@@ -0,0 +1,402 @@
'use strict'
const { AsyncResource } = require('node:async_hooks')
const { FifoMap: Fifo } = require('toad-cache')
const { parse: secureJsonParse } = require('secure-json-parse')
const {
kDefaultJsonParse,
kContentTypeParser,
kBodyLimit,
kRequestPayloadStream,
kState,
kTestInternals,
kReplyIsError,
kRouteContext
} = require('./symbols')
const {
FST_ERR_CTP_INVALID_TYPE,
FST_ERR_CTP_EMPTY_TYPE,
FST_ERR_CTP_ALREADY_PRESENT,
FST_ERR_CTP_INVALID_HANDLER,
FST_ERR_CTP_INVALID_PARSE_TYPE,
FST_ERR_CTP_BODY_TOO_LARGE,
FST_ERR_CTP_INVALID_MEDIA_TYPE,
FST_ERR_CTP_INVALID_CONTENT_LENGTH,
FST_ERR_CTP_EMPTY_JSON_BODY,
FST_ERR_CTP_INSTANCE_ALREADY_STARTED,
FST_ERR_CTP_INVALID_JSON_BODY
} = require('./errors')
const { FSTSEC001 } = require('./warnings')
function ContentTypeParser (bodyLimit, onProtoPoisoning, onConstructorPoisoning) {
this[kDefaultJsonParse] = getDefaultJsonParser(onProtoPoisoning, onConstructorPoisoning)
// using a map instead of a plain object to avoid prototype hijack attacks
this.customParsers = new Map()
this.customParsers.set('application/json', new Parser(true, false, bodyLimit, this[kDefaultJsonParse]))
this.customParsers.set('text/plain', new Parser(true, false, bodyLimit, defaultPlainTextParser))
this.parserList = ['application/json', 'text/plain']
this.parserRegExpList = []
this.cache = new Fifo(100)
}
ContentTypeParser.prototype.add = function (contentType, opts, parserFn) {
const contentTypeIsString = typeof contentType === 'string'
if (contentTypeIsString) {
contentType = contentType.trim().toLowerCase()
if (contentType.length === 0) throw new FST_ERR_CTP_EMPTY_TYPE()
} else if (!(contentType instanceof RegExp)) {
throw new FST_ERR_CTP_INVALID_TYPE()
}
if (typeof parserFn !== 'function') {
throw new FST_ERR_CTP_INVALID_HANDLER()
}
if (this.existingParser(contentType)) {
throw new FST_ERR_CTP_ALREADY_PRESENT(contentType)
}
if (opts.parseAs !== undefined) {
if (opts.parseAs !== 'string' && opts.parseAs !== 'buffer') {
throw new FST_ERR_CTP_INVALID_PARSE_TYPE(opts.parseAs)
}
}
const parser = new Parser(
opts.parseAs === 'string',
opts.parseAs === 'buffer',
opts.bodyLimit,
parserFn
)
if (contentType === '*') {
this.customParsers.set('', parser)
} else {
if (contentTypeIsString) {
this.parserList.unshift(contentType)
this.customParsers.set(contentType, parser)
} else {
validateRegExp(contentType)
this.parserRegExpList.unshift(contentType)
this.customParsers.set(contentType.toString(), parser)
}
}
}
ContentTypeParser.prototype.hasParser = function (contentType) {
if (typeof contentType === 'string') {
contentType = contentType.trim().toLowerCase()
} else {
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
contentType = contentType.toString()
}
return this.customParsers.has(contentType)
}
ContentTypeParser.prototype.existingParser = function (contentType) {
if (contentType === 'application/json' && this.customParsers.has(contentType)) {
return this.customParsers.get(contentType).fn !== this[kDefaultJsonParse]
}
if (contentType === 'text/plain' && this.customParsers.has(contentType)) {
return this.customParsers.get(contentType).fn !== defaultPlainTextParser
}
return this.hasParser(contentType)
}
ContentTypeParser.prototype.getParser = function (contentType) {
let parser = this.customParsers.get(contentType)
if (parser !== undefined) return parser
parser = this.cache.get(contentType)
if (parser !== undefined) return parser
const caseInsensitiveContentType = contentType.toLowerCase()
for (let i = 0; i !== this.parserList.length; ++i) {
const parserListItem = this.parserList[i]
if (
caseInsensitiveContentType.slice(0, parserListItem.length) === parserListItem &&
(
caseInsensitiveContentType.length === parserListItem.length ||
caseInsensitiveContentType.charCodeAt(parserListItem.length) === 59 /* `;` */ ||
caseInsensitiveContentType.charCodeAt(parserListItem.length) === 32 /* ` ` */
)
) {
parser = this.customParsers.get(parserListItem)
this.cache.set(contentType, parser)
return parser
}
}
for (let j = 0; j !== this.parserRegExpList.length; ++j) {
const parserRegExp = this.parserRegExpList[j]
if (parserRegExp.test(contentType)) {
parser = this.customParsers.get(parserRegExp.toString())
this.cache.set(contentType, parser)
return parser
}
}
return this.customParsers.get('')
}
ContentTypeParser.prototype.removeAll = function () {
this.customParsers = new Map()
this.parserRegExpList = []
this.parserList = []
this.cache = new Fifo(100)
}
ContentTypeParser.prototype.remove = function (contentType) {
let parsers
if (typeof contentType === 'string') {
contentType = contentType.trim().toLowerCase()
parsers = this.parserList
} else {
if (!(contentType instanceof RegExp)) throw new FST_ERR_CTP_INVALID_TYPE()
contentType = contentType.toString()
parsers = this.parserRegExpList
}
const removed = this.customParsers.delete(contentType)
const idx = parsers.findIndex(ct => ct.toString() === contentType)
if (idx > -1) {
parsers.splice(idx, 1)
}
return removed || idx > -1
}
ContentTypeParser.prototype.run = function (contentType, handler, request, reply) {
const parser = this.getParser(contentType)
if (parser === undefined) {
if (request.is404 === true) {
handler(request, reply)
return
}
reply[kReplyIsError] = true
reply.send(new FST_ERR_CTP_INVALID_MEDIA_TYPE(contentType || undefined))
return
}
const resource = new AsyncResource('content-type-parser:run', request)
const done = resource.bind(onDone)
if (parser.asString === true || parser.asBuffer === true) {
rawBody(
request,
reply,
reply[kRouteContext]._parserOptions,
parser,
done
)
return
}
const result = parser.fn(request, request[kRequestPayloadStream], done)
if (result && typeof result.then === 'function') {
result.then(body => { done(null, body) }, done)
}
function onDone (error, body) {
resource.emitDestroy()
if (error != null) {
// We must close the connection as the client may
// send more data
reply.header('connection', 'close')
reply[kReplyIsError] = true
reply.send(error)
return
}
request.body = body
handler(request, reply)
}
}
function rawBody (request, reply, options, parser, done) {
const asString = parser.asString === true
const limit = options.limit === null ? parser.bodyLimit : options.limit
const contentLength = Number(request.headers['content-length'])
if (contentLength > limit) {
done(new FST_ERR_CTP_BODY_TOO_LARGE(), undefined)
return
}
let receivedLength = 0
let body = asString ? '' : []
const payload = request[kRequestPayloadStream] || request.raw
if (asString) {
payload.setEncoding('utf8')
}
payload.on('data', onData)
payload.on('end', onEnd)
payload.on('error', onEnd)
payload.resume()
function onData (chunk) {
receivedLength += asString ? Buffer.byteLength(chunk) : chunk.length
const { receivedEncodedLength = 0 } = payload
// The resulting body length must not exceed bodyLimit (see "zip bomb").
// The case when encoded length is larger than received length is rather theoretical,
// unless the stream returned by preParsing hook is broken and reports wrong value.
if (receivedLength > limit || receivedEncodedLength > limit) {
payload.removeListener('data', onData)
payload.removeListener('end', onEnd)
payload.removeListener('error', onEnd)
done(new FST_ERR_CTP_BODY_TOO_LARGE(), undefined)
return
}
if (asString) {
body += chunk
} else {
body.push(chunk)
}
}
function onEnd (err) {
payload.removeListener('data', onData)
payload.removeListener('end', onEnd)
payload.removeListener('error', onEnd)
if (err != null) {
if (!(typeof err.statusCode === 'number' && err.statusCode >= 400)) {
err.statusCode = 400
}
done(err, undefined)
return
}
if (!Number.isNaN(contentLength) && (payload.receivedEncodedLength || receivedLength) !== contentLength) {
done(new FST_ERR_CTP_INVALID_CONTENT_LENGTH(), undefined)
return
}
if (!asString) {
body = Buffer.concat(body)
}
const result = parser.fn(request, body, done)
if (result && typeof result.then === 'function') {
result.then(body => { done(null, body) }, done)
}
}
}
function getDefaultJsonParser (onProtoPoisoning, onConstructorPoisoning) {
const parseOptions = { protoAction: onProtoPoisoning, constructorAction: onConstructorPoisoning }
return defaultJsonParser
function defaultJsonParser (req, body, done) {
if (body.length === 0) {
done(new FST_ERR_CTP_EMPTY_JSON_BODY(), undefined)
return
}
try {
done(null, secureJsonParse(body, parseOptions))
} catch {
done(new FST_ERR_CTP_INVALID_JSON_BODY(), undefined)
}
}
}
function defaultPlainTextParser (req, body, done) {
done(null, body)
}
function Parser (asString, asBuffer, bodyLimit, fn) {
this.asString = asString
this.asBuffer = asBuffer
this.bodyLimit = bodyLimit
this.fn = fn
}
function buildContentTypeParser (c) {
const contentTypeParser = new ContentTypeParser()
contentTypeParser[kDefaultJsonParse] = c[kDefaultJsonParse]
contentTypeParser.customParsers = new Map(c.customParsers.entries())
contentTypeParser.parserList = c.parserList.slice()
contentTypeParser.parserRegExpList = c.parserRegExpList.slice()
return contentTypeParser
}
function addContentTypeParser (contentType, opts, parser) {
if (this[kState].started) {
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('addContentTypeParser')
}
if (typeof opts === 'function') {
parser = opts
opts = {}
}
if (!opts) opts = {}
if (!opts.bodyLimit) opts.bodyLimit = this[kBodyLimit]
if (Array.isArray(contentType)) {
contentType.forEach((type) => this[kContentTypeParser].add(type, opts, parser))
} else {
this[kContentTypeParser].add(contentType, opts, parser)
}
return this
}
function hasContentTypeParser (contentType) {
return this[kContentTypeParser].hasParser(contentType)
}
function removeContentTypeParser (contentType) {
if (this[kState].started) {
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeContentTypeParser')
}
if (Array.isArray(contentType)) {
for (const type of contentType) {
this[kContentTypeParser].remove(type)
}
} else {
this[kContentTypeParser].remove(contentType)
}
}
function removeAllContentTypeParsers () {
if (this[kState].started) {
throw new FST_ERR_CTP_INSTANCE_ALREADY_STARTED('removeAllContentTypeParsers')
}
this[kContentTypeParser].removeAll()
}
function validateRegExp (regexp) {
// RegExp should either start with ^ or include ;?
// It can ensure the user is properly detect the essence
// MIME types.
if (regexp.source[0] !== '^' && regexp.source.includes(';?') === false) {
FSTSEC001(regexp.source)
}
}
module.exports = ContentTypeParser
module.exports.helpers = {
buildContentTypeParser,
addContentTypeParser,
hasContentTypeParser,
removeContentTypeParser,
removeAllContentTypeParsers
}
module.exports.defaultParsers = {
getDefaultJsonParser,
defaultTextParser: defaultPlainTextParser
}
module.exports[kTestInternals] = { rawBody }

95
node_modules/fastify/lib/context.js generated vendored Normal file
View File

@@ -0,0 +1,95 @@
'use strict'
const {
kFourOhFourContext,
kReplySerializerDefault,
kSchemaErrorFormatter,
kErrorHandler,
kChildLoggerFactory,
kOptions,
kReply,
kRequest,
kBodyLimit,
kLogLevel,
kContentTypeParser,
kRouteByFastify,
kRequestCacheValidateFns,
kReplyCacheSerializeFns
} = require('./symbols.js')
// Object that holds the context of every request
// Every route holds an instance of this object.
function Context ({
schema,
handler,
config,
requestIdLogLabel,
childLoggerFactory,
errorHandler,
bodyLimit,
logLevel,
logSerializers,
attachValidation,
validatorCompiler,
serializerCompiler,
replySerializer,
schemaErrorFormatter,
exposeHeadRoute,
prefixTrailingSlash,
server,
isFastify
}) {
this.schema = schema
this.handler = handler
this.Reply = server[kReply]
this.Request = server[kRequest]
this.contentTypeParser = server[kContentTypeParser]
this.onRequest = null
this.onSend = null
this.onError = null
this.onTimeout = null
this.preHandler = null
this.onResponse = null
this.preSerialization = null
this.onRequestAbort = null
this.config = config
this.errorHandler = errorHandler || server[kErrorHandler]
this.requestIdLogLabel = requestIdLogLabel || server[kOptions].requestIdLogLabel
this.childLoggerFactory = childLoggerFactory || server[kChildLoggerFactory]
this._middie = null
this._parserOptions = {
limit: bodyLimit || server[kBodyLimit]
}
this.exposeHeadRoute = exposeHeadRoute
this.prefixTrailingSlash = prefixTrailingSlash
this.logLevel = logLevel || server[kLogLevel]
this.logSerializers = logSerializers
this[kFourOhFourContext] = null
this.attachValidation = attachValidation
this[kReplySerializerDefault] = replySerializer
this.schemaErrorFormatter =
schemaErrorFormatter ||
server[kSchemaErrorFormatter] ||
defaultSchemaErrorFormatter
this[kRouteByFastify] = isFastify
this[kRequestCacheValidateFns] = null
this[kReplyCacheSerializeFns] = null
this.validatorCompiler = validatorCompiler || null
this.serializerCompiler = serializerCompiler || null
this.server = server
}
function defaultSchemaErrorFormatter (errors, dataVar) {
let text = ''
const separator = ', '
for (let i = 0; i !== errors.length; ++i) {
const e = errors[i]
text += dataVar + (e.instancePath || '') + ' ' + e.message + separator
}
return new Error(text.slice(0, -separator.length))
}
module.exports = Context

152
node_modules/fastify/lib/decorate.js generated vendored Normal file
View File

@@ -0,0 +1,152 @@
'use strict'
const {
kReply,
kRequest,
kState,
kHasBeenDecorated
} = require('./symbols.js')
const {
FST_ERR_DEC_ALREADY_PRESENT,
FST_ERR_DEC_MISSING_DEPENDENCY,
FST_ERR_DEC_AFTER_START,
FST_ERR_DEC_REFERENCE_TYPE,
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE,
FST_ERR_DEC_UNDECLARED
} = require('./errors')
function decorate (instance, name, fn, dependencies) {
if (Object.hasOwn(instance, name)) {
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
}
checkDependencies(instance, name, dependencies)
if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
Object.defineProperty(instance, name, {
get: fn.getter,
set: fn.setter
})
} else {
instance[name] = fn
}
}
function getInstanceDecorator (name) {
if (!checkExistence(this, name)) {
throw new FST_ERR_DEC_UNDECLARED(name, 'instance')
}
if (typeof this[name] === 'function') {
return this[name].bind(this)
}
return this[name]
}
function decorateConstructor (konstructor, name, fn, dependencies) {
const instance = konstructor.prototype
if (Object.hasOwn(instance, name) || hasKey(konstructor, name)) {
throw new FST_ERR_DEC_ALREADY_PRESENT(name)
}
konstructor[kHasBeenDecorated] = true
checkDependencies(konstructor, name, dependencies)
if (fn && (typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
Object.defineProperty(instance, name, {
get: fn.getter,
set: fn.setter
})
} else if (typeof fn === 'function') {
instance[name] = fn
} else {
konstructor.props.push({ key: name, value: fn })
}
}
function checkReferenceType (name, fn) {
if (typeof fn === 'object' && fn && !(typeof fn.getter === 'function' || typeof fn.setter === 'function')) {
throw new FST_ERR_DEC_REFERENCE_TYPE(name, typeof fn)
}
}
function decorateFastify (name, fn, dependencies) {
assertNotStarted(this, name)
decorate(this, name, fn, dependencies)
return this
}
function checkExistence (instance, name) {
if (name) {
return name in instance || (instance.prototype && name in instance.prototype) || hasKey(instance, name)
}
return instance in this
}
function hasKey (fn, name) {
if (fn.props) {
return fn.props.find(({ key }) => key === name)
}
return false
}
function checkRequestExistence (name) {
if (name && hasKey(this[kRequest], name)) return true
return checkExistence(this[kRequest].prototype, name)
}
function checkReplyExistence (name) {
if (name && hasKey(this[kReply], name)) return true
return checkExistence(this[kReply].prototype, name)
}
function checkDependencies (instance, name, deps) {
if (deps === undefined || deps === null) {
return
}
if (!Array.isArray(deps)) {
throw new FST_ERR_DEC_DEPENDENCY_INVALID_TYPE(name)
}
for (let i = 0; i !== deps.length; ++i) {
if (!checkExistence(instance, deps[i])) {
throw new FST_ERR_DEC_MISSING_DEPENDENCY(deps[i])
}
}
}
function decorateReply (name, fn, dependencies) {
assertNotStarted(this, name)
checkReferenceType(name, fn)
decorateConstructor(this[kReply], name, fn, dependencies)
return this
}
function decorateRequest (name, fn, dependencies) {
assertNotStarted(this, name)
checkReferenceType(name, fn)
decorateConstructor(this[kRequest], name, fn, dependencies)
return this
}
function assertNotStarted (instance, name) {
if (instance[kState].started) {
throw new FST_ERR_DEC_AFTER_START(name)
}
}
module.exports = {
add: decorateFastify,
exist: checkExistence,
existRequest: checkRequestExistence,
existReply: checkReplyExistence,
dependencies: checkDependencies,
decorateReply,
decorateRequest,
getInstanceDecorator,
hasKey
}

176
node_modules/fastify/lib/error-handler.js generated vendored Normal file
View File

@@ -0,0 +1,176 @@
'use strict'
const statusCodes = require('node:http').STATUS_CODES
const wrapThenable = require('./wrapThenable')
const {
kReplyHeaders,
kReplyNextErrorHandler,
kReplyIsRunningOnErrorHook,
kReplyHasStatusCode,
kRouteContext,
kDisableRequestLogging
} = require('./symbols.js')
const {
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
FST_ERR_FAILED_ERROR_SERIALIZATION
} = require('./errors')
const { getSchemaSerializer } = require('./schemas')
const serializeError = require('./error-serializer')
const rootErrorHandler = {
func: defaultErrorHandler,
toJSON () {
return this.func.name.toString() + '()'
}
}
function handleError (reply, error, cb) {
reply[kReplyIsRunningOnErrorHook] = false
const context = reply[kRouteContext]
if (reply[kReplyNextErrorHandler] === false) {
fallbackErrorHandler(error, reply, function (reply, payload) {
try {
reply.raw.writeHead(reply.raw.statusCode, reply[kReplyHeaders])
} catch (error) {
if (!reply.log[kDisableRequestLogging]) {
reply.log.warn(
{ req: reply.request, res: reply, err: error },
error?.message
)
}
reply.raw.writeHead(reply.raw.statusCode)
}
reply.raw.end(payload)
})
return
}
const errorHandler = reply[kReplyNextErrorHandler] || context.errorHandler
// In case the error handler throws, we set the next errorHandler so we can error again
reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)
// we need to remove content-type to allow content-type guessing for serialization
delete reply[kReplyHeaders]['content-type']
delete reply[kReplyHeaders]['content-length']
const func = errorHandler.func
if (!func) {
reply[kReplyNextErrorHandler] = false
fallbackErrorHandler(error, reply, cb)
return
}
try {
const result = func(error, reply.request, reply)
if (result !== undefined) {
if (result !== null && typeof result.then === 'function') {
wrapThenable(result, reply)
} else {
reply.send(result)
}
}
} catch (err) {
reply.send(err)
}
}
function defaultErrorHandler (error, request, reply) {
setErrorHeaders(error, reply)
if (!reply[kReplyHasStatusCode] || reply.statusCode === 200) {
const statusCode = error.statusCode || error.status
reply.code(statusCode >= 400 ? statusCode : 500)
}
if (reply.statusCode < 500) {
if (!reply.log[kDisableRequestLogging]) {
reply.log.info(
{ res: reply, err: error },
error?.message
)
}
} else {
if (!reply.log[kDisableRequestLogging]) {
reply.log.error(
{ req: request, res: reply, err: error },
error?.message
)
}
}
reply.send(error)
}
function fallbackErrorHandler (error, reply, cb) {
const res = reply.raw
const statusCode = reply.statusCode
reply[kReplyHeaders]['content-type'] = reply[kReplyHeaders]['content-type'] ?? 'application/json; charset=utf-8'
let payload
try {
const serializerFn = getSchemaSerializer(reply[kRouteContext], statusCode, reply[kReplyHeaders]['content-type'])
if (serializerFn === false) {
payload = serializeError({
error: statusCodes[statusCode + ''],
code: error.code,
message: error.message,
statusCode
})
} else {
payload = serializerFn(Object.create(error, {
error: { value: statusCodes[statusCode + ''] },
message: { value: error.message },
statusCode: { value: statusCode }
}))
}
} catch (err) {
if (!reply.log[kDisableRequestLogging]) {
// error is always FST_ERR_SCH_SERIALIZATION_BUILD because this is called from route/compileSchemasForSerialization
reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
}
reply.code(500)
payload = serializeError(new FST_ERR_FAILED_ERROR_SERIALIZATION(err.message, error.message))
}
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
payload = serializeError(new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload))
}
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
cb(reply, payload)
}
function buildErrorHandler (parent = rootErrorHandler, func) {
if (!func) {
return parent
}
const errorHandler = Object.create(parent)
errorHandler.func = func
return errorHandler
}
function setErrorHeaders (error, reply) {
const res = reply.raw
let statusCode = res.statusCode
statusCode = (statusCode >= 400) ? statusCode : 500
// treat undefined and null as same
if (error != null) {
if (error.headers !== undefined) {
reply.headers(error.headers)
}
if (error.status >= 400) {
statusCode = error.status
} else if (error.statusCode >= 400) {
statusCode = error.statusCode
}
}
res.statusCode = statusCode
}
module.exports = {
buildErrorHandler,
handleError
}

120
node_modules/fastify/lib/error-serializer.js generated vendored Normal file
View File

@@ -0,0 +1,120 @@
// This file is autogenerated by build/build-error-serializer.js, do not edit
/* c8 ignore start */
'use strict'
const Serializer = require('fast-json-stringify/lib/serializer')
const serializerState = {"mode":"standalone"}
const serializer = Serializer.restoreFromState(serializerState)
const validator = null
module.exports = function anonymous(validator,serializer
) {
const JSON_STR_BEGIN_OBJECT = '{'
const JSON_STR_END_OBJECT = '}'
const JSON_STR_BEGIN_ARRAY = '['
const JSON_STR_END_ARRAY = ']'
const JSON_STR_COMMA = ','
const JSON_STR_COLONS = ':'
const JSON_STR_QUOTE = '"'
const JSON_STR_EMPTY_OBJECT = JSON_STR_BEGIN_OBJECT + JSON_STR_END_OBJECT
const JSON_STR_EMPTY_ARRAY = JSON_STR_BEGIN_ARRAY + JSON_STR_END_ARRAY
const JSON_STR_EMPTY_STRING = JSON_STR_QUOTE + JSON_STR_QUOTE
const JSON_STR_NULL = 'null'
// #
function anonymous0 (input) {
const obj = (input && typeof input.toJSON === 'function')
? input.toJSON()
: input
if (obj === null) return JSON_STR_EMPTY_OBJECT
let value
let json = JSON_STR_BEGIN_OBJECT
let addComma = false
value = obj["statusCode"]
if (value !== undefined) {
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
json += "\"statusCode\":"
json += serializer.asNumber(value)
}
value = obj["code"]
if (value !== undefined) {
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
json += "\"code\":"
if (typeof value !== 'string') {
if (value === null) {
json += JSON_STR_EMPTY_STRING
} else if (value instanceof Date) {
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
} else if (value instanceof RegExp) {
json += serializer.asString(value.source)
} else {
json += serializer.asString(value.toString())
}
} else {
json += serializer.asString(value)
}
}
value = obj["error"]
if (value !== undefined) {
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
json += "\"error\":"
if (typeof value !== 'string') {
if (value === null) {
json += JSON_STR_EMPTY_STRING
} else if (value instanceof Date) {
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
} else if (value instanceof RegExp) {
json += serializer.asString(value.source)
} else {
json += serializer.asString(value.toString())
}
} else {
json += serializer.asString(value)
}
}
value = obj["message"]
if (value !== undefined) {
!addComma && (addComma = true) || (json += JSON_STR_COMMA)
json += "\"message\":"
if (typeof value !== 'string') {
if (value === null) {
json += JSON_STR_EMPTY_STRING
} else if (value instanceof Date) {
json += JSON_STR_QUOTE + value.toISOString() + JSON_STR_QUOTE
} else if (value instanceof RegExp) {
json += serializer.asString(value.source)
} else {
json += serializer.asString(value.toString())
}
} else {
json += serializer.asString(value)
}
}
return json + JSON_STR_END_OBJECT
}
const main = anonymous0
return main
}(validator, serializer)
/* c8 ignore stop */

505
node_modules/fastify/lib/errors.js generated vendored Normal file
View File

@@ -0,0 +1,505 @@
'use strict'
const createError = require('@fastify/error')
const codes = {
/**
* Basic
*/
FST_ERR_NOT_FOUND: createError(
'FST_ERR_NOT_FOUND',
'Not Found',
404
),
FST_ERR_OPTIONS_NOT_OBJ: createError(
'FST_ERR_OPTIONS_NOT_OBJ',
'Options must be an object',
500,
TypeError
),
FST_ERR_QSP_NOT_FN: createError(
'FST_ERR_QSP_NOT_FN',
"querystringParser option should be a function, instead got '%s'",
500,
TypeError
),
FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN: createError(
'FST_ERR_SCHEMA_CONTROLLER_BUCKET_OPT_NOT_FN',
"schemaController.bucket option should be a function, instead got '%s'",
500,
TypeError
),
FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN: createError(
'FST_ERR_SCHEMA_ERROR_FORMATTER_NOT_FN',
"schemaErrorFormatter option should be a non async function. Instead got '%s'.",
500,
TypeError
),
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ: createError(
'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_OBJ',
"ajv.customOptions option should be an object, instead got '%s'",
500,
TypeError
),
FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR: createError(
'FST_ERR_AJV_CUSTOM_OPTIONS_OPT_NOT_ARR',
"ajv.plugins option should be an array, instead got '%s'",
500,
TypeError
),
FST_ERR_VALIDATION: createError(
'FST_ERR_VALIDATION',
'%s',
400
),
FST_ERR_LISTEN_OPTIONS_INVALID: createError(
'FST_ERR_LISTEN_OPTIONS_INVALID',
"Invalid listen options: '%s'",
500,
TypeError
),
FST_ERR_ERROR_HANDLER_NOT_FN: createError(
'FST_ERR_ERROR_HANDLER_NOT_FN',
'Error Handler must be a function',
500,
TypeError
),
FST_ERR_ERROR_HANDLER_ALREADY_SET: createError(
'FST_ERR_ERROR_HANDLER_ALREADY_SET',
"Error Handler already set in this scope. Set 'allowErrorHandlerOverride: true' to allow overriding.",
500,
TypeError
),
/**
* ContentTypeParser
*/
FST_ERR_CTP_ALREADY_PRESENT: createError(
'FST_ERR_CTP_ALREADY_PRESENT',
"Content type parser '%s' already present."
),
FST_ERR_CTP_INVALID_TYPE: createError(
'FST_ERR_CTP_INVALID_TYPE',
'The content type should be a string or a RegExp',
500,
TypeError
),
FST_ERR_CTP_EMPTY_TYPE: createError(
'FST_ERR_CTP_EMPTY_TYPE',
'The content type cannot be an empty string',
500,
TypeError
),
FST_ERR_CTP_INVALID_HANDLER: createError(
'FST_ERR_CTP_INVALID_HANDLER',
'The content type handler should be a function',
500,
TypeError
),
FST_ERR_CTP_INVALID_PARSE_TYPE: createError(
'FST_ERR_CTP_INVALID_PARSE_TYPE',
"The body parser can only parse your data as 'string' or 'buffer', you asked '%s' which is not supported.",
500,
TypeError
),
FST_ERR_CTP_BODY_TOO_LARGE: createError(
'FST_ERR_CTP_BODY_TOO_LARGE',
'Request body is too large',
413,
RangeError
),
FST_ERR_CTP_INVALID_MEDIA_TYPE: createError(
'FST_ERR_CTP_INVALID_MEDIA_TYPE',
'Unsupported Media Type: %s',
415
),
FST_ERR_CTP_INVALID_CONTENT_LENGTH: createError(
'FST_ERR_CTP_INVALID_CONTENT_LENGTH',
'Request body size did not match Content-Length',
400,
RangeError
),
FST_ERR_CTP_EMPTY_JSON_BODY: createError(
'FST_ERR_CTP_EMPTY_JSON_BODY',
"Body cannot be empty when content-type is set to 'application/json'",
400
),
FST_ERR_CTP_INVALID_JSON_BODY: createError(
'FST_ERR_CTP_INVALID_JSON_BODY',
"Body is not valid JSON but content-type is set to 'application/json'",
400
),
FST_ERR_CTP_INSTANCE_ALREADY_STARTED: createError(
'FST_ERR_CTP_INSTANCE_ALREADY_STARTED',
'Cannot call "%s" when fastify instance is already started!',
400
),
/**
* decorate
*/
FST_ERR_DEC_ALREADY_PRESENT: createError(
'FST_ERR_DEC_ALREADY_PRESENT',
"The decorator '%s' has already been added!"
),
FST_ERR_DEC_DEPENDENCY_INVALID_TYPE: createError(
'FST_ERR_DEC_DEPENDENCY_INVALID_TYPE',
"The dependencies of decorator '%s' must be of type Array.",
500,
TypeError
),
FST_ERR_DEC_MISSING_DEPENDENCY: createError(
'FST_ERR_DEC_MISSING_DEPENDENCY',
"The decorator is missing dependency '%s'."
),
FST_ERR_DEC_AFTER_START: createError(
'FST_ERR_DEC_AFTER_START',
"The decorator '%s' has been added after start!"
),
FST_ERR_DEC_REFERENCE_TYPE: createError(
'FST_ERR_DEC_REFERENCE_TYPE',
"The decorator '%s' of type '%s' is a reference type. Use the { getter, setter } interface instead."
),
FST_ERR_DEC_UNDECLARED: createError(
'FST_ERR_DEC_UNDECLARED',
"No decorator '%s' has been declared on %s."
),
/**
* hooks
*/
FST_ERR_HOOK_INVALID_TYPE: createError(
'FST_ERR_HOOK_INVALID_TYPE',
'The hook name must be a string',
500,
TypeError
),
FST_ERR_HOOK_INVALID_HANDLER: createError(
'FST_ERR_HOOK_INVALID_HANDLER',
'%s hook should be a function, instead got %s',
500,
TypeError
),
FST_ERR_HOOK_INVALID_ASYNC_HANDLER: createError(
'FST_ERR_HOOK_INVALID_ASYNC_HANDLER',
'Async function has too many arguments. Async hooks should not use the \'done\' argument.',
500,
TypeError
),
FST_ERR_HOOK_NOT_SUPPORTED: createError(
'FST_ERR_HOOK_NOT_SUPPORTED',
'%s hook not supported!',
500,
TypeError
),
/**
* Middlewares
*/
FST_ERR_MISSING_MIDDLEWARE: createError(
'FST_ERR_MISSING_MIDDLEWARE',
'You must register a plugin for handling middlewares, visit fastify.dev/docs/latest/Reference/Middleware/ for more info.',
500
),
FST_ERR_HOOK_TIMEOUT: createError(
'FST_ERR_HOOK_TIMEOUT',
"A callback for '%s' hook%s timed out. You may have forgotten to call 'done' function or to resolve a Promise"
),
/**
* logger
*/
FST_ERR_LOG_INVALID_DESTINATION: createError(
'FST_ERR_LOG_INVALID_DESTINATION',
'Cannot specify both logger.stream and logger.file options'
),
FST_ERR_LOG_INVALID_LOGGER: createError(
'FST_ERR_LOG_INVALID_LOGGER',
"Invalid logger object provided. The logger instance should have these functions(s): '%s'.",
500,
TypeError
),
FST_ERR_LOG_INVALID_LOGGER_INSTANCE: createError(
'FST_ERR_LOG_INVALID_LOGGER_INSTANCE',
'loggerInstance only accepts a logger instance.',
500,
TypeError
),
FST_ERR_LOG_INVALID_LOGGER_CONFIG: createError(
'FST_ERR_LOG_INVALID_LOGGER_CONFIG',
'logger options only accepts a configuration object.',
500,
TypeError
),
FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED: createError(
'FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED',
'You cannot provide both logger and loggerInstance. Please provide only one.',
500,
TypeError
),
/**
* reply
*/
FST_ERR_REP_INVALID_PAYLOAD_TYPE: createError(
'FST_ERR_REP_INVALID_PAYLOAD_TYPE',
"Attempted to send payload of invalid type '%s'. Expected a string or Buffer.",
500,
TypeError
),
FST_ERR_REP_RESPONSE_BODY_CONSUMED: createError(
'FST_ERR_REP_RESPONSE_BODY_CONSUMED',
'Response.body is already consumed.'
),
FST_ERR_REP_READABLE_STREAM_LOCKED: createError(
'FST_ERR_REP_READABLE_STREAM_LOCKED',
'ReadableStream was locked. You should call releaseLock() method on reader before sending.'
),
FST_ERR_REP_ALREADY_SENT: createError(
'FST_ERR_REP_ALREADY_SENT',
'Reply was already sent, did you forget to "return reply" in "%s" (%s)?'
),
FST_ERR_REP_SENT_VALUE: createError(
'FST_ERR_REP_SENT_VALUE',
'The only possible value for reply.sent is true.',
500,
TypeError
),
FST_ERR_SEND_INSIDE_ONERR: createError(
'FST_ERR_SEND_INSIDE_ONERR',
'You cannot use `send` inside the `onError` hook'
),
FST_ERR_SEND_UNDEFINED_ERR: createError(
'FST_ERR_SEND_UNDEFINED_ERR',
'Undefined error has occurred'
),
FST_ERR_BAD_STATUS_CODE: createError(
'FST_ERR_BAD_STATUS_CODE',
'Called reply with an invalid status code: %s'
),
FST_ERR_BAD_TRAILER_NAME: createError(
'FST_ERR_BAD_TRAILER_NAME',
'Called reply.trailer with an invalid header name: %s'
),
FST_ERR_BAD_TRAILER_VALUE: createError(
'FST_ERR_BAD_TRAILER_VALUE',
"Called reply.trailer('%s', fn) with an invalid type: %s. Expected a function."
),
FST_ERR_FAILED_ERROR_SERIALIZATION: createError(
'FST_ERR_FAILED_ERROR_SERIALIZATION',
'Failed to serialize an error. Error: %s. Original error: %s'
),
FST_ERR_MISSING_SERIALIZATION_FN: createError(
'FST_ERR_MISSING_SERIALIZATION_FN',
'Missing serialization function. Key "%s"'
),
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN: createError(
'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
'Missing serialization function. Key "%s:%s"'
),
FST_ERR_REQ_INVALID_VALIDATION_INVOCATION: createError(
'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION',
'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.'
),
/**
* schemas
*/
FST_ERR_SCH_MISSING_ID: createError(
'FST_ERR_SCH_MISSING_ID',
'Missing schema $id property'
),
FST_ERR_SCH_ALREADY_PRESENT: createError(
'FST_ERR_SCH_ALREADY_PRESENT',
"Schema with id '%s' already declared!"
),
FST_ERR_SCH_CONTENT_MISSING_SCHEMA: createError(
'FST_ERR_SCH_CONTENT_MISSING_SCHEMA',
"Schema is missing for the content type '%s'"
),
FST_ERR_SCH_DUPLICATE: createError(
'FST_ERR_SCH_DUPLICATE',
"Schema with '%s' already present!"
),
FST_ERR_SCH_VALIDATION_BUILD: createError(
'FST_ERR_SCH_VALIDATION_BUILD',
'Failed building the validation schema for %s: %s, due to error %s'
),
FST_ERR_SCH_SERIALIZATION_BUILD: createError(
'FST_ERR_SCH_SERIALIZATION_BUILD',
'Failed building the serialization schema for %s: %s, due to error %s'
),
FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX: createError(
'FST_ERR_SCH_RESPONSE_SCHEMA_NOT_NESTED_2XX',
'response schemas should be nested under a valid status code, e.g { 2xx: { type: "object" } }'
),
/**
* initialConfig
*/
FST_ERR_INIT_OPTS_INVALID: createError(
'FST_ERR_INIT_OPTS_INVALID',
"Invalid initialization options: '%s'"
),
FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE: createError(
'FST_ERR_FORCE_CLOSE_CONNECTIONS_IDLE_NOT_AVAILABLE',
"Cannot set forceCloseConnections to 'idle' as your HTTP server does not support closeIdleConnections method"
),
/**
* router
*/
FST_ERR_DUPLICATED_ROUTE: createError(
'FST_ERR_DUPLICATED_ROUTE',
"Method '%s' already declared for route '%s'"
),
FST_ERR_BAD_URL: createError(
'FST_ERR_BAD_URL',
"'%s' is not a valid url component",
400,
URIError
),
FST_ERR_ASYNC_CONSTRAINT: createError(
'FST_ERR_ASYNC_CONSTRAINT',
'Unexpected error from async constraint',
500
),
FST_ERR_INVALID_URL: createError(
'FST_ERR_INVALID_URL',
"URL must be a string. Received '%s'",
400,
TypeError
),
FST_ERR_ROUTE_OPTIONS_NOT_OBJ: createError(
'FST_ERR_ROUTE_OPTIONS_NOT_OBJ',
'Options for "%s:%s" route must be an object',
500,
TypeError
),
FST_ERR_ROUTE_DUPLICATED_HANDLER: createError(
'FST_ERR_ROUTE_DUPLICATED_HANDLER',
'Duplicate handler for "%s:%s" route is not allowed!',
500
),
FST_ERR_ROUTE_HANDLER_NOT_FN: createError(
'FST_ERR_ROUTE_HANDLER_NOT_FN',
'Error Handler for %s:%s route, if defined, must be a function',
500,
TypeError
),
FST_ERR_ROUTE_MISSING_HANDLER: createError(
'FST_ERR_ROUTE_MISSING_HANDLER',
'Missing handler function for "%s:%s" route.',
500
),
FST_ERR_ROUTE_METHOD_INVALID: createError(
'FST_ERR_ROUTE_METHOD_INVALID',
'Provided method is invalid!',
500,
TypeError
),
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED: createError(
'FST_ERR_ROUTE_METHOD_NOT_SUPPORTED',
'%s method is not supported.',
500
),
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED: createError(
'FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED',
'Body validation schema for %s:%s route is not supported!',
500
),
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT: createError(
'FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT',
"'bodyLimit' option must be an integer > 0. Got '%s'",
500,
TypeError
),
FST_ERR_ROUTE_REWRITE_NOT_STR: createError(
'FST_ERR_ROUTE_REWRITE_NOT_STR',
'Rewrite url for "%s" needs to be of type "string" but received "%s"',
500,
TypeError
),
/**
* again listen when close server
*/
FST_ERR_REOPENED_CLOSE_SERVER: createError(
'FST_ERR_REOPENED_CLOSE_SERVER',
'Fastify has already been closed and cannot be reopened'
),
FST_ERR_REOPENED_SERVER: createError(
'FST_ERR_REOPENED_SERVER',
'Fastify is already listening'
),
FST_ERR_INSTANCE_ALREADY_LISTENING: createError(
'FST_ERR_INSTANCE_ALREADY_LISTENING',
'Fastify instance is already listening. %s'
),
/**
* plugin
*/
FST_ERR_PLUGIN_VERSION_MISMATCH: createError(
'FST_ERR_PLUGIN_VERSION_MISMATCH',
"fastify-plugin: %s - expected '%s' fastify version, '%s' is installed"
),
FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE: createError(
'FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE',
"The decorator '%s'%s is not present in %s"
),
FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER: createError(
'FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER',
'The %s plugin being registered mixes async and callback styles. Async plugin should not mix async and callback style.',
500,
TypeError
),
/**
* Avvio Errors
*/
FST_ERR_PLUGIN_CALLBACK_NOT_FN: createError(
'FST_ERR_PLUGIN_CALLBACK_NOT_FN',
'fastify-plugin: %s',
500,
TypeError
),
FST_ERR_PLUGIN_NOT_VALID: createError(
'FST_ERR_PLUGIN_NOT_VALID',
'fastify-plugin: %s'
),
FST_ERR_ROOT_PLG_BOOTED: createError(
'FST_ERR_ROOT_PLG_BOOTED',
'fastify-plugin: %s'
),
FST_ERR_PARENT_PLUGIN_BOOTED: createError(
'FST_ERR_PARENT_PLUGIN_BOOTED',
'fastify-plugin: %s'
),
FST_ERR_PLUGIN_TIMEOUT: createError(
'FST_ERR_PLUGIN_TIMEOUT',
'fastify-plugin: %s'
)
}
function appendStackTrace (oldErr, newErr) {
newErr.cause = oldErr
return newErr
}
module.exports = codes
module.exports.appendStackTrace = appendStackTrace
module.exports.AVVIO_ERRORS_MAP = {
AVV_ERR_CALLBACK_NOT_FN: codes.FST_ERR_PLUGIN_CALLBACK_NOT_FN,
AVV_ERR_PLUGIN_NOT_VALID: codes.FST_ERR_PLUGIN_NOT_VALID,
AVV_ERR_ROOT_PLG_BOOTED: codes.FST_ERR_ROOT_PLG_BOOTED,
AVV_ERR_PARENT_PLG_LOADED: codes.FST_ERR_PARENT_PLUGIN_BOOTED,
AVV_ERR_READY_TIMEOUT: codes.FST_ERR_PLUGIN_TIMEOUT,
AVV_ERR_PLUGIN_EXEC_TIMEOUT: codes.FST_ERR_PLUGIN_TIMEOUT
}

187
node_modules/fastify/lib/fourOhFour.js generated vendored Normal file
View File

@@ -0,0 +1,187 @@
'use strict'
const FindMyWay = require('find-my-way')
const Reply = require('./reply')
const Request = require('./request')
const Context = require('./context')
const {
kRoutePrefix,
kCanSetNotFoundHandler,
kFourOhFourLevelInstance,
kFourOhFourContext,
kHooks,
kErrorHandler
} = require('./symbols.js')
const { lifecycleHooks } = require('./hooks')
const { buildErrorHandler } = require('./error-handler.js')
const {
FST_ERR_NOT_FOUND
} = require('./errors')
const { createChildLogger } = require('./logger-factory')
const { getGenReqId } = require('./reqIdGenFactory.js')
/**
* Each fastify instance have a:
* kFourOhFourLevelInstance: point to a fastify instance that has the 404 handler set
* kCanSetNotFoundHandler: bool to track if the 404 handler has already been set
* kFourOhFour: the singleton instance of this 404 module
* kFourOhFourContext: the context in the reply object where the handler will be executed
*/
function fourOhFour (options) {
const { logger, disableRequestLogging } = options
// 404 router, used for handling encapsulated 404 handlers
const router = FindMyWay({ onBadUrl: createOnBadUrl(), defaultRoute: fourOhFourFallBack })
let _onBadUrlHandler = null
return { router, setNotFoundHandler, setContext, arrange404 }
function arrange404 (instance) {
// Change the pointer of the fastify instance to itself, so register + prefix can add new 404 handler
instance[kFourOhFourLevelInstance] = instance
instance[kCanSetNotFoundHandler] = true
// we need to bind instance for the context
router.onBadUrl = router.onBadUrl.bind(instance)
router.defaultRoute = router.defaultRoute.bind(instance)
}
function basic404 (request, reply) {
const { url, method } = request.raw
const message = `Route ${method}:${url} not found`
if (!disableRequestLogging) {
request.log.info(message)
}
reply.code(404).send({
message,
error: 'Not Found',
statusCode: 404
})
}
function createOnBadUrl () {
return function onBadUrl (path, req, res) {
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
const id = getGenReqId(fourOhFourContext.server, req)
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
const reply = new Reply(res, request, childLogger)
_onBadUrlHandler(request, reply)
}
}
function setContext (instance, context) {
const _404Context = Object.assign({}, instance[kFourOhFourContext])
_404Context.onSend = context.onSend
context[kFourOhFourContext] = _404Context
}
function setNotFoundHandler (opts, handler, avvio, routeHandler) {
// First initialization of the fastify root instance
if (this[kCanSetNotFoundHandler] === undefined) {
this[kCanSetNotFoundHandler] = true
}
if (this[kFourOhFourContext] === undefined) {
this[kFourOhFourContext] = null
}
const _fastify = this
const prefix = this[kRoutePrefix] || '/'
if (this[kCanSetNotFoundHandler] === false) {
throw new Error(`Not found handler already set for Fastify instance with prefix: '${prefix}'`)
}
if (typeof opts === 'object') {
if (opts.preHandler) {
if (Array.isArray(opts.preHandler)) {
opts.preHandler = opts.preHandler.map(hook => hook.bind(_fastify))
} else {
opts.preHandler = opts.preHandler.bind(_fastify)
}
}
if (opts.preValidation) {
if (Array.isArray(opts.preValidation)) {
opts.preValidation = opts.preValidation.map(hook => hook.bind(_fastify))
} else {
opts.preValidation = opts.preValidation.bind(_fastify)
}
}
}
if (typeof opts === 'function') {
handler = opts
opts = undefined
}
opts = opts || {}
if (handler) {
this[kFourOhFourLevelInstance][kCanSetNotFoundHandler] = false
handler = handler.bind(this)
// update onBadUrl handler
_onBadUrlHandler = handler
} else {
handler = basic404
// update onBadUrl handler
_onBadUrlHandler = basic404
}
this.after((notHandledErr, done) => {
_setNotFoundHandler.call(this, prefix, opts, handler, avvio, routeHandler)
done(notHandledErr)
})
}
function _setNotFoundHandler (prefix, opts, handler, avvio, routeHandler) {
const context = new Context({
schema: opts.schema,
handler,
config: opts.config || {},
server: this
})
avvio.once('preReady', () => {
const context = this[kFourOhFourContext]
for (const hook of lifecycleHooks) {
const toSet = this[kHooks][hook]
.concat(opts[hook] || [])
.map(h => h.bind(this))
context[hook] = toSet.length ? toSet : null
}
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
})
if (this[kFourOhFourContext] !== null && prefix === '/') {
Object.assign(this[kFourOhFourContext], context) // Replace the default 404 handler
return
}
this[kFourOhFourLevelInstance][kFourOhFourContext] = context
router.all(prefix + (prefix.endsWith('/') ? '*' : '/*'), routeHandler, context)
router.all(prefix, routeHandler, context)
}
function fourOhFourFallBack (req, res) {
// if this happen, we have a very bad bug
// we might want to do some hard debugging
// here, let's print out as much info as
// we can
const fourOhFourContext = this[kFourOhFourLevelInstance][kFourOhFourContext]
const id = getGenReqId(fourOhFourContext.server, req)
const childLogger = createChildLogger(fourOhFourContext, logger, req, id)
childLogger.info({ req }, 'incoming request')
const request = new Request(id, null, req, null, childLogger, fourOhFourContext)
const reply = new Reply(res, request, childLogger)
request.log.warn('the default handler for 404 did not catch this, this is likely a fastify bug, please report it')
request.log.warn(router.prettyPrint())
reply.code(404).send(new FST_ERR_NOT_FOUND())
}
}
module.exports = fourOhFour

182
node_modules/fastify/lib/handleRequest.js generated vendored Normal file
View File

@@ -0,0 +1,182 @@
'use strict'
const diagnostics = require('node:diagnostics_channel')
const { validate: validateSchema } = require('./validation')
const { preValidationHookRunner, preHandlerHookRunner } = require('./hooks')
const wrapThenable = require('./wrapThenable')
const {
kReplyIsError,
kRouteContext,
kFourOhFourContext,
kSupportedHTTPMethods
} = require('./symbols')
const channels = diagnostics.tracingChannel('fastify.request.handler')
function handleRequest (err, request, reply) {
if (reply.sent === true) return
if (err != null) {
reply[kReplyIsError] = true
reply.send(err)
return
}
const method = request.method
if (this[kSupportedHTTPMethods].bodyless.has(method)) {
handler(request, reply)
return
}
if (this[kSupportedHTTPMethods].bodywith.has(method)) {
const headers = request.headers
const contentType = headers['content-type']
if (contentType === undefined) {
const contentLength = headers['content-length']
const transferEncoding = headers['transfer-encoding']
const isEmptyBody = transferEncoding === undefined &&
(contentLength === undefined || contentLength === '0')
if (isEmptyBody) {
// Request has no body to parse
handler(request, reply)
return
}
request[kRouteContext].contentTypeParser.run('', handler, request, reply)
return
}
request[kRouteContext].contentTypeParser.run(contentType, handler, request, reply)
return
}
// Return 404 instead of 405 see https://github.com/fastify/fastify/pull/862 for discussion
handler(request, reply)
}
function handler (request, reply) {
try {
if (request[kRouteContext].preValidation !== null) {
preValidationHookRunner(
request[kRouteContext].preValidation,
request,
reply,
preValidationCallback
)
} else {
preValidationCallback(null, request, reply)
}
} catch (err) {
preValidationCallback(err, request, reply)
}
}
function preValidationCallback (err, request, reply) {
if (reply.sent === true) return
if (err != null) {
reply[kReplyIsError] = true
reply.send(err)
return
}
const validationErr = validateSchema(reply[kRouteContext], request)
const isAsync = (validationErr && typeof validationErr.then === 'function') || false
if (isAsync) {
const cb = validationCompleted.bind(null, request, reply)
validationErr.then(cb, cb)
} else {
validationCompleted(request, reply, validationErr)
}
}
function validationCompleted (request, reply, validationErr) {
if (validationErr) {
if (reply[kRouteContext].attachValidation === false) {
reply.send(validationErr)
return
}
reply.request.validationError = validationErr
}
// preHandler hook
if (request[kRouteContext].preHandler !== null) {
preHandlerHookRunner(
request[kRouteContext].preHandler,
request,
reply,
preHandlerCallback
)
} else {
preHandlerCallback(null, request, reply)
}
}
function preHandlerCallback (err, request, reply) {
if (reply.sent) return
const context = request[kRouteContext]
if (!channels.hasSubscribers || context[kFourOhFourContext] === null) {
preHandlerCallbackInner(err, request, reply)
} else {
const store = {
request,
reply,
async: false,
route: {
url: context.config.url,
method: context.config.method
}
}
channels.start.runStores(store, preHandlerCallbackInner, undefined, err, request, reply, store)
}
}
function preHandlerCallbackInner (err, request, reply, store) {
const context = request[kRouteContext]
try {
if (err != null) {
reply[kReplyIsError] = true
reply.send(err)
if (store) {
store.error = err
channels.error.publish(store)
}
return
}
let result
try {
result = context.handler(request, reply)
} catch (err) {
if (store) {
store.error = err
channels.error.publish(store)
}
reply[kReplyIsError] = true
reply.send(err)
return
}
if (result !== undefined) {
if (result !== null && typeof result.then === 'function') {
wrapThenable(result, reply, store)
} else {
reply.send(result)
}
}
} finally {
if (store) channels.end.publish(store)
}
}
module.exports = handleRequest
module.exports[Symbol.for('internals')] = { handler, preHandlerCallback }

33
node_modules/fastify/lib/headRoute.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
'use strict'
function headRouteOnSendHandler (req, reply, payload, done) {
// If payload is undefined
if (payload === undefined) {
reply.header('content-length', '0')
done(null, null)
return
}
if (typeof payload.resume === 'function') {
payload.on('error', (err) => {
reply.log.error({ err }, 'Error on Stream found for HEAD route')
})
payload.resume()
done(null, null)
return
}
const size = '' + Buffer.byteLength(payload)
reply.header('content-length', size)
done(null, null)
}
function parseHeadOnSendHandlers (onSendHandlers) {
if (onSendHandlers == null) return headRouteOnSendHandler
return Array.isArray(onSendHandlers) ? [...onSendHandlers, headRouteOnSendHandler] : [onSendHandlers, headRouteOnSendHandler]
}
module.exports = {
parseHeadOnSendHandlers
}

429
node_modules/fastify/lib/hooks.js generated vendored Normal file
View File

@@ -0,0 +1,429 @@
'use strict'
const applicationHooks = [
'onRoute',
'onRegister',
'onReady',
'onListen',
'preClose',
'onClose'
]
const lifecycleHooks = [
'onTimeout',
'onRequest',
'preParsing',
'preValidation',
'preSerialization',
'preHandler',
'onSend',
'onResponse',
'onError',
'onRequestAbort'
]
const supportedHooks = lifecycleHooks.concat(applicationHooks)
const {
FST_ERR_HOOK_INVALID_TYPE,
FST_ERR_HOOK_INVALID_HANDLER,
FST_ERR_SEND_UNDEFINED_ERR,
FST_ERR_HOOK_TIMEOUT,
FST_ERR_HOOK_NOT_SUPPORTED,
AVVIO_ERRORS_MAP,
appendStackTrace
} = require('./errors')
const {
kChildren,
kHooks,
kRequestPayloadStream
} = require('./symbols')
function Hooks () {
this.onRequest = []
this.preParsing = []
this.preValidation = []
this.preSerialization = []
this.preHandler = []
this.onResponse = []
this.onSend = []
this.onError = []
this.onRoute = []
this.onRegister = []
this.onReady = []
this.onListen = []
this.onTimeout = []
this.onRequestAbort = []
this.preClose = []
}
Hooks.prototype = Object.create(null)
Hooks.prototype.validate = function (hook, fn) {
if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE()
if (Array.isArray(this[hook]) === false) {
throw new FST_ERR_HOOK_NOT_SUPPORTED(hook)
}
if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(fn))
}
Hooks.prototype.add = function (hook, fn) {
this.validate(hook, fn)
this[hook].push(fn)
}
function buildHooks (h) {
const hooks = new Hooks()
hooks.onRequest = h.onRequest.slice()
hooks.preParsing = h.preParsing.slice()
hooks.preValidation = h.preValidation.slice()
hooks.preSerialization = h.preSerialization.slice()
hooks.preHandler = h.preHandler.slice()
hooks.onSend = h.onSend.slice()
hooks.onResponse = h.onResponse.slice()
hooks.onError = h.onError.slice()
hooks.onRoute = h.onRoute.slice()
hooks.onRegister = h.onRegister.slice()
hooks.onTimeout = h.onTimeout.slice()
hooks.onRequestAbort = h.onRequestAbort.slice()
hooks.onReady = []
hooks.onListen = []
hooks.preClose = []
return hooks
}
function hookRunnerApplication (hookName, boot, server, cb) {
const hooks = server[kHooks][hookName]
let i = 0
let c = 0
next()
function exit (err) {
const hookFnName = hooks[i - 1]?.name
const hookFnFragment = hookFnName ? ` "${hookFnName}"` : ''
if (err) {
if (err.code === 'AVV_ERR_READY_TIMEOUT') {
err = appendStackTrace(err, new FST_ERR_HOOK_TIMEOUT(hookName, hookFnFragment))
} else {
err = AVVIO_ERRORS_MAP[err.code] != null
? appendStackTrace(err, new AVVIO_ERRORS_MAP[err.code](err.message))
: err
}
cb(err)
return
}
cb()
}
function next (err) {
if (err) {
exit(err)
return
}
if (i === hooks.length && c === server[kChildren].length) {
if (i === 0 && c === 0) { // speed up start
exit()
} else {
// This is the last function executed for every fastify instance
boot(function manageTimeout (err, done) {
// this callback is needed by fastify to provide an hook interface without the error
// as first parameter and managing it on behalf the user
exit(err)
// this callback is needed by avvio to continue the loading of the next `register` plugins
done(err)
})
}
return
}
if (i === hooks.length && c < server[kChildren].length) {
const child = server[kChildren][c++]
hookRunnerApplication(hookName, boot, child, next)
return
}
boot(wrap(hooks[i++], server))
next()
}
function wrap (fn, server) {
return function (err, done) {
if (err) {
done(err)
return
}
if (fn.length === 1) {
try {
fn.call(server, done)
} catch (error) {
done(error)
}
return
}
try {
const ret = fn.call(server)
if (ret && typeof ret.then === 'function') {
ret.then(done, done)
return
}
} catch (error) {
err = error
}
done(err) // auto done
}
}
}
function onListenHookRunner (server) {
const hooks = server[kHooks].onListen
const hooksLen = hooks.length
let i = 0
let c = 0
next()
function next (err) {
err && server.log.error(err)
if (
i === hooksLen
) {
while (c < server[kChildren].length) {
const child = server[kChildren][c++]
onListenHookRunner(child)
}
return
}
wrap(hooks[i++], server, next)
}
async function wrap (fn, server, done) {
if (fn.length === 1) {
try {
fn.call(server, done)
} catch (e) {
done(e)
}
return
}
try {
const ret = fn.call(server)
if (ret && typeof ret.then === 'function') {
ret.then(done, done)
return
}
done()
} catch (error) {
done(error)
}
}
}
function hookRunnerGenerator (iterator) {
return function hookRunner (functions, request, reply, cb) {
let i = 0
function next (err) {
if (err || i === functions.length) {
cb(err, request, reply)
return
}
let result
try {
result = iterator(functions[i++], request, reply, next)
} catch (error) {
cb(error, request, reply)
return
}
if (result && typeof result.then === 'function') {
result.then(handleResolve, handleReject)
}
}
function handleResolve () {
next()
}
function handleReject (err) {
if (!err) {
err = new FST_ERR_SEND_UNDEFINED_ERR()
}
cb(err, request, reply)
}
next()
}
}
function onResponseHookIterator (fn, request, reply, next) {
return fn(request, reply, next)
}
const onResponseHookRunner = hookRunnerGenerator(onResponseHookIterator)
const preValidationHookRunner = hookRunnerGenerator(hookIterator)
const preHandlerHookRunner = hookRunnerGenerator(hookIterator)
const onTimeoutHookRunner = hookRunnerGenerator(hookIterator)
const onRequestHookRunner = hookRunnerGenerator(hookIterator)
function onSendHookRunner (functions, request, reply, payload, cb) {
let i = 0
function next (err, newPayload) {
if (err) {
cb(err, request, reply, payload)
return
}
if (newPayload !== undefined) {
payload = newPayload
}
if (i === functions.length) {
cb(null, request, reply, payload)
return
}
let result
try {
result = functions[i++](request, reply, payload, next)
} catch (error) {
cb(error, request, reply)
return
}
if (result && typeof result.then === 'function') {
result.then(handleResolve, handleReject)
}
}
function handleResolve (newPayload) {
next(null, newPayload)
}
function handleReject (err) {
if (!err) {
err = new FST_ERR_SEND_UNDEFINED_ERR()
}
cb(err, request, reply, payload)
}
next()
}
const preSerializationHookRunner = onSendHookRunner
function preParsingHookRunner (functions, request, reply, cb) {
let i = 0
function next (err, newPayload) {
if (reply.sent) {
return
}
if (newPayload !== undefined) {
request[kRequestPayloadStream] = newPayload
}
if (err || i === functions.length) {
cb(err, request, reply)
return
}
let result
try {
result = functions[i++](request, reply, request[kRequestPayloadStream], next)
} catch (error) {
cb(error, request, reply)
return
}
if (result && typeof result.then === 'function') {
result.then(handleResolve, handleReject)
}
}
function handleResolve (newPayload) {
next(null, newPayload)
}
function handleReject (err) {
if (!err) {
err = new FST_ERR_SEND_UNDEFINED_ERR()
}
cb(err, request, reply)
}
next()
}
function onRequestAbortHookRunner (functions, request, cb) {
let i = 0
function next (err) {
if (err || i === functions.length) {
cb(err, request)
return
}
let result
try {
result = functions[i++](request, next)
} catch (error) {
cb(error, request)
return
}
if (result && typeof result.then === 'function') {
result.then(handleResolve, handleReject)
}
}
function handleResolve () {
next()
}
function handleReject (err) {
if (!err) {
err = new FST_ERR_SEND_UNDEFINED_ERR()
}
cb(err, request)
}
next()
}
function hookIterator (fn, request, reply, next) {
if (reply.sent === true) return undefined
return fn(request, reply, next)
}
module.exports = {
Hooks,
buildHooks,
hookRunnerGenerator,
preParsingHookRunner,
onResponseHookRunner,
onSendHookRunner,
preSerializationHookRunner,
onRequestAbortHookRunner,
hookIterator,
hookRunnerApplication,
onListenHookRunner,
preHandlerHookRunner,
preValidationHookRunner,
onRequestHookRunner,
onTimeoutHookRunner,
lifecycleHooks,
supportedHooks
}

37
node_modules/fastify/lib/initialConfigValidation.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
'use strict'
const validate = require('./configValidator')
const deepClone = require('rfdc')({ circles: true, proto: false })
const { FST_ERR_INIT_OPTS_INVALID } = require('./errors')
function validateInitialConfig (options) {
const opts = deepClone(options)
if (!validate(opts)) {
const error = new FST_ERR_INIT_OPTS_INVALID(JSON.stringify(validate.errors.map(e => e.message)))
error.errors = validate.errors
throw error
}
return deepFreezeObject(opts)
}
function deepFreezeObject (object) {
const properties = Object.getOwnPropertyNames(object)
for (const name of properties) {
const value = object[name]
if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
continue
}
object[name] = value && typeof value === 'object' ? deepFreezeObject(value) : value
}
return Object.freeze(object)
}
module.exports = validateInitialConfig
module.exports.defaultInitOptions = validate.defaultInitOptions
module.exports.utils = { deepFreezeObject }

136
node_modules/fastify/lib/logger-factory.js generated vendored Normal file
View File

@@ -0,0 +1,136 @@
'use strict'
const {
FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED,
FST_ERR_LOG_INVALID_LOGGER_CONFIG,
FST_ERR_LOG_INVALID_LOGGER_INSTANCE,
FST_ERR_LOG_INVALID_LOGGER
} = require('./errors')
/**
* Utility for creating a child logger with the appropriate bindings, logger factory
* and validation.
* @param {object} context
* @param {import('../fastify').FastifyBaseLogger} logger
* @param {import('../fastify').RawRequestDefaultExpression<any>} req
* @param {string} reqId
* @param {import('../types/logger.js').ChildLoggerOptions?} loggerOpts
*
* @returns {object} New logger instance, inheriting all parent bindings,
* with child bindings added.
*/
function createChildLogger (context, logger, req, reqId, loggerOpts) {
const loggerBindings = {
[context.requestIdLogLabel]: reqId
}
const child = context.childLoggerFactory.call(context.server, logger, loggerBindings, loggerOpts || {}, req)
// Optimization: bypass validation if the factory is our own default factory
if (context.childLoggerFactory !== defaultChildLoggerFactory) {
validateLogger(child, true) // throw if the child is not a valid logger
}
return child
}
/** Default factory to create child logger instance
*
* @param {import('../fastify.js').FastifyBaseLogger} logger
* @param {import('../types/logger.js').Bindings} bindings
* @param {import('../types/logger.js').ChildLoggerOptions} opts
*
* @returns {import('../types/logger.js').FastifyBaseLogger}
*/
function defaultChildLoggerFactory (logger, bindings, opts) {
return logger.child(bindings, opts)
}
/**
* Determines if a provided logger object meets the requirements
* of a Fastify compatible logger.
*
* @param {object} logger Object to validate.
* @param {boolean?} strict `true` if the object must be a logger (always throw if any methods missing)
*
* @returns {boolean} `true` when the logger meets the requirements.
*
* @throws {FST_ERR_LOG_INVALID_LOGGER} When the logger object is
* missing required methods.
*/
function validateLogger (logger, strict) {
const methods = ['info', 'error', 'debug', 'fatal', 'warn', 'trace', 'child']
const missingMethods = logger
? methods.filter(method => !logger[method] || typeof logger[method] !== 'function')
: methods
if (!missingMethods.length) {
return true
} else if ((missingMethods.length === methods.length) && !strict) {
return false
} else {
throw FST_ERR_LOG_INVALID_LOGGER(missingMethods.join(','))
}
}
function createLogger (options) {
if (options.logger && options.loggerInstance) {
throw new FST_ERR_LOG_LOGGER_AND_LOGGER_INSTANCE_PROVIDED()
}
if (!options.loggerInstance && !options.logger) {
const nullLogger = require('abstract-logging')
const logger = nullLogger
logger.child = () => logger
return { logger, hasLogger: false }
}
const { createPinoLogger, serializers } = require('./logger-pino.js')
// check if the logger instance has all required properties
if (validateLogger(options.loggerInstance)) {
const logger = createPinoLogger({
logger: options.loggerInstance,
serializers: Object.assign({}, serializers, options.loggerInstance.serializers)
})
return { logger, hasLogger: true }
}
// if a logger instance is passed to logger, throw an exception
if (validateLogger(options.logger)) {
throw FST_ERR_LOG_INVALID_LOGGER_CONFIG()
}
if (options.loggerInstance) {
throw FST_ERR_LOG_INVALID_LOGGER_INSTANCE()
}
const localLoggerOptions = {}
if (Object.prototype.toString.call(options.logger) === '[object Object]') {
Reflect.ownKeys(options.logger).forEach(prop => {
Object.defineProperty(localLoggerOptions, prop, {
value: options.logger[prop],
writable: true,
enumerable: true,
configurable: true
})
})
}
localLoggerOptions.level = localLoggerOptions.level || 'info'
localLoggerOptions.serializers = Object.assign({}, serializers, localLoggerOptions.serializers)
options.logger = localLoggerOptions
const logger = createPinoLogger(options.logger)
return { logger, hasLogger: true }
}
function now () {
const ts = process.hrtime()
return (ts[0] * 1e3) + (ts[1] / 1e6)
}
module.exports = {
createChildLogger,
defaultChildLoggerFactory,
createLogger,
validateLogger,
now
}

68
node_modules/fastify/lib/logger-pino.js generated vendored Normal file
View File

@@ -0,0 +1,68 @@
'use strict'
/**
* Code imported from `pino-http`
* Repo: https://github.com/pinojs/pino-http
* License: MIT (https://raw.githubusercontent.com/pinojs/pino-http/master/LICENSE)
*/
const pino = require('pino')
const { serializersSym } = pino.symbols
const {
FST_ERR_LOG_INVALID_DESTINATION
} = require('./errors')
function createPinoLogger (opts) {
if (opts.stream && opts.file) {
throw new FST_ERR_LOG_INVALID_DESTINATION()
} else if (opts.file) {
// we do not have stream
opts.stream = pino.destination(opts.file)
delete opts.file
}
const prevLogger = opts.logger
const prevGenReqId = opts.genReqId
let logger = null
if (prevLogger) {
opts.logger = undefined
opts.genReqId = undefined
// we need to tap into pino internals because in v5 it supports
// adding serializers in child loggers
if (prevLogger[serializersSym]) {
opts.serializers = Object.assign({}, opts.serializers, prevLogger[serializersSym])
}
logger = prevLogger.child({}, opts)
opts.logger = prevLogger
opts.genReqId = prevGenReqId
} else {
logger = pino(opts, opts.stream)
}
return logger
}
const serializers = {
req: function asReqValue (req) {
return {
method: req.method,
url: req.url,
version: req.headers && req.headers['accept-version'],
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket ? req.socket.remotePort : undefined
}
},
err: pino.stdSerializers.err,
res: function asResValue (reply) {
return {
statusCode: reply.statusCode
}
}
}
module.exports = {
serializers,
createPinoLogger
}

10
node_modules/fastify/lib/noop-set.js generated vendored Normal file
View File

@@ -0,0 +1,10 @@
'use strict'
module.exports = function noopSet () {
return {
[Symbol.iterator]: function * () {},
add () {},
delete () {},
has () { return true }
}
}

90
node_modules/fastify/lib/pluginOverride.js generated vendored Normal file
View File

@@ -0,0 +1,90 @@
'use strict'
const {
kAvvioBoot,
kChildren,
kRoutePrefix,
kLogLevel,
kLogSerializers,
kHooks,
kSchemaController,
kContentTypeParser,
kReply,
kRequest,
kFourOhFour,
kPluginNameChain,
kErrorHandlerAlreadySet
} = require('./symbols.js')
const Reply = require('./reply')
const Request = require('./request')
const SchemaController = require('./schema-controller')
const ContentTypeParser = require('./contentTypeParser')
const { buildHooks } = require('./hooks')
const pluginUtils = require('./pluginUtils')
// Function that runs the encapsulation magic.
// Everything that need to be encapsulated must be handled in this function.
module.exports = function override (old, fn, opts) {
const shouldSkipOverride = pluginUtils.registerPlugin.call(old, fn)
const fnName = pluginUtils.getPluginName(fn) || pluginUtils.getFuncPreview(fn)
if (shouldSkipOverride) {
// after every plugin registration we will enter a new name
old[kPluginNameChain].push(fnName)
return old
}
const instance = Object.create(old)
old[kChildren].push(instance)
instance.ready = old[kAvvioBoot].bind(instance)
instance[kChildren] = []
instance[kReply] = Reply.buildReply(instance[kReply])
instance[kRequest] = Request.buildRequest(instance[kRequest])
instance[kContentTypeParser] = ContentTypeParser.helpers.buildContentTypeParser(instance[kContentTypeParser])
instance[kHooks] = buildHooks(instance[kHooks])
instance[kRoutePrefix] = buildRoutePrefix(instance[kRoutePrefix], opts.prefix)
instance[kLogLevel] = opts.logLevel || instance[kLogLevel]
instance[kSchemaController] = SchemaController.buildSchemaController(old[kSchemaController])
instance.getSchema = instance[kSchemaController].getSchema.bind(instance[kSchemaController])
instance.getSchemas = instance[kSchemaController].getSchemas.bind(instance[kSchemaController])
// Track the registered and loaded plugins since the root instance.
// It does not track the current encapsulated plugin.
instance[pluginUtils.kRegisteredPlugins] = Object.create(instance[pluginUtils.kRegisteredPlugins])
// Track the plugin chain since the root instance.
// When an non-encapsulated plugin is added, the chain will be updated.
instance[kPluginNameChain] = [fnName]
instance[kErrorHandlerAlreadySet] = false
if (instance[kLogSerializers] || opts.logSerializers) {
instance[kLogSerializers] = Object.assign(Object.create(instance[kLogSerializers]), opts.logSerializers)
}
if (opts.prefix) {
instance[kFourOhFour].arrange404(instance)
}
for (const hook of instance[kHooks].onRegister) hook.call(old, instance, opts)
return instance
}
function buildRoutePrefix (instancePrefix, pluginPrefix) {
if (!pluginPrefix) {
return instancePrefix
}
// Ensure that there is a '/' between the prefixes
if (instancePrefix.endsWith('/') && pluginPrefix[0] === '/') {
// Remove the extra '/' to avoid: '/first//second'
pluginPrefix = pluginPrefix.slice(1)
} else if (pluginPrefix[0] !== '/') {
pluginPrefix = '/' + pluginPrefix
}
return instancePrefix + pluginPrefix
}

169
node_modules/fastify/lib/pluginUtils.js generated vendored Normal file
View File

@@ -0,0 +1,169 @@
'use strict'
const semver = require('semver')
const assert = require('node:assert')
const kRegisteredPlugins = Symbol.for('registered-plugin')
const {
kTestInternals
} = require('./symbols.js')
const { exist, existReply, existRequest } = require('./decorate')
const {
FST_ERR_PLUGIN_VERSION_MISMATCH,
FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE,
FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER
} = require('./errors')
const rcRegex = /-(?:rc|pre|alpha).+$/u
function getMeta (fn) {
return fn[Symbol.for('plugin-meta')]
}
function getPluginName (func) {
const display = getDisplayName(func)
if (display) {
return display
}
// let's see if this is a file, and in that case use that
// this is common for plugins
const cache = require.cache
// cache is undefined inside SEA
if (cache) {
const keys = Object.keys(cache)
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
if (cache[key].exports === func) {
return key
}
}
}
// if not maybe it's a named function, so use that
if (func.name) {
return func.name
}
return null
}
function getFuncPreview (func) {
// takes the first two lines of the function if nothing else works
return func.toString().split('\n', 2).map(s => s.trim()).join(' -- ')
}
function getDisplayName (fn) {
return fn[Symbol.for('fastify.display-name')]
}
function shouldSkipOverride (fn) {
return !!fn[Symbol.for('skip-override')]
}
function checkDependencies (fn) {
const meta = getMeta(fn)
if (!meta) return
const dependencies = meta.dependencies
if (!dependencies) return
assert(Array.isArray(dependencies), 'The dependencies should be an array of strings')
dependencies.forEach(dependency => {
assert(
this[kRegisteredPlugins].indexOf(dependency) > -1,
`The dependency '${dependency}' of plugin '${meta.name}' is not registered`
)
})
}
function checkDecorators (fn) {
const meta = getMeta(fn)
if (!meta) return
const { decorators, name } = meta
if (!decorators) return
if (decorators.fastify) _checkDecorators(this, 'Fastify', decorators.fastify, name)
if (decorators.reply) _checkDecorators(this, 'Reply', decorators.reply, name)
if (decorators.request) _checkDecorators(this, 'Request', decorators.request, name)
}
const checks = {
Fastify: exist,
Request: existRequest,
Reply: existReply
}
function _checkDecorators (that, instance, decorators, name) {
assert(Array.isArray(decorators), 'The decorators should be an array of strings')
decorators.forEach(decorator => {
const withPluginName = typeof name === 'string' ? ` required by '${name}'` : ''
if (!checks[instance].call(that, decorator)) {
throw new FST_ERR_PLUGIN_NOT_PRESENT_IN_INSTANCE(decorator, withPluginName, instance)
}
})
}
function checkVersion (fn) {
const meta = getMeta(fn)
if (meta?.fastify == null) return
const requiredVersion = meta.fastify
const fastifyRc = rcRegex.test(this.version)
if (fastifyRc === true && semver.gt(this.version, semver.coerce(requiredVersion)) === true) {
// A Fastify release candidate phase is taking place. In order to reduce
// the effort needed to test plugins with the RC, we allow plugins targeting
// the prior Fastify release to be loaded.
return
}
if (requiredVersion && semver.satisfies(this.version, requiredVersion, { includePrerelease: fastifyRc }) === false) {
// We are not in a release candidate phase. Thus, we must honor the semver
// ranges defined by the plugin's metadata. Which is to say, if the plugin
// expects an older version of Fastify than the _current_ version, we will
// throw an error.
throw new FST_ERR_PLUGIN_VERSION_MISMATCH(meta.name, requiredVersion, this.version)
}
}
function registerPluginName (fn) {
const meta = getMeta(fn)
if (!meta) return
const name = meta.name
if (!name) return
this[kRegisteredPlugins].push(name)
return name
}
function checkPluginHealthiness (fn, pluginName) {
if (fn.constructor.name === 'AsyncFunction' && fn.length === 3) {
throw new FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER(pluginName)
}
}
function registerPlugin (fn) {
const pluginName = registerPluginName.call(this, fn) || getPluginName(fn)
checkPluginHealthiness.call(this, fn, pluginName)
checkVersion.call(this, fn)
checkDecorators.call(this, fn)
checkDependencies.call(this, fn)
return shouldSkipOverride(fn)
}
module.exports = {
getPluginName,
getFuncPreview,
kRegisteredPlugins,
getDisplayName,
registerPlugin
}
module.exports[kTestInternals] = {
shouldSkipOverride,
getMeta,
checkDecorators,
checkDependencies
}

23
node_modules/fastify/lib/promise.js generated vendored Normal file
View File

@@ -0,0 +1,23 @@
'use strict'
const { kTestInternals } = require('./symbols')
function withResolvers () {
let res, rej
const promise = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
return { promise, resolve: res, reject: rej }
}
module.exports = {
// TODO(20.x): remove when node@20 is not supported
withResolvers: typeof Promise.withResolvers === 'function'
? Promise.withResolvers.bind(Promise) // Promise.withResolvers must bind to itself
/* c8 ignore next */
: withResolvers, // Tested using the kTestInternals
[kTestInternals]: {
withResolvers
}
}

942
node_modules/fastify/lib/reply.js generated vendored Normal file
View File

@@ -0,0 +1,942 @@
'use strict'
const eos = require('node:stream').finished
const Readable = require('node:stream').Readable
const {
kFourOhFourContext,
kReplyErrorHandlerCalled,
kReplyHijacked,
kReplyStartTime,
kReplyEndTime,
kReplySerializer,
kReplySerializerDefault,
kReplyIsError,
kReplyHeaders,
kReplyTrailers,
kReplyHasStatusCode,
kReplyIsRunningOnErrorHook,
kReplyNextErrorHandler,
kDisableRequestLogging,
kSchemaResponse,
kReplyCacheSerializeFns,
kSchemaController,
kOptions,
kRouteContext
} = require('./symbols.js')
const {
onSendHookRunner,
onResponseHookRunner,
preHandlerHookRunner,
preSerializationHookRunner
} = require('./hooks')
const internals = require('./handleRequest')[Symbol.for('internals')]
const loggerUtils = require('./logger-factory')
const now = loggerUtils.now
const { handleError } = require('./error-handler')
const { getSchemaSerializer } = require('./schemas')
const CONTENT_TYPE = {
JSON: 'application/json; charset=utf-8',
PLAIN: 'text/plain; charset=utf-8',
OCTET: 'application/octet-stream'
}
const {
FST_ERR_REP_INVALID_PAYLOAD_TYPE,
FST_ERR_REP_RESPONSE_BODY_CONSUMED,
FST_ERR_REP_READABLE_STREAM_LOCKED,
FST_ERR_REP_ALREADY_SENT,
FST_ERR_SEND_INSIDE_ONERR,
FST_ERR_BAD_STATUS_CODE,
FST_ERR_BAD_TRAILER_NAME,
FST_ERR_BAD_TRAILER_VALUE,
FST_ERR_MISSING_SERIALIZATION_FN,
FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN,
FST_ERR_DEC_UNDECLARED
} = require('./errors')
const decorators = require('./decorate')
const toString = Object.prototype.toString
function Reply (res, request, log) {
this.raw = res
this[kReplySerializer] = null
this[kReplyErrorHandlerCalled] = false
this[kReplyIsError] = false
this[kReplyIsRunningOnErrorHook] = false
this.request = request
this[kReplyHeaders] = {}
this[kReplyTrailers] = null
this[kReplyHasStatusCode] = false
this[kReplyStartTime] = undefined
this.log = log
}
Reply.props = []
Object.defineProperties(Reply.prototype, {
[kRouteContext]: {
get () {
return this.request[kRouteContext]
}
},
elapsedTime: {
get () {
if (this[kReplyStartTime] === undefined) {
return 0
}
return (this[kReplyEndTime] || now()) - this[kReplyStartTime]
}
},
server: {
get () {
return this.request[kRouteContext].server
}
},
sent: {
enumerable: true,
get () {
// We are checking whether reply was hijacked or the response has ended.
return (this[kReplyHijacked] || this.raw.writableEnded) === true
}
},
statusCode: {
get () {
return this.raw.statusCode
},
set (value) {
this.code(value)
}
},
routeOptions: {
get () {
return this.request.routeOptions
}
}
})
Reply.prototype.writeEarlyHints = function (hints, callback) {
this.raw.writeEarlyHints(hints, callback)
return this
}
Reply.prototype.hijack = function () {
this[kReplyHijacked] = true
return this
}
Reply.prototype.send = function (payload) {
if (this[kReplyIsRunningOnErrorHook]) {
throw new FST_ERR_SEND_INSIDE_ONERR()
}
if (this.sent === true) {
this.log.warn({ err: new FST_ERR_REP_ALREADY_SENT(this.request.url, this.request.method) })
return this
}
if (this[kReplyIsError] || payload instanceof Error) {
this[kReplyIsError] = false
onErrorHook(this, payload, onSendHook)
return this
}
if (payload === undefined) {
onSendHook(this, payload)
return this
}
const contentType = this.getHeader('content-type')
const hasContentType = contentType !== undefined
if (payload !== null) {
if (
// node:stream
typeof payload.pipe === 'function' ||
// node:stream/web
typeof payload.getReader === 'function' ||
// Response
toString.call(payload) === '[object Response]'
) {
onSendHook(this, payload)
return this
}
if (payload.buffer instanceof ArrayBuffer) {
if (!hasContentType) {
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.OCTET
}
const payloadToSend = Buffer.isBuffer(payload) ? payload : Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength)
onSendHook(this, payloadToSend)
return this
}
if (!hasContentType && typeof payload === 'string') {
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.PLAIN
onSendHook(this, payload)
return this
}
}
if (this[kReplySerializer] !== null) {
if (typeof payload !== 'string') {
preSerializationHook(this, payload)
return this
}
payload = this[kReplySerializer](payload)
// The indexOf below also matches custom json mimetypes such as 'application/hal+json' or 'application/ld+json'
} else if (!hasContentType || contentType.indexOf('json') !== -1) {
if (!hasContentType) {
this[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON
} else if (contentType.indexOf('charset') === -1) {
// If user doesn't set charset, we will set charset to utf-8
const customContentType = contentType.trim()
if (customContentType.endsWith(';')) {
// custom content-type is ended with ';'
this[kReplyHeaders]['content-type'] = `${customContentType} charset=utf-8`
} else {
this[kReplyHeaders]['content-type'] = `${customContentType}; charset=utf-8`
}
}
if (typeof payload !== 'string') {
preSerializationHook(this, payload)
return this
}
}
onSendHook(this, payload)
return this
}
Reply.prototype.getHeader = function (key) {
key = key.toLowerCase()
const value = this[kReplyHeaders][key]
return value !== undefined ? value : this.raw.getHeader(key)
}
Reply.prototype.getHeaders = function () {
return {
...this.raw.getHeaders(),
...this[kReplyHeaders]
}
}
Reply.prototype.hasHeader = function (key) {
key = key.toLowerCase()
return this[kReplyHeaders][key] !== undefined || this.raw.hasHeader(key)
}
Reply.prototype.removeHeader = function (key) {
// Node.js does not like headers with keys set to undefined,
// so we have to delete the key.
delete this[kReplyHeaders][key.toLowerCase()]
return this
}
Reply.prototype.header = function (key, value = '') {
key = key.toLowerCase()
if (this[kReplyHeaders][key] && key === 'set-cookie') {
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.2
if (typeof this[kReplyHeaders][key] === 'string') {
this[kReplyHeaders][key] = [this[kReplyHeaders][key]]
}
if (Array.isArray(value)) {
Array.prototype.push.apply(this[kReplyHeaders][key], value)
} else {
this[kReplyHeaders][key].push(value)
}
} else {
this[kReplyHeaders][key] = value
}
return this
}
Reply.prototype.headers = function (headers) {
const keys = Object.keys(headers)
for (let i = 0; i !== keys.length; ++i) {
const key = keys[i]
this.header(key, headers[key])
}
return this
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer#directives
// https://datatracker.ietf.org/doc/html/rfc7230.html#chunked.trailer.part
const INVALID_TRAILERS = new Set([
'transfer-encoding',
'content-length',
'host',
'cache-control',
'max-forwards',
'te',
'authorization',
'set-cookie',
'content-encoding',
'content-type',
'content-range',
'trailer'
])
Reply.prototype.trailer = function (key, fn) {
key = key.toLowerCase()
if (INVALID_TRAILERS.has(key)) {
throw new FST_ERR_BAD_TRAILER_NAME(key)
}
if (typeof fn !== 'function') {
throw new FST_ERR_BAD_TRAILER_VALUE(key, typeof fn)
}
if (this[kReplyTrailers] === null) this[kReplyTrailers] = {}
this[kReplyTrailers][key] = fn
return this
}
Reply.prototype.hasTrailer = function (key) {
return this[kReplyTrailers]?.[key.toLowerCase()] !== undefined
}
Reply.prototype.removeTrailer = function (key) {
if (this[kReplyTrailers] === null) return this
this[kReplyTrailers][key.toLowerCase()] = undefined
return this
}
Reply.prototype.code = function (code) {
const statusCode = +code
if (!(statusCode >= 100 && statusCode <= 599)) {
throw new FST_ERR_BAD_STATUS_CODE(code || String(code))
}
this.raw.statusCode = statusCode
this[kReplyHasStatusCode] = true
return this
}
Reply.prototype.status = Reply.prototype.code
Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType) {
let serialize
if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
if (typeof contentType === 'string') {
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]?.[contentType]
} else {
serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
}
} else if (typeof schemaOrStatus === 'object') {
serialize = this[kRouteContext][kReplyCacheSerializeFns]?.get(schemaOrStatus)
}
return serialize
}
Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null, contentType = null) {
const { request } = this
const { method, url } = request
// Check if serialize function already compiled
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
return this[kRouteContext][kReplyCacheSerializeFns].get(schema)
}
const serializerCompiler = this[kRouteContext].serializerCompiler ||
this.server[kSchemaController].serializerCompiler ||
(
// We compile the schemas if no custom serializerCompiler is provided
// nor set
this.server[kSchemaController].setupSerializer(this.server[kOptions]) ||
this.server[kSchemaController].serializerCompiler
)
const serializeFn = serializerCompiler({
schema,
method,
url,
httpStatus,
contentType
})
// We create a WeakMap to compile the schema only once
// Its done lazily to avoid add overhead by creating the WeakMap
// if it is not used
// TODO: Explore a central cache for all the schemas shared across
// encapsulated contexts
if (this[kRouteContext][kReplyCacheSerializeFns] == null) {
this[kRouteContext][kReplyCacheSerializeFns] = new WeakMap()
}
this[kRouteContext][kReplyCacheSerializeFns].set(schema, serializeFn)
return serializeFn
}
Reply.prototype.serializeInput = function (input, schema, httpStatus, contentType) {
const possibleContentType = httpStatus
let serialize
httpStatus = typeof schema === 'string' || typeof schema === 'number'
? schema
: httpStatus
contentType = httpStatus && possibleContentType !== httpStatus
? possibleContentType
: contentType
if (httpStatus != null) {
if (contentType != null) {
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]?.[contentType]
} else {
serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]
}
if (serialize == null) {
if (contentType) throw new FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN(httpStatus, contentType)
throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
}
} else {
// Check if serialize function already compiled
if (this[kRouteContext][kReplyCacheSerializeFns]?.has(schema)) {
serialize = this[kRouteContext][kReplyCacheSerializeFns].get(schema)
} else {
serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
}
}
return serialize(input)
}
Reply.prototype.serialize = function (payload) {
if (this[kReplySerializer] !== null) {
return this[kReplySerializer](payload)
} else {
if (this[kRouteContext] && this[kRouteContext][kReplySerializerDefault]) {
return this[kRouteContext][kReplySerializerDefault](payload, this.raw.statusCode)
} else {
return serialize(this[kRouteContext], payload, this.raw.statusCode)
}
}
}
Reply.prototype.serializer = function (fn) {
this[kReplySerializer] = fn
return this
}
Reply.prototype.type = function (type) {
this[kReplyHeaders]['content-type'] = type
return this
}
Reply.prototype.redirect = function (url, code) {
if (!code) {
code = this[kReplyHasStatusCode] ? this.raw.statusCode : 302
}
return this.header('location', url).code(code).send()
}
Reply.prototype.callNotFound = function () {
notFound(this)
return this
}
// Make reply a thenable, so it could be used with async/await.
// See
// - https://github.com/fastify/fastify/issues/1864 for the discussions
// - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then for the signature
Reply.prototype.then = function (fulfilled, rejected) {
if (this.sent) {
fulfilled()
return
}
eos(this.raw, (err) => {
// We must not treat ERR_STREAM_PREMATURE_CLOSE as
// an error because it is created by eos, not by the stream.
if (err && err.code !== 'ERR_STREAM_PREMATURE_CLOSE') {
if (rejected) {
rejected(err)
} else {
this.log && this.log.warn('unhandled rejection on reply.then')
}
} else {
fulfilled()
}
})
}
Reply.prototype.getDecorator = function (name) {
if (!decorators.hasKey(this, name) && !decorators.exist(this, name)) {
throw new FST_ERR_DEC_UNDECLARED(name, 'reply')
}
const decorator = this[name]
if (typeof decorator === 'function') {
return decorator.bind(this)
}
return decorator
}
function preSerializationHook (reply, payload) {
if (reply[kRouteContext].preSerialization !== null) {
preSerializationHookRunner(
reply[kRouteContext].preSerialization,
reply.request,
reply,
payload,
preSerializationHookEnd
)
} else {
preSerializationHookEnd(null, undefined, reply, payload)
}
}
function preSerializationHookEnd (err, _request, reply, payload) {
if (err != null) {
onErrorHook(reply, err)
return
}
try {
if (reply[kReplySerializer] !== null) {
payload = reply[kReplySerializer](payload)
} else if (reply[kRouteContext] && reply[kRouteContext][kReplySerializerDefault]) {
payload = reply[kRouteContext][kReplySerializerDefault](payload, reply.raw.statusCode)
} else {
payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode, reply[kReplyHeaders]['content-type'])
}
} catch (e) {
wrapSerializationError(e, reply)
onErrorHook(reply, e)
return
}
onSendHook(reply, payload)
}
function wrapSerializationError (error, reply) {
error.serialization = reply[kRouteContext].config
}
function onSendHook (reply, payload) {
if (reply[kRouteContext].onSend !== null) {
onSendHookRunner(
reply[kRouteContext].onSend,
reply.request,
reply,
payload,
wrapOnSendEnd
)
} else {
onSendEnd(reply, payload)
}
}
function wrapOnSendEnd (err, request, reply, payload) {
if (err != null) {
onErrorHook(reply, err)
} else {
onSendEnd(reply, payload)
}
}
function safeWriteHead (reply, statusCode) {
const res = reply.raw
try {
res.writeHead(statusCode, reply[kReplyHeaders])
} catch (err) {
if (err.code === 'ERR_HTTP_HEADERS_SENT') {
reply.log.warn(`Reply was already sent, did you forget to "return reply" in the "${reply.request.raw.url}" (${reply.request.raw.method}) route?`)
}
throw err
}
}
function onSendEnd (reply, payload) {
const res = reply.raw
const req = reply.request
// we check if we need to update the trailers header and set it
if (reply[kReplyTrailers] !== null) {
const trailerHeaders = Object.keys(reply[kReplyTrailers])
let header = ''
for (const trailerName of trailerHeaders) {
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
header += ' '
header += trailerName
}
// it must be chunked for trailer to work
reply.header('Transfer-Encoding', 'chunked')
reply.header('Trailer', header.trim())
}
// since Response contain status code, headers and body,
// we need to update the status, add the headers and use it's body as payload
// before continuing
if (toString.call(payload) === '[object Response]') {
// https://developer.mozilla.org/en-US/docs/Web/API/Response/status
if (typeof payload.status === 'number') {
reply.code(payload.status)
}
// https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
if (typeof payload.headers === 'object' && typeof payload.headers.forEach === 'function') {
for (const [headerName, headerValue] of payload.headers) {
reply.header(headerName, headerValue)
}
}
// https://developer.mozilla.org/en-US/docs/Web/API/Response/body
if (payload.body !== null) {
if (payload.bodyUsed) {
throw new FST_ERR_REP_RESPONSE_BODY_CONSUMED()
}
}
// Keep going, body is either null or ReadableStream
payload = payload.body
}
const statusCode = res.statusCode
if (payload === undefined || payload === null) {
// according to https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
// we cannot send a content-length for 304 and 204, and all status code
// < 200
// A sender MUST NOT send a Content-Length header field in any message
// that contains a Transfer-Encoding header field.
// For HEAD we don't overwrite the `content-length`
if (statusCode >= 200 && statusCode !== 204 && statusCode !== 304 && req.method !== 'HEAD' && reply[kReplyTrailers] === null) {
reply[kReplyHeaders]['content-length'] = '0'
}
safeWriteHead(reply, statusCode)
sendTrailer(payload, res, reply)
return
}
if ((statusCode >= 100 && statusCode < 200) || statusCode === 204) {
// Responses without a content body must not send content-type
// or content-length headers.
// See https://www.rfc-editor.org/rfc/rfc9110.html#section-8.6.
reply.removeHeader('content-type')
reply.removeHeader('content-length')
safeWriteHead(reply, statusCode)
sendTrailer(undefined, res, reply)
if (typeof payload.resume === 'function') {
payload.on('error', noop)
payload.resume()
}
return
}
// node:stream
if (typeof payload.pipe === 'function') {
sendStream(payload, res, reply)
return
}
// node:stream/web
if (typeof payload.getReader === 'function') {
sendWebStream(payload, res, reply)
return
}
if (typeof payload !== 'string' && !Buffer.isBuffer(payload)) {
throw new FST_ERR_REP_INVALID_PAYLOAD_TYPE(typeof payload)
}
if (reply[kReplyTrailers] === null) {
const contentLength = reply[kReplyHeaders]['content-length']
if (!contentLength ||
(req.raw.method !== 'HEAD' &&
Number(contentLength) !== Buffer.byteLength(payload)
)
) {
reply[kReplyHeaders]['content-length'] = '' + Buffer.byteLength(payload)
}
}
safeWriteHead(reply, statusCode)
// write payload first
res.write(payload)
// then send trailers
sendTrailer(payload, res, reply)
}
function logStreamError (logger, err, res) {
if (err.code === 'ERR_STREAM_PREMATURE_CLOSE') {
if (!logger[kDisableRequestLogging]) {
logger.info({ res }, 'stream closed prematurely')
}
} else {
logger.warn({ err }, 'response terminated with an error with headers already sent')
}
}
function sendWebStream (payload, res, reply) {
if (payload.locked) {
throw FST_ERR_REP_READABLE_STREAM_LOCKED()
}
const nodeStream = Readable.fromWeb(payload)
sendStream(nodeStream, res, reply)
}
function sendStream (payload, res, reply) {
let sourceOpen = true
let errorLogged = false
// set trailer when stream ended
sendStreamTrailer(payload, res, reply)
eos(payload, { readable: true, writable: false }, function (err) {
sourceOpen = false
if (err != null) {
if (res.headersSent || reply.request.raw.aborted === true) {
if (!errorLogged) {
errorLogged = true
logStreamError(reply.log, err, reply)
}
res.destroy()
} else {
onErrorHook(reply, err)
}
}
// there is nothing to do if there is not an error
})
eos(res, function (err) {
if (sourceOpen) {
if (err != null && res.headersSent && !errorLogged) {
errorLogged = true
logStreamError(reply.log, err, res)
}
if (typeof payload.destroy === 'function') {
payload.destroy()
} else if (typeof payload.close === 'function') {
payload.close(noop)
} else if (typeof payload.abort === 'function') {
payload.abort()
} else {
reply.log.warn('stream payload does not end properly')
}
}
})
// streams will error asynchronously, and we want to handle that error
// appropriately, e.g. a 404 for a missing file. So we cannot use
// writeHead, and we need to resort to setHeader, which will trigger
// a writeHead when there is data to send.
if (!res.headersSent) {
for (const key in reply[kReplyHeaders]) {
res.setHeader(key, reply[kReplyHeaders][key])
}
} else {
reply.log.warn('response will send, but you shouldn\'t use res.writeHead in stream mode')
}
payload.pipe(res)
}
function sendTrailer (payload, res, reply) {
if (reply[kReplyTrailers] === null) {
// when no trailer, we close the stream
res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
return
}
const trailerHeaders = Object.keys(reply[kReplyTrailers])
const trailers = {}
let handled = 0
let skipped = true
function send () {
// add trailers when all handler handled
/* istanbul ignore else */
if (handled === 0) {
res.addTrailers(trailers)
// we need to properly close the stream
// after trailers sent
res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
}
}
for (const trailerName of trailerHeaders) {
if (typeof reply[kReplyTrailers][trailerName] !== 'function') continue
skipped = false
handled--
function cb (err, value) {
// TODO: we may protect multiple callback calls
// or mixing async-await with callback
handled++
// we can safely ignore error for trailer
// since it does affect the client
// we log in here only for debug usage
if (err) reply.log.debug(err)
else trailers[trailerName] = value
// we push the check to the end of event
// loop, so the registration continue to
// process.
process.nextTick(send)
}
const result = reply[kReplyTrailers][trailerName](reply, payload, cb)
if (typeof result === 'object' && typeof result.then === 'function') {
result.then((v) => cb(null, v), cb)
}
}
// when all trailers are skipped
// we need to close the stream
if (skipped) res.end(null, null, null) // avoid ArgumentsAdaptorTrampoline from V8
}
function sendStreamTrailer (payload, res, reply) {
if (reply[kReplyTrailers] === null) return
payload.on('end', () => sendTrailer(null, res, reply))
}
function onErrorHook (reply, error, cb) {
if (reply[kRouteContext].onError !== null && !reply[kReplyNextErrorHandler]) {
reply[kReplyIsRunningOnErrorHook] = true
onSendHookRunner(
reply[kRouteContext].onError,
reply.request,
reply,
error,
() => handleError(reply, error, cb)
)
} else {
handleError(reply, error, cb)
}
}
function setupResponseListeners (reply) {
reply[kReplyStartTime] = now()
const onResFinished = err => {
reply[kReplyEndTime] = now()
reply.raw.removeListener('finish', onResFinished)
reply.raw.removeListener('error', onResFinished)
const ctx = reply[kRouteContext]
if (ctx && ctx.onResponse !== null) {
onResponseHookRunner(
ctx.onResponse,
reply.request,
reply,
onResponseCallback
)
} else {
onResponseCallback(err, reply.request, reply)
}
}
reply.raw.on('finish', onResFinished)
reply.raw.on('error', onResFinished)
}
function onResponseCallback (err, request, reply) {
if (reply.log[kDisableRequestLogging]) {
return
}
const responseTime = reply.elapsedTime
if (err != null) {
reply.log.error({
res: reply,
err,
responseTime
}, 'request errored')
return
}
reply.log.info({
res: reply,
responseTime
}, 'request completed')
}
function buildReply (R) {
const props = R.props.slice()
function _Reply (res, request, log) {
this.raw = res
this[kReplyIsError] = false
this[kReplyErrorHandlerCalled] = false
this[kReplyHijacked] = false
this[kReplySerializer] = null
this.request = request
this[kReplyHeaders] = {}
this[kReplyTrailers] = null
this[kReplyStartTime] = undefined
this[kReplyEndTime] = undefined
this.log = log
let prop
for (let i = 0; i < props.length; i++) {
prop = props[i]
this[prop.key] = prop.value
}
}
Object.setPrototypeOf(_Reply.prototype, R.prototype)
Object.setPrototypeOf(_Reply, R)
_Reply.parent = R
_Reply.props = props
return _Reply
}
function notFound (reply) {
if (reply[kRouteContext][kFourOhFourContext] === null) {
reply.log.warn('Trying to send a NotFound error inside a 404 handler. Sending basic 404 response.')
reply.code(404).send('404 Not Found')
return
}
reply.request[kRouteContext] = reply[kRouteContext][kFourOhFourContext]
// preHandler hook
if (reply[kRouteContext].preHandler !== null) {
preHandlerHookRunner(
reply[kRouteContext].preHandler,
reply.request,
reply,
internals.preHandlerCallback
)
} else {
internals.preHandlerCallback(null, reply.request, reply)
}
}
/**
* This function runs when a payload that is not a string|buffer|stream or null
* should be serialized to be streamed to the response.
* This is the default serializer that can be customized by the user using the replySerializer
*
* @param {object} context the request context
* @param {object} data the JSON payload to serialize
* @param {number} statusCode the http status code
* @param {string} [contentType] the reply content type
* @returns {string} the serialized payload
*/
function serialize (context, data, statusCode, contentType) {
const fnSerialize = getSchemaSerializer(context, statusCode, contentType)
if (fnSerialize) {
return fnSerialize(data)
}
return JSON.stringify(data)
}
function noop () { }
module.exports = Reply
module.exports.buildReply = buildReply
module.exports.setupResponseListeners = setupResponseListeners

52
node_modules/fastify/lib/reqIdGenFactory.js generated vendored Normal file
View File

@@ -0,0 +1,52 @@
'use strict'
/**
* @callback GenerateRequestId
* @param {Object} req
* @returns {string}
*/
/**
* @param {string} [requestIdHeader]
* @param {GenerateRequestId} [optGenReqId]
* @returns {GenerateRequestId}
*/
function reqIdGenFactory (requestIdHeader, optGenReqId) {
const genReqId = optGenReqId || buildDefaultGenReqId()
if (requestIdHeader) {
return buildOptionalHeaderReqId(requestIdHeader, genReqId)
}
return genReqId
}
function getGenReqId (contextServer, req) {
return contextServer.genReqId(req)
}
function buildDefaultGenReqId () {
// 2,147,483,647 (2^31 1) stands for max SMI value (an internal optimization of V8).
// With this upper bound, if you'll be generating 1k ids/sec, you're going to hit it in ~25 days.
// This is very likely to happen in real-world applications, hence the limit is enforced.
// Growing beyond this value will make the id generation slower and cause a deopt.
// In the worst cases, it will become a float, losing accuracy.
const maxInt = 2147483647
let nextReqId = 0
return function defaultGenReqId () {
nextReqId = (nextReqId + 1) & maxInt
return `req-${nextReqId.toString(36)}`
}
}
function buildOptionalHeaderReqId (requestIdHeader, genReqId) {
return function (req) {
return req.headers[requestIdHeader] || genReqId(req)
}
}
module.exports = {
getGenReqId,
reqIdGenFactory
}

369
node_modules/fastify/lib/request.js generated vendored Normal file
View File

@@ -0,0 +1,369 @@
'use strict'
const proxyAddr = require('@fastify/proxy-addr')
const {
kHasBeenDecorated,
kSchemaBody,
kSchemaHeaders,
kSchemaParams,
kSchemaQuerystring,
kSchemaController,
kOptions,
kRequestCacheValidateFns,
kRouteContext,
kRequestOriginalUrl
} = require('./symbols')
const { FST_ERR_REQ_INVALID_VALIDATION_INVOCATION, FST_ERR_DEC_UNDECLARED } = require('./errors')
const decorators = require('./decorate')
const HTTP_PART_SYMBOL_MAP = {
body: kSchemaBody,
headers: kSchemaHeaders,
params: kSchemaParams,
querystring: kSchemaQuerystring,
query: kSchemaQuerystring
}
function Request (id, params, req, query, log, context) {
this.id = id
this[kRouteContext] = context
this.params = params
this.raw = req
this.query = query
this.log = log
this.body = undefined
}
Request.props = []
function getTrustProxyFn (tp) {
if (typeof tp === 'function') {
return tp
}
if (tp === true) {
// Support trusting everything
return null
}
if (typeof tp === 'number') {
// Support trusting hop count
return function (a, i) { return i < tp }
}
if (typeof tp === 'string') {
// Support comma-separated tps
const values = tp.split(',').map(it => it.trim())
return proxyAddr.compile(values)
}
return proxyAddr.compile(tp)
}
function buildRequest (R, trustProxy) {
if (trustProxy) {
return buildRequestWithTrustProxy(R, trustProxy)
}
return buildRegularRequest(R)
}
function buildRegularRequest (R) {
const props = R.props.slice()
function _Request (id, params, req, query, log, context) {
this.id = id
this[kRouteContext] = context
this.params = params
this.raw = req
this.query = query
this.log = log
this.body = undefined
let prop
for (let i = 0; i < props.length; i++) {
prop = props[i]
this[prop.key] = prop.value
}
}
Object.setPrototypeOf(_Request.prototype, R.prototype)
Object.setPrototypeOf(_Request, R)
_Request.props = props
_Request.parent = R
return _Request
}
function getLastEntryInMultiHeaderValue (headerValue) {
// we use the last one if the header is set more than once
const lastIndex = headerValue.lastIndexOf(',')
return lastIndex === -1 ? headerValue.trim() : headerValue.slice(lastIndex + 1).trim()
}
function buildRequestWithTrustProxy (R, trustProxy) {
const _Request = buildRegularRequest(R)
const proxyFn = getTrustProxyFn(trustProxy)
// This is a more optimized version of decoration
_Request[kHasBeenDecorated] = true
Object.defineProperties(_Request.prototype, {
ip: {
get () {
const addrs = proxyAddr.all(this.raw, proxyFn)
return addrs[addrs.length - 1]
}
},
ips: {
get () {
return proxyAddr.all(this.raw, proxyFn)
}
},
host: {
get () {
if (this.ip !== undefined && this.headers['x-forwarded-host']) {
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-host'])
}
/**
* The last fallback supports the following cases:
* 1. http.requireHostHeader === false
* 2. HTTP/1.0 without a Host Header
* 3. Headers schema that may remove the Host Header
*/
return this.headers.host ?? this.headers[':authority'] ?? ''
}
},
protocol: {
get () {
if (this.headers['x-forwarded-proto']) {
return getLastEntryInMultiHeaderValue(this.headers['x-forwarded-proto'])
}
if (this.socket) {
return this.socket.encrypted ? 'https' : 'http'
}
}
}
})
return _Request
}
function assertsRequestDecoration (request, name) {
if (!decorators.hasKey(request, name) && !decorators.exist(request, name)) {
throw new FST_ERR_DEC_UNDECLARED(name, 'request')
}
}
Object.defineProperties(Request.prototype, {
server: {
get () {
return this[kRouteContext].server
}
},
url: {
get () {
return this.raw.url
}
},
originalUrl: {
get () {
/* istanbul ignore else */
if (!this[kRequestOriginalUrl]) {
this[kRequestOriginalUrl] = this.raw.originalUrl || this.raw.url
}
return this[kRequestOriginalUrl]
}
},
method: {
get () {
return this.raw.method
}
},
routeOptions: {
get () {
const context = this[kRouteContext]
const routeLimit = context._parserOptions.limit
const serverLimit = context.server.initialConfig.bodyLimit
const version = context.server.hasConstraintStrategy('version') ? this.raw.headers['accept-version'] : undefined
const options = {
method: context.config?.method,
url: context.config?.url,
bodyLimit: (routeLimit || serverLimit),
attachValidation: context.attachValidation,
logLevel: context.logLevel,
exposeHeadRoute: context.exposeHeadRoute,
prefixTrailingSlash: context.prefixTrailingSlash,
handler: context.handler,
config: context.config,
schema: context.schema,
version
}
return options
}
},
is404: {
get () {
return this[kRouteContext].config?.url === undefined
}
},
socket: {
get () {
return this.raw.socket
}
},
ip: {
get () {
if (this.socket) {
return this.socket.remoteAddress
}
}
},
host: {
get () {
/**
* The last fallback supports the following cases:
* 1. http.requireHostHeader === false
* 2. HTTP/1.0 without a Host Header
* 3. Headers schema that may remove the Host Header
*/
return this.raw.headers.host ?? this.raw.headers[':authority'] ?? ''
}
},
hostname: {
get () {
return this.host.split(':', 1)[0]
}
},
port: {
get () {
// first try taking port from host
const portFromHost = parseInt(this.host.split(':').slice(-1)[0])
if (!isNaN(portFromHost)) {
return portFromHost
}
// now fall back to port from host/:authority header
const host = (this.headers.host ?? this.headers[':authority'] ?? '')
const portFromHeader = parseInt(host.split(':').slice(-1)[0])
if (!isNaN(portFromHeader)) {
return portFromHeader
}
// fall back to null
return null
}
},
protocol: {
get () {
if (this.socket) {
return this.socket.encrypted ? 'https' : 'http'
}
}
},
headers: {
get () {
if (this.additionalHeaders) {
return Object.assign({}, this.raw.headers, this.additionalHeaders)
}
return this.raw.headers
},
set (headers) {
this.additionalHeaders = headers
}
},
getValidationFunction: {
value: function (httpPartOrSchema) {
if (typeof httpPartOrSchema === 'string') {
const symbol = HTTP_PART_SYMBOL_MAP[httpPartOrSchema]
return this[kRouteContext][symbol]
} else if (typeof httpPartOrSchema === 'object') {
return this[kRouteContext][kRequestCacheValidateFns]?.get(httpPartOrSchema)
}
}
},
compileValidationSchema: {
value: function (schema, httpPart = null) {
const { method, url } = this
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
return this[kRouteContext][kRequestCacheValidateFns].get(schema)
}
const validatorCompiler = this[kRouteContext].validatorCompiler ||
this.server[kSchemaController].validatorCompiler ||
(
// We compile the schemas if no custom validatorCompiler is provided
// nor set
this.server[kSchemaController].setupValidator(this.server[kOptions]) ||
this.server[kSchemaController].validatorCompiler
)
const validateFn = validatorCompiler({
schema,
method,
url,
httpPart
})
// We create a WeakMap to compile the schema only once
// Its done lazily to avoid add overhead by creating the WeakMap
// if it is not used
// TODO: Explore a central cache for all the schemas shared across
// encapsulated contexts
if (this[kRouteContext][kRequestCacheValidateFns] == null) {
this[kRouteContext][kRequestCacheValidateFns] = new WeakMap()
}
this[kRouteContext][kRequestCacheValidateFns].set(schema, validateFn)
return validateFn
}
},
validateInput: {
value: function (input, schema, httpPart) {
httpPart = typeof schema === 'string' ? schema : httpPart
const symbol = (httpPart != null && typeof httpPart === 'string') && HTTP_PART_SYMBOL_MAP[httpPart]
let validate
if (symbol) {
// Validate using the HTTP Request Part schema
validate = this[kRouteContext][symbol]
}
// We cannot compile if the schema is missed
if (validate == null && (schema == null ||
typeof schema !== 'object' ||
Array.isArray(schema))
) {
throw new FST_ERR_REQ_INVALID_VALIDATION_INVOCATION(httpPart)
}
if (validate == null) {
if (this[kRouteContext][kRequestCacheValidateFns]?.has(schema)) {
validate = this[kRouteContext][kRequestCacheValidateFns].get(schema)
} else {
// We proceed to compile if there's no validate function yet
validate = this.compileValidationSchema(schema, httpPart)
}
}
return validate(input)
}
},
getDecorator: {
value: function (name) {
assertsRequestDecoration(this, name)
const decorator = this[name]
if (typeof decorator === 'function') {
return decorator.bind(this)
}
return decorator
}
},
setDecorator: {
value: function (name, value) {
assertsRequestDecoration(this, name)
this[name] = value
}
}
})
module.exports = Request
module.exports.buildRequest = buildRequest

620
node_modules/fastify/lib/route.js generated vendored Normal file
View File

@@ -0,0 +1,620 @@
'use strict'
const FindMyWay = require('find-my-way')
const Context = require('./context')
const handleRequest = require('./handleRequest')
const { onRequestAbortHookRunner, lifecycleHooks, preParsingHookRunner, onTimeoutHookRunner, onRequestHookRunner } = require('./hooks')
const { normalizeSchema } = require('./schemas')
const { parseHeadOnSendHandlers } = require('./headRoute')
const {
compileSchemasForValidation,
compileSchemasForSerialization
} = require('./validation')
const {
FST_ERR_SCH_VALIDATION_BUILD,
FST_ERR_SCH_SERIALIZATION_BUILD,
FST_ERR_DUPLICATED_ROUTE,
FST_ERR_INVALID_URL,
FST_ERR_HOOK_INVALID_HANDLER,
FST_ERR_ROUTE_OPTIONS_NOT_OBJ,
FST_ERR_ROUTE_DUPLICATED_HANDLER,
FST_ERR_ROUTE_HANDLER_NOT_FN,
FST_ERR_ROUTE_MISSING_HANDLER,
FST_ERR_ROUTE_METHOD_NOT_SUPPORTED,
FST_ERR_ROUTE_METHOD_INVALID,
FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED,
FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT,
FST_ERR_HOOK_INVALID_ASYNC_HANDLER
} = require('./errors')
const { FSTDEP022 } = require('./warnings')
const {
kRoutePrefix,
kSupportedHTTPMethods,
kLogLevel,
kLogSerializers,
kHooks,
kSchemaController,
kOptions,
kReplySerializerDefault,
kReplyIsError,
kRequestPayloadStream,
kDisableRequestLogging,
kSchemaErrorFormatter,
kErrorHandler,
kHasBeenDecorated,
kRequestAcceptVersion,
kRouteByFastify,
kRouteContext
} = require('./symbols.js')
const { buildErrorHandler } = require('./error-handler')
const { createChildLogger } = require('./logger-factory.js')
const { getGenReqId } = require('./reqIdGenFactory.js')
const routerKeys = [
'allowUnsafeRegex',
'buildPrettyMeta',
'caseSensitive',
'constraints',
'defaultRoute',
'ignoreDuplicateSlashes',
'ignoreTrailingSlash',
'maxParamLength',
'onBadUrl',
'querystringParser',
'useSemicolonDelimiter'
]
function buildRouting (options) {
const router = FindMyWay(options.config)
let avvio
let fourOhFour
let logger
let hasLogger
let setupResponseListeners
let throwIfAlreadyStarted
let disableRequestLogging
let ignoreTrailingSlash
let ignoreDuplicateSlashes
let return503OnClosing
let globalExposeHeadRoutes
let keepAliveConnections
let closing = false
return {
/**
* @param {import('../fastify').FastifyServerOptions} options
* @param {*} fastifyArgs
*/
setup (options, fastifyArgs) {
avvio = fastifyArgs.avvio
fourOhFour = fastifyArgs.fourOhFour
logger = fastifyArgs.logger
hasLogger = fastifyArgs.hasLogger
setupResponseListeners = fastifyArgs.setupResponseListeners
throwIfAlreadyStarted = fastifyArgs.throwIfAlreadyStarted
globalExposeHeadRoutes = options.exposeHeadRoutes
disableRequestLogging = options.disableRequestLogging
ignoreTrailingSlash = options.routerOptions.ignoreTrailingSlash
ignoreDuplicateSlashes = options.routerOptions.ignoreDuplicateSlashes
return503OnClosing = Object.hasOwn(options, 'return503OnClosing') ? options.return503OnClosing : true
keepAliveConnections = fastifyArgs.keepAliveConnections
},
routing: router.lookup.bind(router), // router func to find the right handler to call
route, // configure a route in the fastify instance
hasRoute,
prepareRoute,
routeHandler,
closeRoutes: () => { closing = true },
printRoutes: router.prettyPrint.bind(router),
addConstraintStrategy,
hasConstraintStrategy,
isAsyncConstraint,
findRoute
}
function addConstraintStrategy (strategy) {
throwIfAlreadyStarted('Cannot add constraint strategy!')
return router.addConstraintStrategy(strategy)
}
function hasConstraintStrategy (strategyName) {
return router.hasConstraintStrategy(strategyName)
}
function isAsyncConstraint () {
return router.constrainer.asyncStrategiesInUse.size > 0
}
// Convert shorthand to extended route declaration
function prepareRoute ({ method, url, options, handler, isFastify }) {
if (typeof url !== 'string') {
throw new FST_ERR_INVALID_URL(typeof url)
}
if (!handler && typeof options === 'function') {
handler = options // for support over direct function calls such as fastify.get() options are reused as the handler
options = {}
} else if (handler && typeof handler === 'function') {
if (Object.prototype.toString.call(options) !== '[object Object]') {
throw new FST_ERR_ROUTE_OPTIONS_NOT_OBJ(method, url)
} else if (options.handler) {
if (typeof options.handler === 'function') {
throw new FST_ERR_ROUTE_DUPLICATED_HANDLER(method, url)
} else {
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(method, url)
}
}
}
options = Object.assign({}, options, {
method,
url,
path: url,
handler: handler || (options && options.handler)
})
return route.call(this, { options, isFastify })
}
function hasRoute ({ options }) {
const normalizedMethod = options.method?.toUpperCase() ?? ''
return router.hasRoute(
normalizedMethod,
options.url || '',
options.constraints
)
}
function findRoute (options) {
const route = router.find(
options.method,
options.url || '',
options.constraints
)
if (route) {
// we must reduce the expose surface, otherwise
// we provide the ability for the user to modify
// all the route and server information in runtime
return {
handler: route.handler,
params: route.params,
searchParams: route.searchParams
}
} else {
return null
}
}
/**
* Route management
* @param {{ options: import('../fastify').RouteOptions, isFastify: boolean }}
*/
function route ({ options, isFastify }) {
throwIfAlreadyStarted('Cannot add route!')
// Since we are mutating/assigning only top level props, it is fine to have a shallow copy using the spread operator
const opts = { ...options }
const path = opts.url || opts.path || ''
if (!opts.handler) {
throw new FST_ERR_ROUTE_MISSING_HANDLER(opts.method, path)
}
if (opts.errorHandler !== undefined && typeof opts.errorHandler !== 'function') {
throw new FST_ERR_ROUTE_HANDLER_NOT_FN(opts.method, path)
}
validateBodyLimitOption(opts.bodyLimit)
const shouldExposeHead = opts.exposeHeadRoute ?? globalExposeHeadRoutes
let isGetRoute = false
let isHeadRoute = false
if (Array.isArray(opts.method)) {
for (let i = 0; i < opts.method.length; ++i) {
opts.method[i] = normalizeAndValidateMethod.call(this, opts.method[i])
validateSchemaBodyOption.call(this, opts.method[i], path, opts.schema)
isGetRoute = opts.method.includes('GET')
isHeadRoute = opts.method.includes('HEAD')
}
} else {
opts.method = normalizeAndValidateMethod.call(this, opts.method)
validateSchemaBodyOption.call(this, opts.method, path, opts.schema)
isGetRoute = opts.method === 'GET'
isHeadRoute = opts.method === 'HEAD'
}
// we need to clone a set of initial options for HEAD route
const headOpts = shouldExposeHead && isGetRoute ? { ...options } : null
const prefix = this[kRoutePrefix]
if (path === '/' && prefix.length > 0 && opts.method !== 'HEAD') {
switch (opts.prefixTrailingSlash) {
case 'slash':
addNewRoute.call(this, { path, isFastify })
break
case 'no-slash':
addNewRoute.call(this, { path: '', isFastify })
break
case 'both':
default:
addNewRoute.call(this, { path: '', isFastify })
// If ignoreTrailingSlash is set to true we need to add only the '' route to prevent adding an incomplete one.
if (ignoreTrailingSlash !== true && (ignoreDuplicateSlashes !== true || !prefix.endsWith('/'))) {
addNewRoute.call(this, { path, prefixing: true, isFastify })
}
}
} else if (path[0] === '/' && prefix.endsWith('/')) {
// Ensure that '/prefix/' + '/route' gets registered as '/prefix/route'
addNewRoute.call(this, { path: path.slice(1), isFastify })
} else {
addNewRoute.call(this, { path, isFastify })
}
// chainable api
return this
function addNewRoute ({ path, prefixing = false, isFastify = false }) {
const url = prefix + path
opts.url = url
opts.path = url
opts.routePath = path
opts.prefix = prefix
opts.logLevel = opts.logLevel || this[kLogLevel]
if (this[kLogSerializers] || opts.logSerializers) {
opts.logSerializers = Object.assign(Object.create(this[kLogSerializers]), opts.logSerializers)
}
if (opts.attachValidation == null) {
opts.attachValidation = false
}
if (prefixing === false) {
// run 'onRoute' hooks
for (const hook of this[kHooks].onRoute) {
hook.call(this, opts)
}
}
for (const hook of lifecycleHooks) {
if (opts && hook in opts) {
if (Array.isArray(opts[hook])) {
for (const func of opts[hook]) {
if (typeof func !== 'function') {
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(func))
}
if (hook === 'onSend' || hook === 'preSerialization' || hook === 'onError' || hook === 'preParsing') {
if (func.constructor.name === 'AsyncFunction' && func.length === 4) {
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else if (hook === 'onRequestAbort') {
if (func.constructor.name === 'AsyncFunction' && func.length !== 1) {
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
} else {
if (func.constructor.name === 'AsyncFunction' && func.length === 3) {
throw new FST_ERR_HOOK_INVALID_ASYNC_HANDLER()
}
}
}
} else if (opts[hook] !== undefined && typeof opts[hook] !== 'function') {
throw new FST_ERR_HOOK_INVALID_HANDLER(hook, Object.prototype.toString.call(opts[hook]))
}
}
}
const constraints = opts.constraints || {}
const config = {
...opts.config,
url,
method: opts.method
}
const context = new Context({
schema: opts.schema,
handler: opts.handler.bind(this),
config,
errorHandler: opts.errorHandler,
childLoggerFactory: opts.childLoggerFactory,
bodyLimit: opts.bodyLimit,
logLevel: opts.logLevel,
logSerializers: opts.logSerializers,
attachValidation: opts.attachValidation,
schemaErrorFormatter: opts.schemaErrorFormatter,
replySerializer: this[kReplySerializerDefault],
validatorCompiler: opts.validatorCompiler,
serializerCompiler: opts.serializerCompiler,
exposeHeadRoute: shouldExposeHead,
prefixTrailingSlash: (opts.prefixTrailingSlash || 'both'),
server: this,
isFastify
})
const headHandler = router.findRoute('HEAD', opts.url, constraints)
const hasHEADHandler = headHandler !== null
try {
router.on(opts.method, opts.url, { constraints }, routeHandler, context)
} catch (error) {
// any route insertion error created by fastify can be safely ignore
// because it only duplicate route for head
if (!context[kRouteByFastify]) {
const isDuplicatedRoute = error.message.includes(`Method '${opts.method}' already declared for route`)
if (isDuplicatedRoute) {
throw new FST_ERR_DUPLICATED_ROUTE(opts.method, opts.url)
}
throw error
}
}
this.after((notHandledErr, done) => {
// Send context async
context.errorHandler = opts.errorHandler ? buildErrorHandler(this[kErrorHandler], opts.errorHandler) : this[kErrorHandler]
context._parserOptions.limit = opts.bodyLimit || null
context.logLevel = opts.logLevel
context.logSerializers = opts.logSerializers
context.attachValidation = opts.attachValidation
context[kReplySerializerDefault] = this[kReplySerializerDefault]
context.schemaErrorFormatter = opts.schemaErrorFormatter || this[kSchemaErrorFormatter] || context.schemaErrorFormatter
// Run hooks and more
avvio.once('preReady', () => {
for (const hook of lifecycleHooks) {
const toSet = this[kHooks][hook]
.concat(opts[hook] || [])
.map(h => h.bind(this))
context[hook] = toSet.length ? toSet : null
}
// Optimization: avoid encapsulation if no decoration has been done.
while (!context.Request[kHasBeenDecorated] && context.Request.parent) {
context.Request = context.Request.parent
}
while (!context.Reply[kHasBeenDecorated] && context.Reply.parent) {
context.Reply = context.Reply.parent
}
// Must store the 404 Context in 'preReady' because it is only guaranteed to
// be available after all of the plugins and routes have been loaded.
fourOhFour.setContext(this, context)
if (opts.schema) {
context.schema = normalizeSchema(context.schema, this.initialConfig)
const schemaController = this[kSchemaController]
if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
schemaController.setupValidator(this[kOptions])
}
try {
const isCustom = typeof opts?.validatorCompiler === 'function' || schemaController.isCustomValidatorCompiler
compileSchemasForValidation(context, opts.validatorCompiler || schemaController.validatorCompiler, isCustom)
} catch (error) {
throw new FST_ERR_SCH_VALIDATION_BUILD(opts.method, url, error.message)
}
if (opts.schema.response && !opts.serializerCompiler) {
schemaController.setupSerializer(this[kOptions])
}
try {
compileSchemasForSerialization(context, opts.serializerCompiler || schemaController.serializerCompiler)
} catch (error) {
throw new FST_ERR_SCH_SERIALIZATION_BUILD(opts.method, url, error.message)
}
}
})
done(notHandledErr)
})
// register head route in sync
// we must place it after the `this.after`
if (shouldExposeHead && isGetRoute && !isHeadRoute && !hasHEADHandler) {
const onSendHandlers = parseHeadOnSendHandlers(headOpts.onSend)
prepareRoute.call(this, { method: 'HEAD', url: path, options: { ...headOpts, onSend: onSendHandlers }, isFastify: true })
}
}
}
// HTTP request entry point, the routing has already been executed
function routeHandler (req, res, params, context, query) {
const id = getGenReqId(context.server, req)
const loggerOpts = {
level: context.logLevel
}
if (context.logSerializers) {
loggerOpts.serializers = context.logSerializers
}
const childLogger = createChildLogger(context, logger, req, id, loggerOpts)
childLogger[kDisableRequestLogging] = disableRequestLogging
if (closing === true) {
/* istanbul ignore next mac, windows */
if (req.httpVersionMajor !== 2) {
res.setHeader('Connection', 'close')
}
// TODO remove return503OnClosing after Node v18 goes EOL
/* istanbul ignore else */
if (return503OnClosing) {
// On Node v19 we cannot test this behavior as it won't be necessary
// anymore. It will close all the idle connections before they reach this
// stage.
const headers = {
'Content-Type': 'application/json',
'Content-Length': '80'
}
res.writeHead(503, headers)
res.end('{"error":"Service Unavailable","message":"Service Unavailable","statusCode":503}')
childLogger.info({ res: { statusCode: 503 } }, 'request aborted - refusing to accept new requests as server is closing')
return
}
}
// When server.forceCloseConnections is true, we will collect any requests
// that have indicated they want persistence so that they can be reaped
// on server close. Otherwise, the container is a noop container.
const connHeader = String.prototype.toLowerCase.call(req.headers.connection || '')
if (connHeader === 'keep-alive') {
if (keepAliveConnections.has(req.socket) === false) {
keepAliveConnections.add(req.socket)
req.socket.on('close', removeTrackedSocket.bind({ keepAliveConnections, socket: req.socket }))
}
}
// we revert the changes in defaultRoute
if (req.headers[kRequestAcceptVersion] !== undefined) {
req.headers['accept-version'] = req.headers[kRequestAcceptVersion]
req.headers[kRequestAcceptVersion] = undefined
}
const request = new context.Request(id, params, req, query, childLogger, context)
const reply = new context.Reply(res, request, childLogger)
if (disableRequestLogging === false) {
childLogger.info({ req: request }, 'incoming request')
}
if (hasLogger === true || context.onResponse !== null) {
setupResponseListeners(reply)
}
if (context.onRequest !== null) {
onRequestHookRunner(
context.onRequest,
request,
reply,
runPreParsing
)
} else {
runPreParsing(null, request, reply)
}
if (context.onRequestAbort !== null) {
req.on('close', () => {
/* istanbul ignore else */
if (req.aborted) {
onRequestAbortHookRunner(
context.onRequestAbort,
request,
handleOnRequestAbortHooksErrors.bind(null, reply)
)
}
})
}
if (context.onTimeout !== null) {
if (!request.raw.socket._meta) {
request.raw.socket.on('timeout', handleTimeout)
}
request.raw.socket._meta = { context, request, reply }
}
}
}
function handleOnRequestAbortHooksErrors (reply, err) {
if (err) {
reply.log.error({ err }, 'onRequestAborted hook failed')
}
}
function handleTimeout () {
const { context, request, reply } = this._meta
onTimeoutHookRunner(
context.onTimeout,
request,
reply,
noop
)
}
function normalizeAndValidateMethod (method) {
if (typeof method !== 'string') {
throw new FST_ERR_ROUTE_METHOD_INVALID()
}
method = method.toUpperCase()
if (!this[kSupportedHTTPMethods].bodyless.has(method) &&
!this[kSupportedHTTPMethods].bodywith.has(method)) {
throw new FST_ERR_ROUTE_METHOD_NOT_SUPPORTED(method)
}
return method
}
function validateSchemaBodyOption (method, path, schema) {
if (this[kSupportedHTTPMethods].bodyless.has(method) && schema?.body) {
throw new FST_ERR_ROUTE_BODY_VALIDATION_SCHEMA_NOT_SUPPORTED(method, path)
}
}
function validateBodyLimitOption (bodyLimit) {
if (bodyLimit === undefined) return
if (!Number.isInteger(bodyLimit) || bodyLimit <= 0) {
throw new FST_ERR_ROUTE_BODY_LIMIT_OPTION_NOT_INT(bodyLimit)
}
}
function runPreParsing (err, request, reply) {
if (reply.sent === true) return
if (err != null) {
reply[kReplyIsError] = true
reply.send(err)
return
}
request[kRequestPayloadStream] = request.raw
if (request[kRouteContext].preParsing !== null) {
preParsingHookRunner(request[kRouteContext].preParsing, request, reply, handleRequest.bind(request.server))
} else {
handleRequest.call(request.server, null, request, reply)
}
}
function buildRouterOptions (options, defaultOptions) {
const routerOptions = options.routerOptions || Object.create(null)
const usedDeprecatedOptions = routerKeys.filter(key => Object.hasOwn(options, key))
if (usedDeprecatedOptions.length > 0) {
FSTDEP022(usedDeprecatedOptions.join(', '))
}
for (const key of routerKeys) {
if (!Object.hasOwn(routerOptions, key)) {
routerOptions[key] = options[key] ?? defaultOptions[key]
}
}
return routerOptions
}
/**
* Used within the route handler as a `net.Socket.close` event handler.
* The purpose is to remove a socket from the tracked sockets collection when
* the socket has naturally timed out.
*/
function removeTrackedSocket () {
this.keepAliveConnections.delete(this.socket)
}
function noop () { }
module.exports = { buildRouting, validateBodyLimitOption, buildRouterOptions }

Some files were not shown because too many files have changed in this diff Show More