Aktueller Stand

This commit is contained in:
2026-01-22 19:05:45 +01:00
parent 85dee61a4d
commit e280e4eadb
1967 changed files with 397327 additions and 74093 deletions

View File

@@ -1,18 +1,18 @@
<h1 align="center">Fastify</h1>
## Benchmarking
Benchmarking is important if you want to measure how a change can affect the
performance of your application. We provide a simple way to benchmark your
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): A HTTP/1.1 benchmarking
- [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/kimmobrunfeldt/concurrently): Run commands
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

View File

@@ -2,17 +2,17 @@
## Database
Fastify's ecosystem provides a handful of
plugins for connecting to various database engines.
This guide covers engines that have Fastify
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 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
> 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)
@@ -104,8 +104,8 @@ fastify.listen({ port: 3000 }, err => {
})
```
By default `@fastify/redis` doesn't close
the client connection when Fastify server shuts down.
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
@@ -126,7 +126,7 @@ 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'
})
@@ -178,8 +178,8 @@ fastify.listen({ port: 3000 }, err => {
```
### Writing plugin for a database library
We could write a plugin for a database
library too (e.g. Knex, Prisma, or TypeORM).
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
@@ -245,7 +245,7 @@ for Postgres, MySQL, SQL Server and SQLite. For MongoDB migrations, please check
#### [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 an migrations folder need to follow the
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).
@@ -281,7 +281,7 @@ async function migrate() {
const client = new pg.Client({
host: 'localhost',
port: 5432,
database: 'example',
database: 'example',
user: 'example',
password: 'example',
});
@@ -313,7 +313,7 @@ async function migrate() {
console.error(err)
process.exitCode = 1
}
await client.end()
}

View File

@@ -77,8 +77,8 @@ server.get('/ping', function (request, reply) {
})
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
// 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
@@ -103,7 +103,7 @@ server.get('/v1*', async function (request, reply) {
}
})
server.decorate('magicKey', null)
server.decorate('magicKey')
server.listen({ port: '1234' }, () => {
provider.thirdPartyMagicKeyGenerator(USUAL_WAIT_TIME_MS)
@@ -303,7 +303,7 @@ async function setup(fastify) {
fastify.server.on('listening', doMagic)
// Set up the placeholder for the magicKey
fastify.decorate('magicKey', null)
fastify.decorate('magicKey')
// Our magic -- important to make sure errors are handled. Beware of async
// functions outside `try/catch` blocks
@@ -406,7 +406,7 @@ 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', null)
fastify.decorate('magicKey')
```
The `magicKey` decoration is also part of the plugin now. We initialize it with
@@ -448,10 +448,10 @@ 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) we're still
not ready to take incoming requests and they should redirect traffic to other
instances, if available, besides in how long we estimate that will be solved.
All of that in a few simple lines!
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
@@ -524,14 +524,17 @@ Retry-After: 5000
}
```
Then we attempt a new request (`req-2`), which was a `GET /ping`. As expected,
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 means of informing an interested party
whether or not we were ready to serve requests (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
[here](https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-setting-up-health-checks-with-readiness-and-liveness-probes))
with the `ready` field. Below is the response for that request:
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
@@ -547,7 +550,7 @@ Keep-Alive: timeout=5
}
```
After that there were more interesting log messages:
After that, there were more interesting log messages:
<!-- markdownlint-disable -->
```sh

View File

@@ -4,30 +4,30 @@
## 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
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
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
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
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
@@ -78,10 +78,10 @@ const start = async () => {
start()
```
Our code is setting up a Fastify server which includes the following
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
- 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.
@@ -108,7 +108,7 @@ app.get('/', async (request, reply) => {
})
```
At any point in your business logic, you can check if the request has been
At any point in your business logic, you can check if the request has been
aborted and perform alternative actions.
```js
@@ -122,14 +122,14 @@ app.get('/', async (request, reply) => {
})
```
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
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
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
@@ -151,7 +151,7 @@ setTimeout(() => {
}, 1000);
```
With either approach, you should see the Fastify log appear at the moment the
With either approach, you should see the Fastify log appear at the moment the
request is aborted.
## Conclusion
@@ -160,13 +160,13 @@ 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
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
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.

View File

@@ -12,8 +12,6 @@ section.
[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/any-schema`](https://github.com/fastify/any-schema-you-like) Save
multiple schemas and decide which one to use to serialize the payload.
- [`@fastify/auth`](https://github.com/fastify/fastify-auth) Run multiple auth
functions in Fastify.
- [`@fastify/autoload`](https://github.com/fastify/fastify-autoload) Require all
@@ -42,11 +40,6 @@ section.
plugin for adding
[CSRF](https://en.wikipedia.org/wiki/Cross-site_request_forgery) protection to
Fastify.
- [`@fastify/diagnostics-channel`](https://github.com/fastify/fastify-diagnostics-channel)
Plugin to deal with `diagnostics_channel` on Fastify
- [`@fastify/early-hints`](https://github.com/fastify/fastify-early-hints) Plugin
to add HTTP 103 feature based on [RFC
8297](https://datatracker.ietf.org/doc/html/rfc8297).
- [`@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
@@ -91,6 +84,8 @@ section.
[`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
@@ -124,8 +119,8 @@ section.
HTTP errors and assertions, but also more request and reply methods.
- [`@fastify/session`](https://github.com/fastify/session) a session plugin for
Fastify.
- [`@fastify/soap-client`](https://github.com/fastify/fastify-soap-client) a SOAP
client plugin for Fastify.
- [`@fastify/sse`](https://github.com/fastify/sse) Plugin for Server-Sent Events
(SSE) support in 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
@@ -159,8 +154,22 @@ section.
#### [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.
- [`@attaryz/fastify-devtools`](https://github.com/attaryz/fastify-devtools)
Development tools plugin for Fastify with live request dashboard, replay
capabilities, and metrics tracking.
- [`@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)
@@ -173,13 +182,12 @@ section.
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)
@@ -191,14 +199,16 @@ section.
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).
- [`@immobiliarelabs/fastify-sentry`](https://github.com/immobiliare/fastify-sentry)
Sentry errors handler that just works! Install, add your DSN and you're good
to go!
A plugin to implement [Lyra](https://github.com/nearform/lyra) search engine
on Fastify
- [`@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)
@@ -213,13 +223,15 @@ section.
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
- [`@scalar/fastify-api-reference`](https://github.com/scalar/scalar/tree/main/packages/fastify-api-reference)
- [`@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/nodejs-client) Fastify plugin to
integrate with [Apitally](https://apitally.io), a simple API monitoring &
API key management solution.
- [`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)
@@ -229,6 +241,9 @@ section.
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.
- [`elements-fastify`](https://github.com/rohitsoni007/elements-fastify) Fastify
Plugin for Stoplight Elements API Documentation using
openapi swagger json yml.
- [`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
@@ -252,7 +267,7 @@ section.
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
- [`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
@@ -280,7 +295,7 @@ section.
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)
- [`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.
@@ -292,7 +307,7 @@ section.
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)
- [`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
@@ -347,7 +362,7 @@ section.
- [`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
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.
@@ -356,7 +371,7 @@ section.
- [`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/)).
[unleash](https://github.com/Unleash/unleash)).
- [`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)
@@ -447,12 +462,16 @@ section.
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/)
@@ -493,6 +512,8 @@ middlewares into Fastify plugins
[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)
@@ -501,10 +522,7 @@ middlewares into Fastify plugins
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
@@ -539,12 +557,17 @@ middlewares into Fastify plugins
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
- [`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
@@ -600,6 +623,9 @@ middlewares into Fastify plugins
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)
@@ -615,6 +641,8 @@ middlewares into Fastify plugins
- [`fastify-server-session`](https://github.com/jsumners/fastify-server-session)
A session plugin with support for arbitrary backing caches via
`fastify-caching`.
- [`fastify-ses-mailer`](https://github.com/KaranHotwani/fastify-ses-mailer) A
Fastify plugin for sending emails via AWS SES using AWS SDK v3.
- [`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
@@ -656,7 +684,7 @@ middlewares into Fastify plugins
- [`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).
for [@effect/schema](https://github.com/Effect-TS/effect).
- [`fastify-type-provider-zod`](https://github.com/turkerdev/fastify-type-provider-zod)
Fastify
[type provider](https://fastify.dev/docs/latest/Reference/Type-Providers/)
@@ -718,6 +746,7 @@ middlewares into Fastify plugins
- [`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)
@@ -726,9 +755,13 @@ middlewares into Fastify plugins
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.

View File

@@ -55,7 +55,7 @@ fastify.post('/the/url', { schema }, handler)
### Reuse
With `fluent-json-schema` you can manipulate your schemas more easily and
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
@@ -122,5 +122,5 @@ 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`.
> Note: You can mix up the `$ref-way` and the `replace-way`
> when using `fastify.addSchema`.

View File

@@ -106,7 +106,7 @@ of your code.
Fastify offers an easy platform that helps to solve all of the problems outlined
above, and more!
> ## Note
> **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
@@ -128,6 +128,9 @@ above, and more!
>
> 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>
@@ -143,7 +146,7 @@ declaration](../Reference/Routes.md) docs).
```js
// ESM
import Fastify from 'fastify'
import firstRoute from './our-first-route'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
*/
@@ -232,8 +235,8 @@ npm i fastify-plugin @fastify/mongodb
```js
// ESM
import Fastify from 'fastify'
import dbConnector from './our-db-connector'
import firstRoute from './our-first-route'
import dbConnector from './our-db-connector.js'
import firstRoute from './our-first-route.js'
/**
* @type {import('fastify').FastifyInstance} Instance of Fastify
@@ -417,7 +420,7 @@ 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 single and independent services. If you want to
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.
```
@@ -450,8 +453,6 @@ Data validation is extremely important and a core concept of the framework.
To validate incoming requests, Fastify uses [JSON
Schema](https://json-schema.org/).
(JTD schemas are loosely supported, but `jsonShorthand` must be disabled first)
Let's look at an example demonstrating validation for routes:
```js
/**
@@ -554,15 +555,16 @@ an amazing [ecosystem](./Ecosystem.md)!
<a id="test-server"></a>
Fastify does not offer a testing framework, but we do recommend a way to write
your tests that use the features and architecture of Fastify.
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 thanks to
[fastify-cli](https://github.com/fastify/fastify-cli).
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`:

View File

@@ -15,11 +15,11 @@ This table of contents is in alphabetical order.
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
+ [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 writing JSON Schema can be
+ [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.

View File

@@ -9,13 +9,13 @@ work after upgrading.
## Codemods
### Fastify v4 Codemods
To help with the upgrade, weve worked with the team at
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
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
Run the following
[migration recipe](https://go.codemod.com/fastify-4-migration-recipe) to
automatically update your code to Fastify v4:
```
@@ -30,7 +30,7 @@ This will run the following codemods:
- [`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,
For a complete list of available Fastify codemods and further details,
see [Codemod Registry](https://go.codemod.com/fastify).
@@ -52,14 +52,14 @@ fastify.register(async fastify => {
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'
console.log(err.message) // 'caught'
throw new Error('wrapped')
})
@@ -67,10 +67,10 @@ 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,
>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**.
>a custom error handler is provided**.
### Removed `app.use()` ([#3506](https://github.com/fastify/fastify/pull/3506))
@@ -109,7 +109,8 @@ 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.
You can revert this behavior by setting `exposeHeadRoutes: false` in the server
options.
### Synchronous route definitions ([#2954](https://github.com/fastify/fastify/pull/2954))
@@ -210,7 +211,7 @@ fastify.get('/posts/:id?', (request, reply) => {
The [variadic signature](https://en.wikipedia.org/wiki/Variadic_function) of the
`fastify.listen()` method is now deprecated.
Prior to this release, the following invocations of this method were valid:
Before this release, the following invocations of this method were valid:
- `fastify.listen(8000)`
- `fastify.listen(8000, 127.0.0.1)`
@@ -242,7 +243,7 @@ As such, schemas like below will need to be changed from:
properties: {
api_key: { type: 'string' },
image: { type: ['object', 'array'] }
}
}
}
```

View File

@@ -0,0 +1,727 @@
# 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.
### `time` and `date-time` formats enforce timezone
The updated AJV compiler updates `ajv-formats` which now
enforce the use of timezone in `time` and `date-time` format.
A workaround is to use `iso-time` and `iso-date-time` formats
which support an optional timezone for backwards compatibility.
See the
[full discussion](https://github.com/fastify/fluent-json-schema/issues/267).
## 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 |

View File

@@ -71,8 +71,8 @@ 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, utilities (we
will see this in a moment) and do nested registers, just remember to call `done`
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) {
@@ -117,7 +117,7 @@ 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 enable
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) => {
@@ -137,7 +137,7 @@ 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 which avoids the negative situations
`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
@@ -202,7 +202,7 @@ 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*.
In the same way you can do this for the `request` object:
You can do the same for the `request` object:
```js
fastify.decorate('getHeader', (req, header) => {
return req.headers[header]
@@ -316,7 +316,7 @@ 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()
@@ -395,7 +395,7 @@ As we mentioned earlier, Fastify starts loading its plugins __after__
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()`.
`.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

View File

@@ -8,7 +8,7 @@
<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
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
@@ -16,7 +16,7 @@ a moment to read up on [Prototype Poisoning](https://medium.com/intrinsic/javasc
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()`
It focuses on specific framework however, any solution that uses `JSON.parse()`
to process external data is potentially at risk.
### BOOM
@@ -42,7 +42,7 @@ defect a validation library can have.
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
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.

View File

@@ -212,17 +212,19 @@ server {
# server group via port 3000.
server {
# This listen directive asks NGINX to accept requests
# coming to any address, port 443, with SSL, and HTTP/2
# if possible.
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
# 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 http2;
# listen [::]:443 ssl http2;
# 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;
@@ -283,7 +285,7 @@ server {
## Kubernetes
<a id="kubernetes"></a>
The `readinessProbe` uses [(by
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,
@@ -305,22 +307,22 @@ readinessProbe:
## 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
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)
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
* 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.
@@ -328,7 +330,7 @@ frequently. Also, the main thread won't have to stop to let the GC run.
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
* 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.
@@ -345,7 +347,7 @@ 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.
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.

View File

@@ -1,20 +1,20 @@
<h1 align="center">Serverless</h1>
Run serverless applications and REST APIs using your existing Fastify
application. By default, Fastify will not work on your serverless platform of
choice, you will need to make some small changes to fix this. This document
contains a small guide for the most popular serverless providers and how to use
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 that functions as a service should always use
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, and Azure
Container Instances, where the server can handle multiple requests at the same
time and make full use of Fastify's features.
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
@@ -25,21 +25,21 @@ 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)
- [Platformatic Cloud](#platformatic-cloud)
- [Vercel](#vercel)
## AWS
To integrate with AWS, you have two choices of library:
- Using [@fastify/aws-lambda](https://github.com/fastify/aws-lambda-fastify)
- 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
- 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.
@@ -129,6 +129,13 @@ 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
@@ -230,14 +237,13 @@ 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"
...
...
"dev": "npx @google-cloud/functions-framework --target=fastifyFunction"
...
}
```
and run it with `npm run dev`.
### Deploy
```bash
gcloud functions deploy fastifyFunction \
@@ -263,7 +269,7 @@ curl -X POST https://$GOOGLE_REGION-$GOOGLE_PROJECT.cloudfunctions.net/me \
## Google Firebase Functions
Follow this guide if you want to use Fastify as the HTTP framework for
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) => {}`.
@@ -280,8 +286,8 @@ const { onRequest } = require("firebase-functions/v2/https")
### Creation of Fastify instance
Create the Fastify instance and encapsulate the returned application instance
in a function which will register routes, await the server's processing of
plugins, hooks and other settings. As follows:
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")({
@@ -297,10 +303,10 @@ const fastifyApp = async (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 in order to calculate
request signatures to validate HTTP webhooks.
Firebase Function's HTTP layer already parses the request and makes a JSON
payload available through the property `payload.body` below. 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:
@@ -318,7 +324,7 @@ async function registerRoutes (fastify) {
})
// define your endpoints here...
fastify.post("/some-route-here", async (request, reply) => {}
fastify.post("/some-route-here", async (request, reply) => {})
fastify.get('/', async (request, reply) => {
reply.send({message: 'Hello World!'})
@@ -326,6 +332,24 @@ async function registerRoutes (fastify) {
}
```
**Failing to add this `ContentTypeParser` may lead to the Fastify process
remaining stuck and not processing any other requests after receiving one with
the Content-Type `application/json`.**
When using Typescript, since the type of `payload` is a native `IncomingMessage`
that gets modified by Firebase, it won't be able to find the property
`payload.body`.
In order to suppress the error, you can use the following signature:
```ts
declare module 'http' {
interface IncomingMessage {
body?: unknown;
}
}
```
### Export the function using Firebase onRequest
Final step is to export the Fastify app instance to Firebase's own
@@ -369,7 +393,6 @@ firebase functions:log
- [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
@@ -384,7 +407,7 @@ familiar with gcloud or just follow their
### Adjust Fastify server
In order for Fastify to properly listen for requests within the container, be
For Fastify to properly listen for requests within the container, be
sure to set the correct port and address:
```js
@@ -455,7 +478,7 @@ CMD [ "npm", "start" ]
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
```dockerignore
Dockerfile
README.md
node_modules
@@ -482,12 +505,11 @@ 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
Create a folder called `functions`, then create `server.js` (and your endpoint
path will be `server.js`) inside the `functions` folder.
### functions/server.js
@@ -556,106 +578,27 @@ Add this command to your `package.json` *scripts*
```json
"scripts": {
...
"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"
...
...
"build:functions": "netlify-lambda build functions --config ./webpack.config.netlify.js"
...
}
```
Then it should work fine
## Platformatic Cloud
[Platformatic](https://platformatic.dev) provides zero-configuration deployment
for Node.js applications.
To use it now, you should wrap your existing Fastify application inside a
[Platformatic Service](https://oss.platformatic.dev/docs/reference/service/introduction),
by running the following:
```bash
npm create platformatic@latest -- service
```
The wizard would ask you to fill in a few answers:
```
? Where would you like to create your project? .
? Do you want to run npm install? yes
? Do you want to use TypeScript? no
? What port do you want to use? 3042
[13:04:14] INFO: Configuration file platformatic.service.json successfully created.
[13:04:14] INFO: Environment file .env successfully created.
[13:04:14] INFO: Plugins folder "plugins" successfully created.
[13:04:14] INFO: Routes folder "routes" successfully created.
? Do you want to create the github action to deploy this application to Platformatic Cloud dynamic workspace? no
? Do you want to create the github action to deploy this application to Platformatic Cloud static workspace? no
```
Then, head to [Platformatic Cloud](https://platformatic.cloud) and sign in
with your GitHub account.
Create your first application and a static workspace: be careful to download the
API key as an env file, e.g. `yourworkspace.txt`.
Then, you can easily deploy your application with the following command:
```bash
platformatic deploy --keys `yourworkspace.txt`
```
Check out the [Full Guide](https://blog.platformatic.dev/how-to-migrate-a-fastify-app-to-platformatic-service)
on how to wrap Fastify application in Platformatic.
Then it should work fine.
## Vercel
[Vercel](https://vercel.com) provides zero-configuration deployment for Node.js
applications. To use it now, it is as simple as configuring your `vercel.json`
file like the following:
[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.
```json
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/api/serverless.js"
}
]
}
```
Get started with the
[Fastify template on Vercel](
https://vercel.com/templates/backend/fastify-on-vercel).
Then, write `api/serverless.js` like so:
```js
"use strict";
// Read the .env file.
import * as dotenv from "dotenv";
dotenv.config();
// Require the framework
import Fastify from "fastify";
// Instantiate Fastify with some config
const app = Fastify({
logger: true,
});
// Register your application as a normal plugin.
app.register(import("../src/app.js"));
export default async (req, res) => {
await app.ready();
app.server.emit('request', req, res);
}
```
In `src/app.js` define the plugin.
```js
async function routes (fastify, options) {
fastify.get('/', async (request, reply) => {
return { hello: 'world' }
})
}
export default routes;
```
[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).

View File

@@ -83,7 +83,7 @@ Result:
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 used a particular quote from their work.
if you use a particular quote from their work.
## Word Choice
@@ -217,7 +217,7 @@ Styles](https://medium.com/better-programming/string-case-styles-camel-pascal-sn
### Hyperlinks
Hyperlinks should have a clear title of what it references. Here is how your
Hyperlinks should have a clear title of what they reference. Here is how your
hyperlink should look:
```MD

View File

@@ -5,15 +5,15 @@
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 [Tap](https://www.npmjs.com/package/tap), which is used in
the examples below).
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 tap pino-pretty -D`
Run `npm i fastify && npm i pino-pretty -D`
### Separating concerns makes testing easy
@@ -113,24 +113,25 @@ Now we can replace our `console.log` calls with actual tests!
In your `package.json` change the "test" script to:
`"test": "tap --reporter=list --watch"`
`"test": "node --test --watch"`
**app.test.js**:
```js
'use strict'
const { test } = require('tap')
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.equal(response.statusCode, 200, 'returns a status code of 200')
t.assert.strictEqual(response.statusCode, 200, 'returns a status code of 200')
})
```
@@ -214,26 +215,26 @@ module.exports = buildFastify
**test.js**
```js
const tap = require('tap')
const { test } = require('node:test')
const buildFastify = require('./app')
tap.test('GET `/` route', t => {
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.teardown(() => fastify.close())
t.after(() => fastify.close())
fastify.inject({
method: 'GET',
url: '/'
}, (err, response) => {
t.error(err)
t.equal(response.statusCode, 200)
t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
t.same(response.json(), { hello: 'world' })
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' })
})
})
```
@@ -248,11 +249,11 @@ Uses **app.js** from the previous example.
**test-listen.js** (testing with [`undici`](https://www.npmjs.com/package/undici))
```js
const tap = require('tap')
const { test } = require('node:test')
const { Client } = require('undici')
const buildFastify = require('./app')
tap.test('should work with undici', async t => {
test('should work with undici', async t => {
t.plan(2)
const fastify = buildFastify()
@@ -263,63 +264,64 @@ tap.test('should work with undici', async t => {
'http://localhost:' + fastify.server.address().port, {
keepAliveTimeout: 10,
keepAliveMaxTimeout: 10
}
}
)
t.teardown(() => {
t.after(() => {
fastify.close()
client.close()
})
const response = await client.request({ method: 'GET', path: '/' })
t.equal(await response.body.text(), '{"hello":"world"}')
t.equal(response.statusCode, 200)
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)
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 tap = require('tap')
const { test } = require('node:test')
const buildFastify = require('./app')
tap.test('should work with fetch', async t => {
test('should work with fetch', async t => {
t.plan(3)
const fastify = buildFastify()
t.teardown(() => fastify.close())
t.after(() => fastify.close())
await fastify.listen()
const response = await fetch(
'http://localhost:' + fastify.server.address().port
)
t.equal(response.status, 200)
t.equal(
t.assert.strictEqual(response.status, 200)
t.assert.strictEqual(
response.headers.get('content-type'),
'application/json; charset=utf-8'
)
t.has(await response.json(), { hello: 'world' })
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 tap = require('tap')
const { test } = require('node:test')
const supertest = require('supertest')
const buildFastify = require('./app')
tap.test('GET `/` route', async (t) => {
test('GET `/` route', async (t) => {
const fastify = buildFastify()
t.teardown(() => fastify.close())
t.after(() => fastify.close())
await fastify.ready()
@@ -327,22 +329,21 @@ tap.test('GET `/` route', async (t) => {
.get('/')
.expect(200)
.expect('Content-Type', 'application/json; charset=utf-8')
t.same(response.body, { hello: 'world' })
t.assert.deepStrictEqual(response.body, { hello: 'world' })
})
```
### How to inspect tap tests
### How to inspect node tests
1. Isolate your test by passing the `{only: true}` option
```javascript
test('should ...', {only: true}, t => ...)
```
2. Run `tap` using `npx`
2. Run `node --test`
```bash
> npx tap -O -T --node-arg=--inspect-brk test/<test-file.test.js>
> node --test --test-only --inspect-brk test/<test-file.test.js>
```
- `-O` specifies to run tests with the `only` option enabled
- `-T` specifies not to timeout (while you're debugging)
- `--node-arg=--inspect-brk` will launch the node debugger
- `--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.
@@ -352,10 +353,10 @@ Now you should be able to step through your test file (and the rest of
## Plugins
Let's `cd` into a fresh directory called 'testing-plugin-example' and type `npm init
-y` in our terminal.
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 && npm i tap -D`
Run `npm i fastify fastify-plugin`
**plugin/myFirstPlugin.js**:
@@ -376,16 +377,16 @@ A basic example of a Plugin. See [Plugin Guide](./Plugins-Guide.md)
```js
const Fastify = require("fastify");
const tap = require("tap");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
tap.test("Test the Plugin Route", async t => {
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
// Add an endpoint of your choice
fastify.get("/", async (request, reply) => {
return ({ message: request.helloRequest })
})
@@ -395,7 +396,7 @@ tap.test("Test the Plugin Route", async t => {
method: "GET",
url: "/"
})
console.log('status code: ', fastifyResponse.statusCode)
console.log('body: ', fastifyResponse.body)
})
@@ -412,18 +413,18 @@ Now we can replace our `console.log` calls with actual tests!
In your `package.json` change the "test" script to:
`"test": "tap --reporter=list --watch"`
`"test": "node --test --watch"`
Create the tap test for the endpoint.
Create the test for the endpoint.
**test/myFirstPlugin.test.js**:
```js
const Fastify = require("fastify");
const tap = require("tap");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
tap.test("Test the Plugin Route", async t => {
test("Test the Plugin Route", async t => {
// Specifies the number of test
t.plan(2)
@@ -439,9 +440,9 @@ tap.test("Test the Plugin Route", async t => {
method: "GET",
url: "/"
})
t.equal(fastifyResponse.statusCode, 200)
t.same(JSON.parse(fastifyResponse.body), { message: "Hello World" })
t.assert.strictEqual(fastifyResponse.statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })
})
```
@@ -453,10 +454,10 @@ Test the ```.decorate()``` and ```.decorateRequest()```.
```js
const Fastify = require("fastify");
const tap = require("tap");
const { test }= require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
tap.test("Test the Plugin Route", async t => {
test("Test the Plugin Route", async t => {
t.plan(5)
const fastify = Fastify()
@@ -464,9 +465,9 @@ tap.test("Test the Plugin Route", async t => {
fastify.get("/", async (request, reply) => {
// Testing the fastify decorators
t.not(request.helloRequest, null)
t.ok(request.helloRequest, "Hello World")
t.ok(fastify.helloInstance, "Hello Fastify Instance")
t.assert.ifError(request.helloRequest)
t.assert.ok(request.helloRequest, "Hello World")
t.assert.ok(fastify.helloInstance, "Hello Fastify Instance")
return ({ message: request.helloRequest })
})
@@ -474,7 +475,7 @@ tap.test("Test the Plugin Route", async t => {
method: "GET",
url: "/"
})
t.equal(fastifyResponse.statusCode, 200)
t.same(JSON.parse(fastifyResponse.body), { message: "Hello World" })
t.assert.strictEqual(fastifyResponse.statusCode, 200)
t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })
})
```
```

View File

@@ -14,7 +14,7 @@ suggestion"](https://github.com/fastify/fastify/issues?q=is%3Aissue+is%3Aopen+la
in our issue tracker!*
## Code
Fastify uses different techniques to optimize its code, many of them are
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.
@@ -53,17 +53,16 @@ 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
It is extremely important that a plugin is thoroughly tested to verify that is
working properly.
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 [`tap`](https://www.node-tap.org/)
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
We highly recommend you read the [Plugin Testing](./Testing.md#plugins) to
learn about how to test your plugins.
## Code Linter

View File

@@ -10,16 +10,17 @@ 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
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)
output: this['input'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
validator: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
serializer: this['schema'] extends /** custom check here**/ ? /** narrowed type here **/ : never;
}
```
@@ -27,6 +28,7 @@ Unless changed to:
```ts
export interface SubstitutableTypeProvider extends FastifyTypeProvider {
// good, anything can be assigned to `unknown`
output: this['input'] extends /** custom check here**/ ? /** narrowed type here **/ : 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

@@ -1,33 +1,34 @@
<h1 align="center">Fastify</h1>
## `Content-Type` Parser
Natively, Fastify only supports `'application/json'` and `'text/plain'` content
types. If the content type is not one of these, an
`FST_ERR_CTP_INVALID_MEDIA_TYPE` error will be thrown.
Other common content types are supported through the use of
[plugins](https://fastify.dev/ecosystem/).
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.
The default charset is `utf-8`. If you need to support different content types,
you can use the `addContentTypeParser` API. *The default JSON and/or plain text
parser can be changed or removed.*
Unsupported content types will throw an `FST_ERR_CTP_INVALID_MEDIA_TYPE` error.
*Note: If you decide to specify your own content type with the `Content-Type`
header, UTF-8 will not be the default. Be sure to include UTF-8 like this
`text/html; charset=utf-8`.*
To support other content types, use the `addContentTypeParser` API or an
existing [plugin](https://fastify.dev/ecosystem/).
As with the other APIs, `addContentTypeParser` is encapsulated in the scope in
which it is declared. This means that if you declare it in the root scope it
will be available everywhere, while if you declare it inside a plugin it will be
available only in that scope and its children.
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 which you can access with `request.body`.
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 only parsed if the content type
is given in the content-type header. If it is not given, the
[catch-all](#catch-all) parser is not executed as with `POST`, `PUT` and
`PATCH`, but the payload is simply not parsed.
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
@@ -46,13 +47,13 @@ fastify.addContentTypeParser(['text/xml', 'application/xml'], function (request,
// Async is also supported in Node versions >= 8.0.0
fastify.addContentTypeParser('application/jsoff', async function (request, payload) {
var res = await jsoffParserAsync(payload)
const res = await jsoffParserAsync(payload)
return res
})
// Handle all content types that matches RegExp
fastify.addContentTypeParser(/^image\/.*/, function (request, payload, done) {
fastify.addContentTypeParser(/^image\/([\w-]+);?/, function (request, payload, done) {
imageParser(payload, function (err, body) {
done(err, body)
})
@@ -63,11 +64,10 @@ fastify.addContentTypeParser('text/json', { parseAs: 'string' }, fastify.getDefa
```
Fastify first tries to match a content-type parser with a `string` value before
trying to find a matching `RegExp`. If you provide overlapping content types,
Fastify tries to find a matching content type by starting with the last one
passed and ending with the first one. So if you want to specify a general
content type more precisely, first specify the general content type and then the
more specific one, like in the example below.
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
@@ -80,14 +80,34 @@ fastify.addContentTypeParser('application/vnd.custom', (request, body, done) =>
fastify.addContentTypeParser('application/vnd.custom+xml', (request, body, done) => {} )
```
Besides the `addContentTypeParser` API there are further APIs that can be used.
These are `hasContentTypeParser`, `removeContentTypeParser` and
`removeAllContentTypeParsers`.
### 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
You can use the `hasContentTypeParser` API to find if a specific content type
parser already exists.
Use the `hasContentTypeParser` API to check if a specific content type parser
exists.
```js
if (!fastify.hasContentTypeParser('application/jsoff')){
@@ -101,8 +121,8 @@ if (!fastify.hasContentTypeParser('application/jsoff')){
#### removeContentTypeParser
With `removeContentTypeParser` a single or an array of content types can be
removed. The method supports `string` and `RegExp` content types.
`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) {
@@ -116,16 +136,11 @@ fastify.removeContentTypeParser(['application/json', 'text/plain'])
```
#### removeAllContentTypeParsers
In the example from just above, it is noticeable that we need to specify each
content type that we want to remove. To solve this problem Fastify provides the
`removeAllContentTypeParsers` API. This can be used to remove all currently
existing content type parsers. In the example below we achieve the same as in
the example above except that we do not need to specify each content type to
delete. Just like `removeContentTypeParser`, this API supports encapsulation.
The API is especially useful if you want to register a [catch-all content type
parser](#catch-all) that should be executed for every content type and the
built-in parsers should be ignored as well.
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()
@@ -137,22 +152,20 @@ fastify.addContentTypeParser('text/xml', function (request, payload, done) {
})
```
**Notice**: The old syntaxes `function(req, done)` and `async function(req)` for
the parser are still supported but they are deprecated.
> Note: `function(req, done)` and `async function(req)` are
> still supported but deprecated.
#### Body Parser
You can parse the body of a request in two ways. The first one is shown above:
you add a custom content type parser and handle the request stream. In the
second one, you should pass a `parseAs` option to the `addContentTypeParser`
API, where you declare how you want to get the body. It could be of type
`'string'` or `'buffer'`. If you use the `parseAs` option, Fastify will
internally handle the stream and perform some checks, such as 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.
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 {
var json = JSON.parse(body)
const json = JSON.parse(body)
done(null, json)
} catch (err) {
err.statusCode = 400
@@ -166,30 +179,27 @@ See
for an example.
##### Custom Parser Options
+ `parseAs` (string): Either `'string'` or `'buffer'` to designate how the
incoming data should be collected. Default: `'buffer'`.
+ `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
There are some cases where you need to catch all requests regardless of their
content type. With Fastify, you can just use the `'*'` content type.
To catch all requests regardless of content type, use the `'*'` content type:
```js
fastify.addContentTypeParser('*', function (request, payload, done) {
var data = ''
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.
Using this, all requests that do not have a corresponding content type parser
will be handled by the specified function.
This is also useful for piping the request stream. You can define a content
parser like:
This is also useful for piping the request stream. Define a content parser like:
```js
fastify.addContentTypeParser('*', function (request, payload, done) {
@@ -197,7 +207,7 @@ fastify.addContentTypeParser('*', function (request, payload, done) {
})
```
and then access the core HTTP request directly for piping it where you want:
And then access the core HTTP request directly for piping:
```js
app.post('/hello', (request, reply) => {
@@ -225,19 +235,18 @@ fastify.route({
})
```
For piping file uploads you may want to check out [this
plugin](https://github.com/fastify/fastify-multipart).
For piping file uploads, check out
[`@fastify/multipart`](https://github.com/fastify/fastify-multipart).
If you want the content type parser to be executed on all content types and not
only on those that don't have a specific one, you should call the
`removeAllContentTypeParsers` method first.
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) {
var data = ''
const data = ''
payload.on('data', chunk => { data += chunk })
payload.on('end', () => {
done(null, data)

View File

@@ -2,16 +2,15 @@
## Decorators
The decorators API allows customization of the core Fastify objects, such as the
server instance itself and any request and reply objects used during the HTTP
request lifecycle. The decorators API can be used to attach any type of property
to the core objects, e.g. functions, plain objects, or native types.
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*. Attempting to define a decoration asynchronously
could result in the Fastify instance booting before the decoration completes its
initialization. To avoid this issue, and register an asynchronous decoration,
the `register` API, in combination with `fastify-plugin`, must be used instead.
To learn more, see the [Plugins](./Plugins.md) documentation.
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
@@ -35,9 +34,9 @@ fastify.get('/', function (req, reply) {
})
```
Since the above example mutates the request object after it has already been
instantiated, the JavaScript engine must deoptimize access to the request
object. By using the decoration API this deoptimization is avoided:
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
@@ -54,17 +53,13 @@ fastify.get('/', (req, reply) => {
})
```
Note that it is important to keep the initial shape of a decorated field as
close as possible to the value intended to be set dynamically in the future.
Initialize a decorator as a `''` if the intended value is a string, and as
`null` if it will be an object or a function.
Remember this example works only with value types as reference types will be
shared amongst all requests. See [decorateRequest](#decorate-request).
See [JavaScript engine fundamentals: Shapes and Inline
Caches](https://mathiasbynens.be/notes/shapes-ics) for more information on this
topic.
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>
@@ -72,8 +67,7 @@ topic.
#### `decorate(name, value, [dependencies])`
<a id="decorate"></a>
This method is used to customize the Fastify [server](./Server.md)
instance.
This method customizes the Fastify [server](./Server.md) instance.
For example, to attach a new method to the server instance:
@@ -83,7 +77,7 @@ fastify.decorate('utility', function () {
})
```
As mentioned above, non-function values can be attached:
Non-function values can also be attached to the server instance:
```js
fastify.decorate('conf', {
@@ -109,7 +103,7 @@ 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') })
@@ -118,9 +112,9 @@ fastify.get('/', async function (request, reply) {
```
The `dependencies` parameter is an optional list of decorators that the
decorator being defined relies upon. This list is simply a list of string names
of other decorators. In the following example, the "utility" decorator depends
upon "greet" and "hi" decorators:
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) {
@@ -155,18 +149,17 @@ fastify.listen({ port: 3000 }, (err, address) => {
})
```
Note: using an arrow function will break the binding of `this` to the
`FastifyInstance`.
Using an arrow function breaks the binding of `this` to
the `FastifyInstance`.
If a dependency is not satisfied, the `decorate` method will throw an exception.
The dependency check is performed before the server instance is booted. Thus, it
cannot occur during runtime.
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>
As the name suggests, this API is used to add new methods/properties to the core
`Reply` object:
This API adds new methods/properties to the core `Reply` object:
```js
fastify.decorateReply('utility', function () {
@@ -174,28 +167,29 @@ fastify.decorateReply('utility', function () {
})
```
Note: using an arrow function will break the binding of `this` to the Fastify
Using an arrow function will break the binding of `this` to the Fastify
`Reply` instance.
Note: using `decorateReply` will emit a warning if used with a reference type:
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 reference of the object is shared with all the requests:
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**. To achieve proper encapsulation across
requests configure a new value for each incoming request in the [`'onRequest'`
hook](./Hooks.md#onrequest). Example:
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.decorateRequest('foo', null)
app.decorateReply('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
reply.foo = { bar: 42 }
})
}
@@ -207,8 +201,8 @@ See [`decorate`](#decorate) for information about the `dependencies` parameter.
#### `decorateRequest(name, value, [dependencies])`
<a id="decorate-request"></a>
As above with [`decorateReply`](#decorate-reply), this API is used add new
methods/properties to the core `Request` object:
As with [`decorateReply`](#decorate-reply), this API adds new methods/properties
to the core `Request` object:
```js
fastify.decorateRequest('utility', function () {
@@ -216,27 +210,29 @@ fastify.decorateRequest('utility', function () {
})
```
Note: using an arrow function will break the binding of `this` to the Fastify
Using an arrow function will break the binding of `this` to the Fastify
`Request` instance.
Note: using `decorateRequest` will emit a warning if used with a reference type:
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 reference of the object is shared with all the requests:
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**.
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:
incoming request in the [`'onRequest'` hook](./Hooks.md#onrequest).
Example:
```js
const fp = require('fastify-plugin')
async function myPlugin (app) {
app.decorateRequest('foo', null)
app.decorateRequest('foo')
app.addHook('onRequest', async (req, reply) => {
req.foo = { bar: 42 }
})
@@ -245,6 +241,28 @@ async function myPlugin (app) {
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)`
@@ -279,9 +297,7 @@ fastify.hasReplyDecorator('utility')
Defining a decorator (using `decorate`, `decorateRequest`, or `decorateReply`)
with the same name more than once in the same **encapsulated** context will
throw an exception.
As an example, the following will throw:
throw an exception. For example, the following will throw:
```js
const server = require('fastify')()
@@ -332,9 +348,9 @@ server.listen({ port: 3000 })
### Getters and Setters
<a id="getters-setters"></a>
Decorators accept special "getter/setter" objects. These objects have functions
named `getter` and `setter` (though the `setter` function is optional). This
allows defining properties via decorators, for example:
Decorators accept special "getter/setter" objects with `getter` and optional
`setter` functions. This allows defining properties via decorators,
for example:
```js
fastify.decorate('foo', {
@@ -349,3 +365,70 @@ Will define the `foo` property on the Fastify instance:
```js
console.log(fastify.foo) // 'a getter'
```
#### `getDecorator(name)`
<a id="get-decorator"></a>
Used to retrieve an existing decorator from the Fastify instance, `Request`,
or `Reply`.
If the decorator is not defined, an `FST_ERR_DEC_UNDECLARED` error is thrown.
```js
// Get a decorator from the Fastify instance
const utility = fastify.getDecorator('utility')
// Get a decorator from the request object
const user = request.getDecorator('user')
// Get a decorator from the reply object
const helper = reply.getDecorator('helper')
```
The `getDecorator` method is useful for dependency validation - it can be used to
check for required decorators at registration time. If any are missing, it fails
at boot, ensuring dependencies are available during the request lifecycle.
```js
fastify.register(async function (fastify) {
// Verify the decorator exists before using it
const usersRepository = fastify.getDecorator('usersRepository')
fastify.get('/users', async function (request, reply) {
return usersRepository.findAll()
})
})
```
> Note: For TypeScript users, `getDecorator` supports generic type parameters.
> See the [TypeScript documentation](/docs/Reference/TypeScript.md) for
> advanced typing examples.
#### `setDecorator(name, value)`
<a id="set-decorator"></a>
Used to safely update the value of a `Request` decorator.
If the decorator does not exist, a `FST_ERR_DEC_UNDECLARED` error is thrown.
```js
fastify.decorateRequest('user', null)
fastify.addHook('preHandler', async (req, reply) => {
// Safely set the decorator value
req.setDecorator('user', 'Bob Dylan')
})
```
The `setDecorator` method provides runtime safety by ensuring the decorator exists
before setting its value, preventing errors from typos in decorator names.
```js
fastify.decorateRequest('account', null)
fastify.addHook('preHandler', async (req, reply) => {
// This will throw FST_ERR_DEC_UNDECLARED due to typo in decorator name
req.setDecorator('acount', { id: 123 })
})
```
> Note: For TypeScript users, see the
> [TypeScript documentation](/docs/Reference/TypeScript.md) for advanced
> typing examples using `setDecorator<T>`.

View File

@@ -3,21 +3,20 @@
## Encapsulation
<a id="encapsulation"></a>
A fundamental feature of Fastify is the "encapsulation context." The
encapsulation context 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:
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 above figure, there are several entities:
In the figure above, there are several entities:
1. The _root context_
2. Three _root plugins_
3. Two _child contexts_ where each _child context_ has
3. Two _child contexts_, each with:
* Two _child plugins_
* One _grandchild context_ where each _grandchild context_ has
* One _grandchild context_, each with:
- Three _child plugins_
Every _child context_ and _grandchild context_ has access to the _root plugins_.
@@ -26,15 +25,18 @@ _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
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. Thus, to put
this example into concrete terms, consider a basic scenario of a REST API
server that has 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 the authentication, the code for this
example is as follows:
that can consist of decorators, hooks, plugins, and routes. As plugins, they
must still signal completion either by returning a Promise (e.g., using `async`
functions) or by calling the `done` function if using the callback style.
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'
@@ -52,9 +54,9 @@ fastify.register(async function authenticatedContext (childServer) {
handler (request, response) {
response.send({
answer: request.answer,
// request.foo will be undefined as it's only defined in publicContext
// request.foo will be undefined as it is only defined in publicContext
foo: request.foo,
// request.bar will be undefined as it's only defined in grandchildContext
// request.bar will be undefined as it is only defined in grandchildContext
bar: request.bar
})
}
@@ -71,7 +73,7 @@ fastify.register(async function publicContext (childServer) {
response.send({
answer: request.answer,
foo: request.foo,
// request.bar will be undefined as it's only defined in grandchildContext
// request.bar will be undefined as it is only defined in grandchildContext
bar: request.bar
})
}
@@ -97,16 +99,16 @@ fastify.register(async function publicContext (childServer) {
fastify.listen({ port: 8000 })
```
The above server example shows all of the encapsulation concepts outlined in the
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_.
`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.
plugin.
3. Both the `publicContext` and `grandchildContext` have access to the `foo`
request decorator.
request decorator.
4. Only the `grandchildContext` has access to the `bar` request decorator.
To see this, start the server and issue requests:
@@ -125,16 +127,13 @@ To see this, start the server and issue requests:
## Sharing Between Contexts
<a id="shared-context"></a>
Notice that each context in the prior example inherits _only_ from the parent
contexts. Parent contexts cannot access any entities within their descendent
contexts. This default is occasionally not desired. In such cases, the
encapsulation context can be broken through the usage of
[fastify-plugin][fastify-plugin] such that anything registered in a descendent
context is available to the containing parent context.
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.
Assuming the `publicContext` needs access to the `bar` decorator defined
within the `grandchildContext` in the previous example, the code can be
rewritten as:
To allow `publicContext` access to the `bar` decorator in `grandchildContext`,
rewrite the code as follows:
```js
'use strict'

View File

@@ -5,7 +5,7 @@
**Table of contents**
- [Errors](#errors)
- [Error Handling In Node.js](#error-handling-in-node.js)
- [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)
@@ -20,7 +20,6 @@
- [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_VERSION_CONSTRAINT_NOT_STR](#fst_err_version_constraint_not_str)
- [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)
@@ -30,12 +29,15 @@
- [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)
@@ -44,8 +46,12 @@
- [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)
@@ -64,13 +70,11 @@
- [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_HTTP2_INVALID_VERSION](#fst_err_http2_invalid_version)
- [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_DEFAULT_ROUTE_INVALID_TYPE](#fst_err_default_route_invalid_type)
- [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)
@@ -90,16 +94,18 @@
- [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 are likely to cause memory leaks, file descriptor
leaks, and other major production issues.
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.
@@ -108,29 +114,28 @@ way to deal with them is to
[crash](https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly).
#### Catching Errors In Promises
If you are using promises, you should attach a `.catch()` handler synchronously.
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 as
much as possible. The developer is responsible for making sure that the errors
are handled properly.
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 are a result of unexpected input data, so we recommend [validating
your input data against a JSON schema](./Validation-and-Serialization.md).
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 it can without hindering
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')
})`
The error in both cases will be caught safely and routed to Fastify's default
error handler for a generic `500 Internal Server Error` response.
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 you should use
To customize this behavior, use
[`setErrorHandler`](./Server.md#seterrorhandler).
### Errors In Fastify Lifecycle Hooks And A Custom Error Handler
@@ -140,52 +145,50 @@ From the [Hooks documentation](./Hooks.md#manage-errors-from-a-hook):
> `done()` and Fastify will automatically close the request and send the
> appropriate error code to the user.
When a custom error handler has been defined through
[`setErrorHandler`](./Server.md#seterrorhandler), the custom error handler will
receive the error passed to the `done()` callback (or through other supported
automatic error handling mechanisms). If `setErrorHandler` has been used
multiple times to define multiple handlers, the error will be routed to the most
precedent handler defined 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.
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.
Some things to consider in your custom error handler:
The following should be considered when using a custom error handler:
- you can `reply.send(data)`, which will behave as it would in [regular route
handlers](./Reply.md#senddata)
- `reply.send(data)` behaves as in [regular route handlers](./Reply.md#senddata)
- objects are serialized, triggering the `preSerialization` lifecycle hook if
you have one defined
- strings, buffers, and streams are sent to the client, with appropriate
headers (no serialization)
defined
- strings, buffers, and streams are sent to the client with appropriate headers
(no serialization)
- You can throw a new error in your custom error handler - errors (new error or
the received error parameter re-thrown) - will call the parent `errorHandler`.
- `onError` hook will be triggered once only for the first error being thrown.
- an error will not be triggered twice from a lifecycle hook - Fastify
internally monitors the error invocation to avoid infinite loops for errors
thrown in the reply phases of the lifecycle. (those after the route handler)
- 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 utilizing Fastify's custom error handling through [`setErrorHandler`](./Server.md#seterrorhandler),
you should be aware of how errors are propagated between custom and default
error handlers.
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, and the error is not an
instance of [Error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
(as seen in the `/bad` route in the following example), it will not propagate
to the parent context error handler. Instead, it will be caught by the default
error handler.
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, it is recommended to throw instances of
`Error`. For instance, in the following example, replacing `throw 'foo'` with
`throw new Error('foo')` in the `/bad` route ensures that errors propagate through
the custom error handling chain as intended. This practice helps avoid potential
pitfalls when working with custom error handling in Fastify.
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
@@ -238,7 +241,7 @@ You can access `errorCodes` for mapping:
// ESM
import { errorCodes } from 'fastify'
// CommonJs
// CommonJS
const errorCodes = require('fastify').errorCodes
```
@@ -263,7 +266,7 @@ fastify.setErrorHandler(function (error, request, reply) {
// Send error response
reply.status(500).send({ ok: false })
} else {
// fastify will use parent error handler to handle this
// Fastify will use parent error handler to handle this
reply.send(error)
}
})
@@ -278,7 +281,7 @@ fastify.listen({ port: 3000 }, function (err, address) {
})
```
Below is a table with all the error codes that Fastify uses.
Below is a table with all the error codes used by Fastify.
| Code | Description | How to solve | Discussion |
|------|-------------|--------------|------------|
@@ -289,7 +292,6 @@ Below is a table with all the error codes that Fastify uses.
| <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_version_constraint_not_str">FST_ERR_VERSION_CONSTRAINT_NOT_STR</a> | Version constraint wrongly specified. | Version constraint should be a string. | [#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) |
@@ -298,13 +300,16 @@ Below is a table with all the error codes that Fastify uses.
| <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 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_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) |
@@ -313,8 +318,12 @@ Below is a table with all the error codes that Fastify uses.
| <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) |
@@ -333,13 +342,11 @@ Below is a table with all the error codes that Fastify uses.
| <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_http2_invalid_version">FST_ERR_HTTP2_INVALID_VERSION</a> | HTTP2 is available only from node >= 8.8.1. | Use a higher version of node. | [#1346](https://github.com/fastify/fastify/pull/1346) |
| <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_default_route_invalid_type">FST_ERR_DEFAULT_ROUTE_INVALID_TYPE</a> | The `defaultRoute` type should be a function. | Use a function for the `defaultRoute`. | [#2733](https://github.com/fastify/fastify/pull/2733) |
| <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) |
@@ -359,7 +366,7 @@ Below is a table with all the error codes that Fastify uses.
| <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_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) |

View File

@@ -2,11 +2,11 @@
## HTTP2
_Fastify_ supports HTTP2 over either HTTPS (h2) or plaintext (h2c).
_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 our `Request` and `Reply`
interface. PRs are welcome.
Node's `req` and `res` can be accessed through the `Request` and `Reply`
interfaces. PRs are welcome.
### Secure (HTTPS)
@@ -61,7 +61,7 @@ fastify.get('/', function (request, reply) {
fastify.listen({ port: 3000 })
```
You can test your new server with:
Test the new server with:
```
$ npx h2url https://localhost:3000
@@ -69,8 +69,8 @@ $ npx h2url https://localhost:3000
### Plain or insecure
If you are building microservices, you can connect to HTTP2 in plain text,
however, this is not supported by browsers.
For microservices, HTTP2 can connect in plain text, but this is not
supported by browsers.
```js
'use strict'
@@ -86,7 +86,7 @@ fastify.get('/', function (request, reply) {
fastify.listen({ port: 3000 })
```
You can test your new server with:
Test the new server with:
```
$ npx h2url http://localhost:3000

View File

@@ -34,9 +34,9 @@ are Request/Reply hooks and application hooks:
- [Using Hooks to Inject Custom Properties](#using-hooks-to-inject-custom-properties)
- [Diagnostics Channel Hooks](#diagnostics-channel-hooks)
**Notice:** 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.
> 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
@@ -68,9 +68,9 @@ fastify.addHook('onRequest', async (request, reply) => {
})
```
**Notice:** in the [onRequest](#onrequest) hook, `request.body` will always be
`undefined`, because the body parsing happens before the
[preValidation](#prevalidation) hook.
> Note: In the [onRequest](#onrequest) hook, `request.body` will always be
> `undefined`, because the body parsing happens before the
> [preValidation](#prevalidation) hook.
### preParsing
@@ -98,17 +98,17 @@ fastify.addHook('preParsing', async (request, reply, payload) => {
})
```
**Notice:** in the [preParsing](#preparsing) hook, `request.body` will always be
`undefined`, because the body parsing happens before the
[preValidation](#prevalidation) hook.
> Note: In the [preParsing](#preparsing) hook, `request.body` will always be
> `undefined`, because the body parsing happens before the
> [preValidation](#prevalidation) hook.
**Notice:** 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: 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.
**Notice:** The size of the returned stream is checked to not exceed the limit
set in [`bodyLimit`](./Server.md#bodylimit) option.
> Note: The size of the returned stream is checked to not exceed the limit
> set in [`bodyLimit`](./Server.md#bodylimit) option.
### preValidation
@@ -166,8 +166,8 @@ fastify.addHook('preSerialization', async (request, reply, payload) => {
})
```
Note: the hook is NOT called if the payload is a `string`, a `Buffer`, a
`stream`, or `null`.
> Note: The hook is NOT called if the payload is a `string`, a `Buffer`, a
> `stream`, or `null`.
### onError
```js
@@ -189,15 +189,11 @@ 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 only after
the [Custom Error Handler set by `setErrorHandler`](./Server.md#seterrorhandler)
has been executed, and only if the custom error handler sends an error back to the
user
*(Note that the default error handler always sends the error back to the
user)*.
This hook will be executed before
the [Custom Error Handler set by `setErrorHandler`](./Server.md#seterrorhandler).
**Notice:** unlike the other hooks, passing an error to the `done` function is not
supported.
> 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:
@@ -233,8 +229,8 @@ fastify.addHook('onSend', (request, reply, payload, done) => {
> 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`.
> 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
@@ -256,8 +252,8 @@ 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.
> Note: Setting `disableRequestLogging` to `true` will disable any error log
> inside the `onResponse` hook. In this case use `try - catch` to log errors.
### onTimeout
@@ -298,7 +294,8 @@ 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.
**Notice:** client abort detection is not completely reliable. See: [`Detecting-When-Clients-Abort.md`](../Guides/Detecting-When-Clients-Abort.md)
> 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()`
@@ -428,8 +425,8 @@ fastify.addHook('onReady', async function () {
### 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
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
@@ -451,8 +448,8 @@ fastify.addHook('onListen', async function () {
})
```
> **Note**
> This hook will not run when the server is started using `fastify.inject()` or `fastify.ready()`
> Note: This hook will not run when the server is started using
> fastify.inject()` or `fastify.ready()`.
### onClose
<a id="on-close"></a>
@@ -462,7 +459,7 @@ 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,
The hook function takes the Fastify instance as a first argument,
and a `done` callback for synchronous hook functions.
```js
// callback style
@@ -575,8 +572,8 @@ 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).
> 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', [])
@@ -773,7 +770,7 @@ fastify.route({
})
```
**Note**: both options also accept an array of functions.
> Note: Both options also accept an array of functions.
## Using Hooks to Inject Custom Properties
<a id="using-hooks-to-inject-custom-properties"></a>
@@ -828,19 +825,11 @@ consider creating a custom [Plugin](./Plugins.md) instead.
## Diagnostics Channel Hooks
> **Note:** The `diagnostics_channel` is currently experimental on Node.js, so
> its API is subject to change even in semver-patch releases of Node.js. For
> versions of Node.js supported by Fastify where `diagnostics_channel` is
> unavailable, the hook will use the
> [polyfill](https://www.npmjs.com/package/diagnostics_channel) if it is
> available. Otherwise, this feature will not be present.
Currently, 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.
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
@@ -855,7 +844,7 @@ const spans = new WeakMap()
channel.subscribe(function ({ fastify }) {
fastify.addHook('onRequest', (request, reply, done) => {
const span = tracer.startSpan('fastify.request')
const span = tracer.startSpan('fastify.request.handler')
spans.set(request, span)
done()
})
@@ -867,3 +856,41 @@ channel.subscribe(function ({ fastify }) {
})
})
```
> 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)
})
```

View File

@@ -1,8 +1,7 @@
<h1 align="center">Fastify</h1>
## Long Term Support
`<a id="lts"></a>`
<a id="lts"></a>
Fastify's Long Term Support (LTS) is provided according to the schedule laid out
in this document:
@@ -25,13 +24,11 @@ in this document:
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/nsolid), maintained by NodeSource,
commits to testing and verifying each Fastify major release against the N|Solid
LTS versions that are current at the time of the Fastify release.
NodeSource guarantees that Fastify will be compatible and function correctly
with N|Solid, aligning with the support and compatibility scope of the N|Solid
LTS versions available at the time of the Fastify release.
This ensures users of N|Solid can confidently use Fastify.
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.
@@ -41,27 +38,32 @@ A "month" is defined as 30 consecutive days.
> 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 with caution.
> 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>`
<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 | TBD | 14, 16, 18, 20 | v5(18), v5(20) |
| 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>`
<a id="supported-os"></a>
Fastify uses GitHub Actions for CI testing, please refer to [GitHub&#39;s
documentation regarding workflow
@@ -71,12 +73,14 @@ YAML workflow labels below:
| OS | YAML Workflow Label | Package Manager | Node.js | Nsolid(Node) |
| ------- | ------------------- | --------------- | ----------- | ------------- |
| Linux | `ubuntu-latest` | npm | 14,16,18,20 | v5(18),v5(20) |
| Linux | `ubuntu-latest` | yarn,pnpm | 14,16,18,20 | v5(18),v5(20) |
| Windows | `windows-latest` | npm | 14,16,18,20 | v5(18),v5(20) |
| MacOS | `macos-latest` | npm | 14,16,18,20 | v5(18),v5(20) |
| 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

View File

@@ -3,12 +3,11 @@
## Lifecycle
<a id="lifecycle"></a>
Following the schema of the internal lifecycle of Fastify.
This schema shows the internal lifecycle of Fastify.
On the right branch of every section there is the next phase of the lifecycle,
on the left branch there is the corresponding error code that will be generated
if the parent throws an error *(note that all the errors are automatically
handled by 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
@@ -42,26 +41,23 @@ Incoming Request
└─▶ onResponse Hook
```
At any point before or during the `User Handler`, `reply.hijack()` can be called
to prevent Fastify from:
- Running all the following hooks and user handler
- Sending the response automatically
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
NB (*): If `reply.raw` is used to send a response back to the user, `onResponse`
hooks will still be executed
If `reply.raw` is used to send a response, `onResponse` hooks will still
be executed.
## Reply Lifecycle
<a id="reply-lifecycle"></a>
Whenever the user handles the request, the result may be:
When the user handles the request, the result may be:
- in async handler: it returns a payload
- in async handler: it throws an `Error`
- in sync handler: it sends a payload
- in sync handler: it sends an `Error` instance
- 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, we skip all the below steps. Otherwise, when it is
being submitted, the data flow performed is the following:
If the reply was hijacked, all subsequent steps are skipped. Otherwise, when
submitted, the data flow is as follows:
```
★ schema validation Error
@@ -74,16 +70,15 @@ being submitted, the data flow performed is the following:
★ send or return │ │
│ │ │
│ ▼ │
reply sent ◀── JSON ─┴─ Error instance ──▶ setErrorHandler ◀─────┘
reply sent ◀── JSON ─┴─ Error instance ──▶ onError Hook ◀───────┘
reply sent ◀── JSON ─┴─ Error instance ──▶ onError Hook
reply sent ◀── JSON ─┴─ Error instance ──▶ setErrorHandler
└─▶ reply sent
```
Note: `reply sent` means that the JSON payload will be serialized by:
- the [reply serialized](./Server.md#setreplyserializer) if set
- or by the [serializer compiler](./Server.md#setserializercompiler) when a JSON
schema has been set for the returning HTTP status code
- or by the default `JSON.stringify` function
`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

View File

@@ -2,17 +2,18 @@
## Logging
### Enable logging
Logging is disabled by default, and you can enable it by passing `{ logger: true
}` or `{ logger: { level: 'info' } }` when you create a Fastify instance. Note
that if the logger is disabled, it is impossible to enable it at runtime. We use
[abstract-logging](https://www.npmjs.com/package/abstract-logging) for this
purpose.
### 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, when enabled, set to `'info'`.
level set to `'info'` when enabled.
#### Basic logging setup
Enabling the production JSON logger:
```js
@@ -21,8 +22,9 @@ const fastify = require('fastify')({
})
```
Enabling the logger with appropriate configuration for both local development
and production and test environment requires a bit more configuration:
#### Environment-Specific Configuration
Enabling the logger with appropriate configuration for local development,
production, and test environments requires more configuration:
```js
const envToLogger = {
@@ -42,11 +44,11 @@ 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
⚠️ `pino-pretty` needs to be installed as a dev dependency. It is not included
by default for performance reasons.
### Usage
You can use the logger like this in your route handlers:
The logger can be used in route handlers as follows:
```js
fastify.get('/', options, function (request, reply) {
@@ -55,16 +57,16 @@ fastify.get('/', options, function (request, reply) {
})
```
You can trigger new logs outside route handlers by using the Pino instance from
the Fastify instance:
Trigger new logs outside route handlers using the Pino instance from the Fastify
instance:
```js
fastify.log.info('Something important happened!');
```
If you want to pass some options to the logger, just pass them to Fastify.
You can find all available options in the
[Pino documentation](https://github.com/pinojs/pino/blob/master/docs/api.md#options).
If you want to specify a file destination, use:
#### 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')({
@@ -80,8 +82,8 @@ fastify.get('/', options, function (request, reply) {
})
```
If you want to pass a custom stream to the Pino instance, just add a stream
field to the logger object.
To pass a custom stream to the Pino instance, add a `stream` field to the logger
object:
```js
const split = require('split2')
@@ -95,19 +97,25 @@ const fastify = require('fastify')({
})
```
<a id="logging-request-id"></a>
### 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
"request-id" header is present its value is used, otherwise a new incremental ID
is generated. See Fastify Factory
`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.
The default logger is configured with a set of standard serializers that
serialize objects with `req`, `res`, and `err` properties. The object received
by `req` is the Fastify [`Request`](./Request.md) object, while the object
received by `res` is the Fastify [`Reply`](./Reply.md) object. This behavior
can be customized by specifying custom serializers.
> ⚠ Warning: enabling `requestIdHeader` allows any callers to set `reqId` to a
> value of their choosing.
> No validation is performed on `requestIdHeader`.
#### 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')({
@@ -121,7 +129,7 @@ const fastify = require('fastify')({
})
```
For example, the response payload and headers could be logged using the approach
below (even if it is *not recommended*):
below (not recommended):
```js
const fastify = require('fastify')({
@@ -140,12 +148,11 @@ const fastify = require('fastify')({
return {
method: request.method,
url: request.url,
path: request.routerPath,
path: request.routeOptions.url,
parameters: request.params,
// Including the headers in the log could be in violation
// of privacy laws, e.g. GDPR. You should use the "redact" option to
// remove sensitive fields. It could also leak authentication data in
// the logs.
// 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
};
}
@@ -154,11 +161,11 @@ const fastify = require('fastify')({
});
```
**Note**: In certain cases, the [`Reply`](./Reply.md) object passed to the `res`
serializer cannot be fully constructed. When writing a custom `res` serializer,
it is necessary to check for the existence of any properties on `reply` aside
from `statusCode`, which is always present. For example, the existence of
`getHeaders` must be verified before it can be called:
> 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')({
@@ -170,7 +177,7 @@ const fastify = require('fastify')({
res (reply) {
// The default
return {
statusCode: reply.statusCode
statusCode: reply.statusCode,
headers: typeof reply.getHeaders === 'function'
? reply.getHeaders()
: {}
@@ -181,11 +188,11 @@ const fastify = require('fastify')({
});
```
**Note**: The body cannot be serialized inside a `req` method because the
request is serialized when we create the child logger. At that time, the body is
not yet parsed.
> 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 an approach to log `req.body`
See the following approach to log `req.body`:
```js
app.addHook('preHandler', function (req, reply, done) {
@@ -196,23 +203,24 @@ app.addHook('preHandler', function (req, reply, done) {
})
```
**Note**: Care should be taken to ensure serializers never throw, as an error
thrown from a serializer has the potential to cause the Node process to exit.
See the [Pino documentation](https://getpino.io/#/docs/api?id=opt-serializers)
on serializers for more information.
> 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.*
You can also supply your own logger instance. Instead of passing configuration
options, pass the instance. The logger you supply must conform to the Pino
interface; that is, it must have the following methods: `info`, `error`,
`debug`, `fatal`, `warn`, `trace`, `silent`, `child` and a string property `level`.
### 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')({ logger: log })
const fastify = require('fastify')({ loggerInstance: log })
log.info('does not have request information')
@@ -225,11 +233,11 @@ fastify.get('/', function (request, reply) {
*The logger instance for the current request is available in every part of the
[lifecycle](./Lifecycle.md).*
## Log Redaction
### Log Redaction
[Pino](https://getpino.io) supports low-overhead log redaction for obscuring
values of specific properties in recorded logs. As an example, we might want to
log all the HTTP headers minus the `Authorization` header for security concerns:
values of specific properties in recorded logs. For example, log all HTTP
headers except the `Authorization` header for security:
```js
const fastify = Fastify({
@@ -243,7 +251,7 @@ const fastify = Fastify({
method: request.method,
url: request.url,
headers: request.headers,
hostname: request.hostname,
host: request.host,
remoteAddress: request.ip,
remotePort: request.socket.remotePort
}

View File

@@ -22,36 +22,36 @@ fastify.use(require('ienoopen')())
fastify.use(require('x-xss-protection')())
```
You can also use [`@fastify/middie`](https://github.com/fastify/middie), which provides
support for simple Express-style middleware but with improved performance:
[`@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')())
```
Remember that middleware can be encapsulated; this means that you can decide
where your middleware should run by using `register` as explained in the
[plugins guide](../Guides/Plugins-Guide.md).
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
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. If you need to create
middleware, you have to use the Node `req` and `res` instances. Otherwise, you
can use the `preHandler` hook that already has the
[Request](./Request.md#request) and [Reply](./Reply.md#reply) Fastify instances.
For more information, see [Hooks](./Hooks.md#hooks).
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>
If you need to only run middleware under certain paths, just pass the path as
the first parameter to `use` and you are done!
To run middleware under certain paths, pass the path as the first parameter to
`use`.
*Note that this does not support routes with parameters, (e.g.
`/user/:id/comments`) and wildcards are not supported in multiple paths.*
> 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')
@@ -69,10 +69,10 @@ fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets')))
### Alternatives
Fastify offers some alternatives to the most commonly used middleware, such as
[`@fastify/helmet`](https://github.com/fastify/fastify-helmet) in case of
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).
[`serve-static`](https://github.com/expressjs/serve-static).

View File

@@ -1,21 +1,18 @@
<h1 align="center">Fastify</h1>
## Plugins
Fastify allows the user to extend its functionalities with plugins. A plugin can
be a set of routes, a server [decorator](./Decorators.md), or whatever. The API
that you will need to use one or more plugins, is `register`.
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*, this means that if you make some
changes to the Fastify instance (via `decorate`), this change will not be
reflected by the current context ancestors, but only by its descendants. This
feature allows us to achieve plugin *encapsulation* and *inheritance*, in this
way we create a *directed acyclic graph* (DAG) and we will not have issues
caused by cross dependencies.
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.
You may have already seen in the [Getting
Started](../Guides/Getting-Started.md#your-first-plugin) guide how easy it is
to use this API:
```
The [Getting Started](../Guides/Getting-Started.md#your-first-plugin) guide
includes an example of using this API:
```js
fastify.register(plugin, [options])
```
@@ -33,10 +30,9 @@ Fastify specific options is:
+ [`logSerializers`](./Routes.md#custom-log-serializer)
+ [`prefix`](#route-prefixing-option)
**Note: Those options will be ignored when used with fastify-plugin**
These options will be ignored when used with fastify-plugin.
It is possible that Fastify will directly support other options in the future.
Thus, to avoid collisions, a plugin should consider namespacing its options. For
To avoid collisions, a plugin should consider namespacing its options. For
example, a plugin `foo` might be registered like so:
```js
@@ -49,8 +45,7 @@ fastify.register(require('fastify-foo'), {
})
```
If collisions are not a concern, the plugin may simply accept the options object
as-is:
If collisions are not a concern, the plugin may accept the options object as-is:
```js
fastify.register(require('fastify-foo'), {
@@ -60,9 +55,8 @@ fastify.register(require('fastify-foo'), {
})
```
The `options` parameter can also be a `Function` that will be evaluated at the
time the plugin is registered while giving access to the Fastify instance via
the first positional argument:
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')
@@ -77,40 +71,38 @@ fastify.register(fp((fastify, opts, done) => {
fastify.register(require('fastify-foo'), parent => parent.foo_bar)
```
The Fastify instance passed on 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 in case a plugin
depends on changes made to the Fastify instance by a preceding plugin i.e.
utilizing an existing database connection to wrap around it.
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 on to the function is the same as
the one that will be 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 plugins function i.e. if `decorate` is called,
the decorated variables will be available within the plugins function unless it
was wrapped with [`fastify-plugin`](https://github.com/fastify/fastify-plugin).
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 you pass an option with the key `prefix` with a `string` value, Fastify will
use it to prefix all the routes inside the register, for more info check
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 you wrap your routes with
Be aware that if routes are wrapped with
[`fastify-plugin`](https://github.com/fastify/fastify-plugin), this option will
not work (there is a [workaround](./Routes.md#fastify-plugin) available).
not work (see the [workaround](./Routes.md#fastify-plugin)).
#### Error handling
<a id="error-handling"></a>
The error handling is done by
[avvio](https://github.com/mcollina/avvio#error-handling).
Error handling is done by [avvio](https://github.com/mcollina/avvio#error-handling).
As a general rule, it is highly recommended that you handle your errors in the
next `after` or `ready` block, otherwise you will get them inside the `listen`
callback.
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'))
@@ -145,16 +137,15 @@ await fastify.ready()
await fastify.listen({ port: 3000 })
```
*Note: Using `await` when registering a plugin loads the plugin
and the underlying dependency tree, "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.*
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 as well from [Node.js
`v13.3.0`](https://nodejs.org/api/esm.html) and above!
ESM is supported from [Node.js `v13.3.0`](https://nodejs.org/api/esm.html)
and above.
```js
// main.mjs
@@ -179,21 +170,29 @@ export default plugin
### Create a plugin
<a id="create-plugin"></a>
Creating a plugin is very easy, you just need to create a function that takes
three parameters, the `fastify` instance, an `options` object, and the `done`
callback.
Creating a plugin is easy. Create a function that takes three parameters: the
`fastify` instance, an `options` object, and the `done` callback. Alternatively,
use an `async` function and omit the `done` callback.
Example:
```js
module.exports = function (fastify, opts, done) {
module.exports = function callbackPlugin (fastify, opts, done) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
done()
}
// Or using async
module.exports = async function asyncPlugin (fastify, opts) {
fastify.decorate('utility', function () {})
fastify.get('/', handler)
}
```
You can also use `register` inside another `register`:
`register` can also be used inside another `register`:
```js
module.exports = function (fastify, opts, done) {
fastify.decorate('utility', function () {})
@@ -205,28 +204,23 @@ module.exports = function (fastify, opts, done) {
done()
}
```
Sometimes, you will need to know when the server is about to close, for example,
because you must close a connection to a database. To know when this is going to
happen, you can use the [`'onClose'`](./Hooks.md#on-close) hook.
Do not forget that `register` will always create a new Fastify scope, if you do
not need that, read the following section.
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 you are using `register` only for extending the functionality of the server
with [`decorate`](./Decorators.md), it is your responsibility to tell Fastify
not to create a new scope. Otherwise, your changes will not be accessible by the
user in the upper scope.
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.
You have two ways to tell Fastify to avoid the creation of a new context:
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
We recommend using the `fastify-plugin` module, because it solves this problem
for you, and you can pass a version range of Fastify as a parameter that your
plugin will support.
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')
@@ -238,10 +232,9 @@ module.exports = fp(function (fastify, opts, done) {
Check the [`fastify-plugin`](https://github.com/fastify/fastify-plugin)
documentation to learn more about how to use this module.
If you do not use the `fastify-plugin` module, you can use the `'skip-override'`
hidden property, but we do not recommend it. If in the future the Fastify API
changes it will be your responsibility to update the module, while if you use
`fastify-plugin`, you can be sure about backward compatibility.
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 () {})

View File

@@ -16,48 +16,41 @@ the following technical principles:
## "Zero" Overhead in Production
Fastify aims to implement its features by adding as minimal overhead to your
application as possible.
This is usually delivered by implementing fast algorithms and data structures,
as well as JavaScript-specific features.
Fastify aims to implement features with minimal overhead. This is achieved by
using fast algorithms, data structures, and JavaScript-specific features.
Given that JavaScript does not offer zero-overhead data structures, this principle
is at odds with providing a great developer experience and providing more features,
as usually those cost some overhead.
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 the performance point
it is operating.
It provides a great out-of-the-box experience that is flexible enough to be
adapted to a variety of situations.
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.
As an example, this means that binary addons are forbidden because most JavaScript
developers would not
have access to a compiler.
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
We recognize that most applications start small and become more complex over time.
Fastify aims to grow with
the complexity of your application, providing advanced features to structure
your codebase.
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
How you deploy your routes should not matter. The framework should "just work".
Route deployment should not matter. The framework should "just work".
## Security and Data Validation
Your web framework is the first point of contact with untrusted data, and it
needs to act as the first line of defense for your system.
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
We recognize that there are an infinite amount of use cases for an HTTP framework
for Node.js. Catering to them in a single module would make the codebase unmaintainable.
Therefore we provide hooks and options to allow you to customize the framework
as you please.
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
@@ -65,14 +58,16 @@ Testing Fastify applications should be a first-class concern.
## Do not monkeypatch core
Monkeypatch Node.js APIs or installing globals that alter the behavior of the
runtime makes building modular applications harder, and limit the use cases of Fastify.
Other frameworks do this and we do not.
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
We provide a clear Long Term Support strategy so developers can know when to upgrade.
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.
In doubt, we chose the strict behavior as defined by the relevant
Specifications.

View File

@@ -11,15 +11,14 @@
- [.headers(object)](#headersobject)
- [.getHeader(key)](#getheaderkey)
- [.getHeaders()](#getheaders)
- [set-cookie](#set-cookie)
- [.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)
- [.getResponseTime()](#getresponsetime)
- [.type(contentType)](#typecontenttype)
- [.getSerializationFunction(schema | httpStatus, [contentType])](#getserializationfunctionschema--httpstatus)
- [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschemaschema-httpstatus)
@@ -33,8 +32,9 @@
- [Strings](#strings)
- [Streams](#streams)
- [Buffers](#buffers)
- [ReadableStream](#send-readablestream)
- [Response](#send-response)
- [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)
@@ -58,6 +58,8 @@ since the request was received by Fastify.
- `.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.
@@ -68,16 +70,17 @@ since the request was received by Fastify.
- `.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
- `.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
- `.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
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
@@ -86,13 +89,10 @@ since the request was received by Fastify.
already been called.
- `.hijack()` - interrupt the normal request lifecycle.
- `.raw` - The
[`http.ServerResponse`](https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_class_http_serverresponse)
[`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.
- `.getResponseTime()` - Deprecated, returns the amount of time passed
since the request was received by Fastify.
- `.context` - Deprecated, access the [Request's context](./Request.md) property.
```js
fastify.get('/', options, function (request, reply) {
@@ -104,14 +104,6 @@ fastify.get('/', options, function (request, reply) {
})
```
Additionally, `Reply` provides access to the context of the request:
```js
fastify.get('/', {config: {foo: 'bar'}}, function (request, reply) {
reply.send('handler config.foo = ' + reply.context.config.foo)
})
```
### .code(statusCode)
<a id="code"></a>
@@ -123,9 +115,6 @@ If not set via `reply.code`, the resulting `statusCode` will be `200`.
Invokes the custom response time getter to calculate the amount of time passed
since the request was received by Fastify.
Note that unless this function is called in the [`onResponse`
hook](./Hooks.md#onresponse) it will always return `0`.
```js
const milliseconds = reply.elapsedTime
```
@@ -163,14 +152,14 @@ fastify.get('/', async function (req, rep) {
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
> 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-v14.x/docs/api/http.html#http_response_setheader_name_value).
[`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>
@@ -243,6 +232,27 @@ reply.getHeader('x-foo') // undefined
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>
@@ -251,11 +261,11 @@ 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: 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.*
> 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() {
@@ -270,14 +280,14 @@ const { createHash } = require('node:crypto')
reply.trailer('content-md5', function(reply, payload, done) {
const hash = createHash('md5')
hash.update(payload)
done(null, hash.disgest('hex'))
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.disgest('hex')
return hash.digest('hex')
})
```
@@ -305,7 +315,7 @@ reply.getTrailer('server-timing') // undefined
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
> 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
@@ -343,22 +353,6 @@ hook specified in [`setNotFoundHandler`](./Server.md#set-not-found-handler).
reply.callNotFound()
```
### .getResponseTime()
<a id="getResponseTime"></a>
Invokes the custom response time getter to calculate the amount of time passed
since the request was received by Fastify.
Note that unless this function is called in the [`onResponse`
hook](./Hooks.md#onresponse) it will always return `0`.
```js
const milliseconds = reply.getResponseTime()
```
*Note: This method is deprecated and will be removed in `fastify@5`.
Use the [.elapsedTime](#elapsedtime) property instead.*
### .type(contentType)
<a id="type"></a>
@@ -369,13 +363,14 @@ Sets the content type for the response. This is a shortcut for
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.
`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
By calling this function using a provided `schema` or `httpStatus`,
and the optional `contentType`, it will return a `serialization` function
that can be used to serialize diverse inputs. It returns `undefined` if no
serialization function was found using either of the provided inputs.
@@ -385,12 +380,12 @@ the serialization functions compiled by using `compileSerializationSchema`.
```js
const serialize = reply
.getSerializationFunction({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
@@ -419,8 +414,8 @@ 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
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
@@ -429,12 +424,12 @@ the serialization functions compiled by using `compileSerializationSchema`.
```js
const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
@@ -442,12 +437,12 @@ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
const serialize = reply
.compileSerializationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200)
serialize({ foo: 'bar' }) // '{"foo":"bar"}'
@@ -493,7 +488,7 @@ const schema1 = {
```
*Not*
```js
```js
const serialize = reply.compileSerializationSchema(schema1)
// Later on...
@@ -527,25 +522,25 @@ function will be compiled, forwarding the `httpStatus` and `contentType` if prov
```js
reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}) // '{"foo":"bar"}'
// or
reply
.serializeInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200) // '{"foo":"bar"}'
// or
@@ -594,7 +589,7 @@ values.
<a id="raw"></a>
This is the
[`http.ServerResponse`](https://nodejs.org/dist/latest-v14.x/docs/api/http.html#http_class_http_serverresponse)
[`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.:
@@ -694,9 +689,13 @@ If you are sending a stream and you have not set a `'Content-Type'` header,
As noted above, streams are considered to be pre-serialized, so they will be
sent unmodified without response validation.
See special note about error handling for streams in
[`setErrorHandler`](./Server.md#seterrorhandler).
```js
const fs = require('node:fs')
fastify.get('/streams', function (request, reply) {
const fs = require('node:fs')
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
reply.send(stream)
@@ -704,8 +703,9 @@ fastify.get('/streams', function (request, reply) {
```
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 fs = require('node:fs')
const stream = fs.createReadStream('some-file', 'utf8')
reply.header('Content-Type', 'application/octet-stream')
return reply.send(stream)
@@ -718,11 +718,12 @@ fastify.get('/streams', async function (request, reply) {
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
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)
@@ -733,6 +734,7 @@ fastify.get('/streams', function (request, reply) {
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)
@@ -747,11 +749,12 @@ fastify.get('/streams', async function (request, reply) {
`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
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)
@@ -762,12 +765,13 @@ fastify.get('/streams', function (request, reply) {
<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
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')
@@ -780,7 +784,7 @@ fastify.get('/streams', function (request, reply) {
`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
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
@@ -792,6 +796,7 @@ 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)
@@ -822,8 +827,8 @@ automatically create an error structured as the following:
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.*
> 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
@@ -870,14 +875,14 @@ fastify.get('/', {
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*
> Note: you are responsible for logging when customizing the error handler.
API:
```js
fastify.setErrorHandler(function (error, request, reply) {
request.log.warn(error)
var statusCode = error.statusCode >= 400 ? error.statusCode : 500
const statusCode = error.statusCode >= 400 ? error.statusCode : 500
reply
.code(statusCode)
.type('text/plain')

View File

@@ -4,80 +4,71 @@
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
- `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)
- `hostname` - the host of the incoming request (derived from `X-Forwarded-Host`
[`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.
- `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`, this allows you to access the
original `url` in case of internal re-routing
- `routerMethod` - Deprecated, use `request.routeOptions.method` instead. The
method defined for the router that is handling the request
- `routerPath` - Deprecated, use `request.routeOptions.url` instead. The
path pattern defined for the router that is handling the request
- `is404` - true if request is being handled by 404 handler, false if it is not
- `connection` - Deprecated, use `socket` instead. The underlying connection of
the incoming request.
- `socket` - the underlying connection of the incoming request
- `context` - Deprecated, use `request.routeOptions.config` instead.
A Fastify internal object. You should not use
it directly or modify it. It is useful to access one special key:
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.
- `routeSchema` - Deprecated, use `request.routeOptions.schema` instead. The
scheme definition set for the router that is handling the request
- `routeConfig` - Deprecated, use `request.routeOptions.config` instead. 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 /
- `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 any of either are set or cached.
- [.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`.
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 specified input by using the specified
schema and returns the serialized payload. If the optional
`httpPart` is provided, the function will use the serializer
function given for that HTTP Status Code. Defaults to `null`.
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. You can set custom headers like this:
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 = {
@@ -86,12 +77,15 @@ request.headers = {
}
```
This operation will add to the request headers the new values that can be read
calling `request.headers.bar`. Moreover, you can still access the standard
request's headers with the `request.raw.headers` property.
This operation adds new values to the request headers, accessible via
`request.headers.bar`. Standard request headers remain accessible via
`request.raw.headers`.
> Note: For performance reason on `not found` route, you may see that we will
add an extra property `Symbol('fastify.RequestAcceptVersion')` on the 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) {
@@ -104,7 +98,9 @@ fastify.post('/:params', options, function (request, reply) {
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)
@@ -123,23 +119,22 @@ fastify.post('/:params', options, function (request, reply) {
### .getValidationFunction(schema | httpPart)
<a id="getvalidationfunction"></a>
By calling this function using a provided `schema` or `httpPart`,
it will return a `validation` function that can be used to
validate diverse inputs. It returns `undefined` if no
serialization function was found using either of the provided inputs.
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 property errors. Errors encountered during the last validation
are assigned to errors
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'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null
@@ -152,34 +147,33 @@ console.log(validate({ foo: 0.5 })) // false
console.log(validate.errors) // validation errors
```
See [.compileValidationSchema(schema, [httpStatus])](#compilevalidationschema)
for more information on how to compile validation function.
See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on compiling validation schemas.
### .compileValidationSchema(schema, [httpPart])
<a id="compilevalidationschema"></a>
This function will compile a validation schema and
return a function that can be used to validate data.
The function returned (a.k.a. _validation function_) is compiled
by using the provided [`SchemaController#ValidationCompiler`](./Server.md#schema-controller).
A `WeakMap` is used to cached this, reducing compilation calls.
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 directly
the `ValidationCompiler`, so it can be used to compile the validation
function if a custom `ValidationCompiler` is provided for the route.
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 property errors. Errors encountered during the last validation
are assigned to errors
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'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
})
console.log(validate({ foo: 'bar' })) // true
console.log(validate.errors) // null
@@ -188,27 +182,24 @@ console.log(validate.errors) // null
const validate = request
.compileValidationSchema({
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 200)
console.log(validate({ hello: 'world' })) // false
console.log(validate.errors) // validation errors
```
Note that you should be careful when using this function, as it will cache
the compiled validation functions based on the schema provided. If the
schemas provided are mutated or changed, the validation functions will not
detect that the schema has been altered and for instance it will reuse the
previously compiled validation function, as the cache is based on
the reference of the schema (Object) previously provided.
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 there is a need to change the properties of a schema, always opt to create
a totally new schema (object), otherwise the implementation will not benefit from
the cache mechanism.
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
@@ -223,7 +214,7 @@ const schema1 = {
```
*Not*
```js
```js
const validate = request.compileValidationSchema(schema1)
// Later on...
@@ -246,37 +237,36 @@ const newValidate = request.compileValidationSchema(newSchema)
console.log(newValidate === validate) // false
```
### .validateInput(data, [schema | httpStatus], [httpStatus])
### .validateInput(data, [schema | httpPart], [httpPart])
<a id="validate"></a>
This function will validate the input based on the provided schema,
or HTTP part passed. If both are provided, the `httpPart` parameter
will take precedence.
This function validates the input based on the provided schema or HTTP part. If
both are provided, the `httpPart` parameter takes precedence.
If there is not a validation function for a given `schema`, a new validation
function will be compiled, forwarding the `httpPart` if provided.
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'
}
}
.validateInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}) // true
// or
request
.validateInput({ foo: 'bar'}, {
type: 'object',
properties: {
foo: {
type: 'string'
}
}
type: 'object',
properties: {
foo: {
type: 'string'
}
}
}, 'body') // true
// or
@@ -286,4 +276,4 @@ request
```
See [.compileValidationSchema(schema, [httpStatus])](#compileValidationSchema)
for more information on how to compile validation schemas.
for more information on compiling validation schemas.

View File

@@ -2,9 +2,8 @@
## Routes
The route methods will configure the endpoints of your application. You have two
ways to declare a route with Fastify: the shorthand method and the full
declaration.
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)
@@ -32,10 +31,9 @@ fastify.route(options)
### Routes options
<a id="options"></a>
* `method`: currently it supports `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`,
`'POST'`, `'PUT'`, `'OPTIONS'`, `'SEARCH'`, `'TRACE'`, `'PROPFIND'`,
`'PROPPATCH'`, `'MKCOL'`, `'COPY'`, `'MOVE'`, `'LOCK'`, `'UNLOCK'`,
`'REPORT'` and `'MKCALENDAR'`.
* `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
@@ -43,8 +41,7 @@ fastify.route(options)
[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, COPY, MOVE, MKCOL, REPORT, MKCALENDAR
or LOCK method.
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
@@ -62,8 +59,9 @@ fastify.route(options)
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, done)`: a [function](./Hooks.md#preparsing) called
before parsing the request, 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
@@ -95,16 +93,16 @@ fastify.route(options)
* `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
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
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 } })`:
* `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.
@@ -123,8 +121,8 @@ fastify.route(options)
* `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
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
@@ -140,11 +138,11 @@ fastify.route(options)
* `reply` is defined in [Reply](./Reply.md).
**Notice:** The documentation of `onRequest`, `preParsing`, `preValidation`,
`preHandler`, `preSerialization`, `onSend`, and `onResponse` are described in
more detail in [Hooks](./Hooks.md). Additionally, to send a response before the
request is handled by the `handler` please refer to [Respond to a request from a
hook](./Hooks.md#respond-to-a-request-from-a-hook).
> 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
@@ -153,8 +151,11 @@ fastify.route({
url: '/',
schema: {
querystring: {
name: { type: 'string' },
excitement: { type: 'integer' }
type: 'object',
properties: {
name: { type: 'string' },
excitement: { type: 'integer' }
}
},
response: {
200: {
@@ -233,17 +234,17 @@ const opts = {
fastify.get('/', opts)
```
> Note: if the handler is specified in both the `options` and as the third
> parameter to the shortcut method then throws a duplicate `handler` error.
> 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 the *colon* before the parameter name.
For **wildcard**, use the *star*. *Remember that static routes are always
checked before parametric and wildcard.*
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
@@ -265,9 +266,8 @@ fastify.get('/example/:userId/:secretToken', function (request, reply) {
fastify.get('/example/*', function (request, reply) {})
```
Regular expression routes are supported as well, but be aware that you have to
escape slashes. Take note that RegExp is also very expensive in terms of
performance!
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) {
@@ -305,24 +305,24 @@ fastify.get('/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', function (request, r
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 if you add a question mark ("?") to the
end of the parameters name.
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 you can request `/example/posts` as well as `/example/posts/1`.
The optional param will be undefined if not specified.
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, so
prefer a single parameter approach whenever possible, especially on routes that
are on the hot path of your application. If you are interested in how we handle
the routing, check out [find-my-way](https://github.com/delvedor/find-my-way).
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).
If you want a path containing a colon without declaring a parameter, use a
double colon. For example:
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
```
@@ -333,23 +333,23 @@ fastify.post('/name::verb') // will be interpreted as /name:verb
Are you an `async/await` user? We have you covered!
```js
fastify.get('/', options, async function (request, reply) {
var data = await getData()
var processed = await processData(data)
const data = await getData()
const processed = await processData(data)
return processed
})
```
As you can see, we are not calling `reply.send` to send back the data to the
user. You just need to return the body and you are done!
As shown, `reply.send` is not called to send data back to the user. Simply
return the body and you are done!
If you need it you can also send back the data to the user with `reply.send`. In
this case do not forget to `return reply` or `await reply` in your `async`
handler or you will introduce a race condition in certain situations.
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) {
var data = await getData()
var processed = await processData(data)
const data = await getData()
const processed = await processData(data)
return reply.send(processed)
})
```
@@ -377,48 +377,42 @@ fastify.get('/', options, async function (request, reply) {
})
```
**Warning:**
* When using both `return value` and `reply.send(value)` at the same time, the
first one that happens takes precedence, the second value will be discarded,
and a *warn* log will also be emitted because you tried to send a response
twice.
* Calling `reply.send()` outside of the promise is possible but requires special
attention. For more details read [promise-resolution](#promise-resolution).
* You cannot return `undefined`. For more details read
[promise-resolution](#promise-resolution).
> 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 your handler is an `async` function or returns a promise, you should be aware
of the special behavior that is necessary to support the callback and promise
control-flow. When the handler's promise is resolved, the reply will be
automatically sent with its value unless you explicitly await or return `reply`
in your handler.
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 you want to use `async/await` or promises but respond with a value with
`reply.send`:
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 you want to use `async/await` or promises:
2. If using `async/await` or promises:
- **Do not** use `reply.send`.
- **Do** return the value that you want to send.
- **Do** return the value to send.
In this way, we can support both `callback-style` and `async-await`, with the
minimum trade-off. Despite so much freedom we highly recommend going with only
one style because error handling should be handled in a consistent way within
your application.
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.
**Notice**: Every async function returns a promise by itself.
> Note: Every async function returns a promise by itself.
### Route Prefixing
<a id="route-prefixing"></a>
Sometimes you need to maintain two or more different versions of the same API; a
classic approach is to prefix all the routes with the API version number,
`/v1/user` for example. Fastify offers you a fast and smart way to create
different versions of the same API without changing all the route names by hand,
*route prefixing*. Let's see how it works:
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
@@ -445,19 +439,18 @@ module.exports = function (fastify, opts, done) {
done()
}
```
Fastify will not complain because you are using the same name for two different
routes, because at compilation time it will handle the prefix automatically
*(this also means that the performance will not be affected at all!)*.
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 your clients will have access to the following routes:
Now clients will have access to the following routes:
- `/v1/user`
- `/v2/user`
You can do this as many times as you want, it also works for nested `register`,
and route parameters are supported as well.
This can be done multiple times and works for nested `register`. Route
parameters are also supported.
In case you want to use prefix for all of your routes, you can put them inside a
plugin:
To use a prefix for all routes, place them inside a plugin:
```js
const fastify = require('fastify')()
@@ -469,23 +462,21 @@ const route = {
schema: {},
}
fastify.register(function(app, _, done) {
fastify.register(function (app, _, done) {
app.get('/users', () => {})
app.route(route)
done()
}, { prefix: '/v1' }) // global route prefix
await fastify.listen({ port: 0 })
await fastify.listen({ port: 3000 })
```
### Route Prefixing and fastify-plugin
<a id="fastify-plugin"></a>
Be aware that if you use
[`fastify-plugin`](https://github.com/fastify/fastify-plugin) for wrapping your
routes, this option will not work. You can still make it work by wrapping a
plugin in a plugin, e. g.:
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')
@@ -501,27 +492,23 @@ module.exports = fp(async function (app, opts) {
#### Handling of / route inside prefixed plugins
The `/` route has different behavior depending on if the prefix ends with `/` or
not. As an example, if we consider a prefix `/something/`, adding a `/` route
will only match `/something/`. If we consider a prefix `/something`, adding a
`/` route will match both `/something` and `/something/`.
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>
You might need different log levels in your routes; Fastify achieves this in a
very straightforward way.
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).
You just need to pass the option `logLevel` to the plugin option or the route
option with the
[value](https://github.com/pinojs/pino/blob/master/docs/api.md#level-string)
that you need.
Be aware that if you set the `logLevel` at plugin level, also the
Be aware that setting `logLevel` at the plugin level also affects
[`setNotFoundHandler`](./Server.md#setnotfoundhandler) and
[`setErrorHandler`](./Server.md#seterrorhandler) will be affected.
[`setErrorHandler`](./Server.md#seterrorhandler).
```js
// server.js
@@ -533,22 +520,21 @@ fastify.register(require('./routes/events'), { logLevel: 'debug' })
fastify.listen({ port: 3000 })
```
Or you can directly pass it to a route:
Or pass it directly to a route:
```js
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
reply.send({ hello: 'world' })
})
```
*Remember that the custom log level is applied only to the routes, and not to
the global Fastify Logger, accessible with `fastify.log`*
*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, you may need to log a large object but it could be a waste of
resources for some routes. In this case, you can define custom
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 right context!
and attach them in the appropriate context.
```js
const fastify = require('fastify')({ logger: true })
@@ -567,7 +553,7 @@ fastify.register(require('./routes/events'), {
fastify.listen({ port: 3000 })
```
You can inherit serializers by context:
Serializers can be inherited by context:
```js
const fastify = Fastify({
@@ -579,7 +565,7 @@ const fastify = Fastify({
method: req.method,
url: req.url,
headers: req.headers,
hostname: req.hostname,
host: req.host,
remoteAddress: req.ip,
remotePort: req.socket.remotePort
}
@@ -616,7 +602,7 @@ retrieve it in the handler.
const fastify = require('fastify')()
function handler (req, reply) {
reply.send(reply.context.config.output)
reply.send(reply.routeOptions.config.output)
}
fastify.get('/en', { config: { output: 'hello world!' } }, handler)
@@ -628,31 +614,29 @@ fastify.listen({ port: 3000 })
### Constraints
<a id="constraints"></a>
Fastify supports constraining routes to match only certain requests based on
some property of the request, like the `Host` header, or any other value via
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 ready for use: the `version` constraint and
the `host` constraint, and you can add your own custom constraint strategies to
inspect other parts of a request to decide if a route should be executed for a
request.
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 allow you to declare multiple handlers for the same HTTP route
path, which will then be matched according to each 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.
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.
*Be aware that using this feature will cause a degradation of the overall
performances of the router.*
> **Note:** using this feature can degrade the routers performance.
```js
fastify.route({
@@ -675,20 +659,20 @@ fastify.inject({
})
```
> ## ⚠ Security Notice
> Remember to set a
> ⚠ Warning:
> Set a
> [`Vary`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary)
> header in your responses with the value you are using for defining the
> versioning (e.g.: `'Accept-Version'`), to prevent cache poisoning attacks. You
> can also configure this as part of your Proxy/CDN.
> 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 you are using
> 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 you are using
> if ((value = append(header, 'Accept-Version'))) { // or the custom header being used
> reply.header('Vary', value)
> }
> }
@@ -696,22 +680,20 @@ fastify.inject({
> })
> ```
If you declare multiple versions with the same major or minor, Fastify will
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 will not have the `Accept-Version` header, a 404 error will be
returned.
If the request lacks an `Accept-Version` header, a 404 error will be returned.
It is possible to define a custom version matching logic. This can be done
through the [`constraints`](./Server.md#constraints) configuration when creating
a Fastify server instance.
Custom version matching logic can be defined through the
[`constraints`](./Server.md#constraints) configuration when creating a Fastify
server instance.
#### Host Constraints
You can provide a `host` key in the `constraints` route option for to limit that
route to only be matched for certain values of the request `Host` header. `host`
constraint values can be specified as strings for exact matches or RegExps for
arbitrary host matching.
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({
@@ -751,7 +733,7 @@ matching wildcard subdomains (or any other pattern):
fastify.route({
method: 'GET',
url: '/',
constraints: { host: /.*\.fastify\.io/ }, // will match any subdomain of fastify.dev
constraints: { host: /.*\.fastify\.dev/ }, // will match any subdomain of fastify.dev
handler: function (request, reply) {
reply.send('hello world from ' + request.headers.host)
}
@@ -760,10 +742,9 @@ fastify.route({
#### Asynchronous Custom Constraints
Custom constraints can be provided and the `constraint` criteria can be
fetched from another source such as `database`. The use of asynchronous
custom constraints should be a last resort as it impacts router
performance.
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) {
@@ -790,18 +771,18 @@ const secret = {
}
```
> ## ⚠ Security Notice
> When using with asynchronous constraint. It is highly recommend never return error
> inside the callback. If the error is not preventable, it is recommended to provide
> a custom `frameworkErrors` handler to deal with it. Otherwise, you route selection
> may break or expose sensitive information to attackers.
>
> ⚠ 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, res, res) {
> if(err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
> frameworkErrors: function (err, req, res) {
> if (err instanceof Fastify.errorCodes.FST_ERR_ASYNC_CONSTRAINT) {
> res.code(400)
> return res.send("Invalid header provided")
> } else {
@@ -810,25 +791,3 @@ const secret = {
> }
> })
> ```
### ⚠ HTTP version check
Fastify will check the HTTP version of every request, based on configuration
options ([http2](./Server.md#http2), [https](./Server.md#https), and
[serverFactory](./Server.md#serverfactory)), to determine if it matches one or
all of the > following versions: `2.0`, `1.1`, and `1.0`. If Fastify receives a
different HTTP version in the request it will return a `505 HTTP Version Not
Supported` error.
| | 2.0 | 1.1 | 1.0 | skip |
|:------------------------:|:---:|:---:|:---:|:----:|
| http2 | ✓ | | | |
| http2 + https | ✓ | | | |
| http2 + https.allowHTTP1 | ✓ | ✓ | ✓ | |
| https | | ✓ | ✓ | |
| http | | ✓ | ✓ | |
| serverFactory | | | | ✓ |
Note: The internal HTTP version check will be removed in the future when Node
implements [this feature](https://github.com/nodejs/node/issues/43115).

File diff suppressed because it is too large Load Diff

View File

@@ -2,18 +2,16 @@
## Type Providers
Type Providers are a TypeScript only feature that enables Fastify to statically
infer type information directly from inline JSON Schema. They are an alternative
to specifying generic arguments on routes; and can greatly reduce the need to
keep associated types for each schema defined in your project.
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
Type Providers are offered as additional packages you will need to install into
your project. Each provider uses a different inference library under the hood;
allowing you to select the library most appropriate for your needs. Official Type
Provider packages follow a `@fastify/type-provider-{provider-name}` naming
convention, and there are several community ones available as well.
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:
@@ -30,78 +28,73 @@ See also the Type Provider wrapper packages for each of the packages respectivel
### Json Schema to Ts
The following sets up a `json-schema-to-ts` Type Provider
The following sets up a `json-schema-to-ts` Type Provider:
```bash
$ npm i @fastify/type-provider-json-schema-to-ts
```
```typescript
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
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']
}
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!
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
```
### TypeBox
The following sets up a TypeBox Type Provider
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'
import fastify from 'fastify'
const server = fastify().withTypeProvider<TypeBoxTypeProvider>()
server.get('/route', {
schema: {
querystring: Type.Object({
foo: Type.Number(),
bar: Type.String()
})
}
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!
// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})
```
See also the [TypeBox
documentation](https://github.com/sinclairzx81/typebox#validation) on how to set
up AJV to work with TypeBox.
See the [TypeBox
documentation](https://sinclairzx81.github.io/typebox/#/docs/overview/2_setup)
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.
for Zod Type Provider instructions.
### Scoped Type-Provider
@@ -159,9 +152,9 @@ fastify.register(pluginWithJsonSchema)
fastify.register(pluginWithTypebox)
```
It's also important to mention that once the types don't propagate globally,
_currently_ is not possible to avoid multiple registrations on routes when
dealing with several scopes, see below:
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'
@@ -183,7 +176,7 @@ function plugin1(fastify: FastifyInstance, _opts, done): void {
})
}
}, (req) => {
// it doesn't work! in a new scope needs to call `withTypeProvider` again
// In a new scope, call `withTypeProvider` again to ensure it works
const { x, y, z } = req.body
});
done()
@@ -210,8 +203,8 @@ function plugin2(fastify: FastifyInstance, _opts, done): void {
### Type Definition of FastifyInstance + TypeProvider
When working with modules one has to make use of `FastifyInstance` with Type
Provider generics. See the example below:
When working with modules, use `FastifyInstance` with Type Provider generics.
See the example below:
```ts
// index.ts

View File

@@ -147,7 +147,7 @@ route-level `request` object.
reply.code(200).send('uh-oh');
// it even works for wildcards
reply.code(404).send({ error: 'Not found' });
return `logged in!`
return { success: true }
})
```
@@ -173,7 +173,7 @@ route-level `request` object.
}, async (request, reply) => {
const customerHeader = request.headers['h-Custom']
// do something with request data
return `logged in!`
return { success: true }
})
```
7. Build and run and query with the `username` query string option set to
@@ -182,7 +182,7 @@ route-level `request` object.
admin"}`
🎉 Good work, now you can define interfaces for each route and have strictly
typed request and reply instances. Other parts of the Fastify type system rely
typed request and reply instances. Other parts of the Fastify type system rely
on generic properties. Make sure to reference the detailed type system
documentation below to learn more about what is available.
@@ -210,24 +210,23 @@ And a `zod` wrapper by a third party called [`fastify-type-provider-zod`](https:
They simplify schema validation setup and you can read more about them in [Type
Providers](./Type-Providers.md) page.
Below is how to setup schema validation using _vanilla_ `typebox` and
`json-schema-to-ts` packages.
Below is how to setup schema validation using the `typebox`,
`json-schema-to-typescript`, and `json-schema-to-ts` packages without type
providers.
#### TypeBox
A useful library for building types and a schema at once is
[TypeBox](https://www.npmjs.com/package/@sinclair/typebox) along with
[fastify-type-provider-typebox](https://github.com/fastify/fastify-type-provider-typebox).
With TypeBox you define your schema within your code and use them
directly as types or schemas as you need them.
A useful library for building types and a schema at once is [TypeBox](https://www.npmjs.com/package/@sinclair/typebox).
With TypeBox you define your schema within your code and use them directly as
types or schemas as you need them.
When you want to use it for validation of some payload in a fastify route you
can do it as follows:
1. Install `typebox` and `fastify-type-provider-typebox` in your project.
1. Install `typebox` in your project.
```bash
npm i @sinclair/typebox @fastify/type-provider-typebox
npm i @sinclair/typebox
```
2. Define the schema you need with `Type` and create the respective type with
@@ -248,10 +247,9 @@ can do it as follows:
```typescript
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
// ...
const fastify = Fastify().withTypeProvider<TypeBoxTypeProvider>()
const fastify = Fastify()
fastify.post<{ Body: UserType, Reply: UserType }>(
'/',
@@ -271,12 +269,12 @@ can do it as follows:
)
```
#### Schemas in JSON Files
#### json-schema-to-typescript
In the last example we used interfaces to define the types for the request
querystring and headers. Many users will already be using JSON Schemas to define
these properties, and luckily there is a way to transform existing JSON Schemas
into TypeScript interfaces!
In the last example we used Typebox to define the types and schemas for our
route. Many users will already be using JSON Schemas to define these properties,
and luckily there is a way to transform existing JSON Schemas into TypeScript
interfaces!
1. If you did not complete the 'Getting Started' example, go back and follow
steps 1-4 first.
@@ -596,7 +594,7 @@ your plugin.
}
module.exports = fp(myPlugin, {
fastify: '3.x',
fastify: '5.x',
name: 'my-plugin' // this is used by fastify-plugin to derive the property name
})
```
@@ -634,7 +632,7 @@ newer, automatically adds `.default` property and a named export to the exported
plugin. Be sure to `export default` and `export const myPlugin` in your typings
to provide the best developer experience. For a complete example you can check
out
[@fastify/swagger](https://github.com/fastify/fastify-swagger/blob/master/index.d.ts).
[@fastify/swagger](https://github.com/fastify/fastify-swagger/blob/main/index.d.ts).
With those files completed, the plugin is now ready to be consumed by any
TypeScript project!
@@ -689,6 +687,143 @@ Or even explicit config on tsconfig
}
```
#### `getDecorator<T>`
Fastify's `getDecorator<T>` method retrieves decorators with enhanced type safety.
The `getDecorator<T>` method supports generic type parameters for enhanced type
safety:
```typescript
// Type-safe decorator retrieval
const usersRepository = fastify.getDecorator<IUsersRepository>('usersRepository')
const session = request.getDecorator<ISession>('session')
const sendSuccess = reply.getDecorator<SendSuccessFn>('sendSuccess')
```
**Alternative to Module Augmentation**
Decorators are typically typed via module augmentation:
```typescript
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 limiting types scope:
```typescript
serverOne.register(async function (fastify) {
const usersRepository = fastify.getDecorator<PostgreUsersRepository>(
'usersRepository'
)
fastify.decorateRequest('session', null)
fastify.addHook('onRequest', async (req, reply) => {
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 is common to infer function types instead of writing them manually:
```typescript
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, use the `OmitThisParameter` utility:
```typescript
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()
})
```
#### `setDecorator<T>`
Fastify's `setDecorator<T>` method provides enhanced type safety for updating request
decorators.
The `setDecorator<T>` method provides enhanced type safety for updating request
decorators:
```typescript
fastify.decorateRequest('user', '')
fastify.addHook('preHandler', async (req, reply) => {
// Type-safe decorator setting
req.setDecorator<string>('user', 'Bob Dylan')
})
```
**Type Safety Benefits**
If the `FastifyRequest` interface does not declare the decorator, type assertions
are typically needed:
```typescript
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 providing type safety:
```typescript
fastify.addHook('preHandler', async (req, reply) => {
req.setDecorator<string>('user', 'Bob Dylan')
})
```
## Code Completion In Vanilla JavaScript
Vanilla JavaScript can use the published types to provide code completion (e.g.
@@ -831,7 +966,7 @@ Constraints: `string | Buffer`
#### Fastify
##### fastify<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(opts?: [FastifyServerOptions][FastifyServerOptions]): [FastifyInstance][FastifyInstance]
##### fastify< [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(opts?: [FastifyServerOptions][FastifyServerOptions]): [FastifyInstance][FastifyInstance]
[src](https://github.com/fastify/fastify/blob/main/fastify.d.ts#L19)
The main Fastify API method. By default creates an HTTP server. Utilizing
@@ -858,11 +993,11 @@ a more detailed http server walkthrough.
1. Create the following imports from `@types/node` and `fastify`
```typescript
import fs from 'fs'
import path from 'path'
import fs from 'node:fs'
import path from 'node:path'
import fastify from 'fastify'
```
2. Perform the following steps before setting up a Fastify HTTPS server
2. Perform the following steps before setting up a Fastify HTTPS server
to create the `key.pem` and `cert.pem` files:
```sh
openssl genrsa -out key.pem
@@ -920,7 +1055,7 @@ specified at server instantiation, the custom type becomes available on all
further instances of the custom type.
```typescript
import fastify from 'fastify'
import http from 'http'
import http from 'node:http'
interface customRequest extends http.IncomingMessage {
mySpecialProp: string
@@ -986,7 +1121,7 @@ Type alias for `http.Server`
---
##### fastify.FastifyServerOptions<[RawServer][RawServerGeneric], [Logger][LoggerGeneric]>
##### fastify.FastifyServerOptions< [RawServer][RawServerGeneric], [Logger][LoggerGeneric]>
[src](https://github.com/fastify/fastify/blob/main/fastify.d.ts#L29)
@@ -997,7 +1132,7 @@ generic parameters are passed down through that method.
See the main [fastify][Fastify] method type definition section for examples on
instantiating a Fastify server with TypeScript.
##### fastify.FastifyInstance<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RequestGeneric][FastifyRequestGenericInterface], [Logger][LoggerGeneric]>
##### fastify.FastifyInstance< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RequestGeneric][FastifyRequestGenericInterface], [Logger][LoggerGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/instance.d.ts#L16)
@@ -1020,7 +1155,7 @@ details on this interface.
#### Request
##### fastify.FastifyRequest<[RequestGeneric][FastifyRequestGenericInterface], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
##### fastify.FastifyRequest< [RequestGeneric][FastifyRequestGenericInterface], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/request.d.ts#L15)
This interface contains properties of Fastify request object. The properties
@@ -1108,8 +1243,8 @@ returns `http.IncomingMessage`, otherwise, it returns
`http2.Http2ServerRequest`.
```typescript
import http from 'http'
import http2 from 'http2'
import http from 'node:http'
import http2 from 'node:http2'
import { RawRequestDefaultExpression } from 'fastify'
RawRequestDefaultExpression<http.Server> // -> http.IncomingMessage
@@ -1120,7 +1255,7 @@ RawRequestDefaultExpression<http2.Http2Server> // -> http2.Http2ServerRequest
#### Reply
##### fastify.FastifyReply<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
##### fastify.FastifyReply<[RequestGeneric][FastifyRequestGenericInterface], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [ContextConfig][ContextConfigGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/reply.d.ts#L32)
This interface contains the custom properties that Fastify adds to the standard
@@ -1156,7 +1291,7 @@ declare module 'fastify' {
}
```
##### fastify.RawReplyDefaultExpression<[RawServer][RawServerGeneric]>
##### fastify.RawReplyDefaultExpression< [RawServer][RawServerGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/utils.d.ts#L27)
Dependent on `@types/node` modules `http`, `https`, `http2`
@@ -1168,8 +1303,8 @@ returns `http.ServerResponse`, otherwise, it returns
`http2.Http2ServerResponse`.
```typescript
import http from 'http'
import http2 from 'http2'
import http from 'node:http'
import http2 from 'node:http2'
import { RawReplyDefaultExpression } from 'fastify'
RawReplyDefaultExpression<http.Server> // -> http.ServerResponse
@@ -1188,19 +1323,19 @@ When creating plugins for Fastify, it is recommended to use the `fastify-plugin`
module. Additionally, there is a guide to creating plugins with TypeScript and
Fastify available in the Learn by Example, [Plugins](#plugins) section.
##### fastify.FastifyPluginCallback<[Options][FastifyPluginOptions]>
##### fastify.FastifyPluginCallback< [Options][FastifyPluginOptions]>
[src](https://github.com/fastify/fastify/blob/main/types/plugin.d.ts#L9)
Interface method definition used within the
[`fastify.register()`][FastifyRegister] method.
##### fastify.FastifyPluginAsync<[Options][FastifyPluginOptions]>
##### fastify.FastifyPluginAsync< [Options][FastifyPluginOptions]>
[src](https://github.com/fastify/fastify/blob/main/types/plugin.d.ts#L20)
Interface method definition used within the
[`fastify.register()`][FastifyRegister] method.
##### fastify.FastifyPlugin<[Options][FastifyPluginOptions]>
##### fastify.FastifyPlugin< [Options][FastifyPluginOptions]>
[src](https://github.com/fastify/fastify/blob/main/types/plugin.d.ts#L29)
Interface method definition used within the
@@ -1269,7 +1404,7 @@ a function that returns the previously described intersection.
Check out the [Specifying Logger Types](#example-5-specifying-logger-types)
example for more details on specifying a custom logger.
##### fastify.FastifyLoggerOptions<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric]>
##### fastify.FastifyLoggerOptions< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/logger.d.ts#L17)
@@ -1332,7 +1467,7 @@ One of the core principles in Fastify is its routing capabilities. Most of the
types defined in this section are used under-the-hood by the Fastify instance
`.route` and `.get/.post/.etc` methods.
##### fastify.RouteHandlerMethod<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
##### fastify.RouteHandlerMethod< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#L105)
@@ -1342,7 +1477,7 @@ The generics parameters are passed through to these arguments. The method
returns either `void` or `Promise<any>` for synchronous and asynchronous
handlers respectively.
##### fastify.RouteOptions<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
##### fastify.RouteOptions< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#L78)
@@ -1354,14 +1489,14 @@ required properties:
3. `handler` the route handler method, see [RouteHandlerMethod][] for more
details
##### fastify.RouteShorthandMethod<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric]>
##### fastify.RouteShorthandMethod< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#12)
An overloaded function interface for three kinds of shorthand route methods to
be used in conjunction with the `.get/.post/.etc` methods.
##### fastify.RouteShorthandOptions<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
##### fastify.RouteShorthandOptions< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#55)
@@ -1369,7 +1504,7 @@ An interface that covers all of the base options for a route. Each property on
this interface is optional, and it serves as the base for the RouteOptions and
RouteShorthandOptionsWithHandler interfaces.
##### fastify.RouteShorthandOptionsWithHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
##### fastify.RouteShorthandOptionsWithHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/route.d.ts#93)
@@ -1384,21 +1519,21 @@ interface `handler` which is of type RouteHandlerMethod
A generic type that is either a `string` or `Buffer`
##### fastify.FastifyBodyParser<[RawBody][RawBodyGeneric], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
##### fastify.FastifyBodyParser< [RawBody][RawBodyGeneric], [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/content-type-parser.d.ts#L7)
A function type definition for specifying a body parser method. Use the
`RawBody` generic to specify the type of the body being parsed.
##### fastify.FastifyContentTypeParser<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
##### fastify.FastifyContentTypeParser< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/content-type-parser.d.ts#L17)
A function type definition for specifying a body parser method. Content is typed
via the `RawRequest` generic.
##### fastify.AddContentTypeParser<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
##### fastify.AddContentTypeParser< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric]>
[src](https://github.com/fastify/fastify/blob/main/types/content-type-parser.d.ts#L46)
@@ -1440,7 +1575,7 @@ This interface is passed to instance of FastifyError.
#### Hooks
##### fastify.onRequestHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.onRequestHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L17)
@@ -1450,7 +1585,7 @@ no previous hook, the next hook will be `preParsing`.
Notice: in the `onRequest` hook, request.body will always be null, because the
body parsing happens before the `preHandler` hook.
##### fastify.preParsingHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.preParsingHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L35)
@@ -1465,21 +1600,21 @@ 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.
##### fastify.preValidationHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.preValidationHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L53)
`preValidation` is the third hook to be executed in the request lifecycle. The
previous hook was `preParsing`, the next hook will be `preHandler`.
##### fastify.preHandlerHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.preHandlerHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L70)
`preHandler` is the fourth hook to be executed in the request lifecycle. The
previous hook was `preValidation`, the next hook will be `preSerialization`.
##### fastify.preSerializationHookHandler<PreSerializationPayload, [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], payload: PreSerializationPayload, done: (err: [FastifyError][FastifyError] | null, res?: unknown) => void): Promise\<unknown\> | void
##### fastify.preSerializationHookHandler< PreSerializationPayload, [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], payload: PreSerializationPayload, done: (err: [FastifyError][FastifyError] | null, res?: unknown) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L94)
@@ -1489,7 +1624,7 @@ The previous hook was `preHandler`, the next hook will be `onSend`.
Note: the hook is NOT called if the payload is a string, a Buffer, a stream or
null.
##### fastify.onSendHookHandler<OnSendPayload, [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], payload: OnSendPayload, done: (err: [FastifyError][FastifyError] | null, res?: unknown) => void): Promise\<unknown\> | void
##### fastify.onSendHookHandler< OnSendPayload, [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], payload: OnSendPayload, done: (err: [FastifyError][FastifyError] | null, res?: unknown) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L114)
@@ -1500,7 +1635,7 @@ next hook will be `onResponse`.
Note: If you change the payload, you may only change it to a string, a Buffer, a
stream, or null.
##### fastify.onResponseHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.onResponseHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L134)
@@ -1511,7 +1646,7 @@ 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.
##### fastify.onErrorHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], error: [FastifyError][FastifyError], done: () => void): Promise\<unknown\> | void
##### fastify.onErrorHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(request: [FastifyRequest][FastifyRequest], reply: [FastifyReply][FastifyReply], error: [FastifyError][FastifyError], done: () => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L154)
@@ -1521,14 +1656,12 @@ 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 only after the customErrorHandler has been executed,
and only if the customErrorHandler sends an error back to the user (Note that
the default customErrorHandler always sends the error back to the user).
This hook will be executed before the customErrorHandler.
Notice: unlike the other hooks, pass an error to the done function is not
supported.
##### fastify.onRouteHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(opts: [RouteOptions][RouteOptions] & { path: string; prefix: string }): Promise\<unknown\> | void
##### fastify.onRouteHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [RequestGeneric][FastifyRequestGenericInterface], [ContextConfig][ContextConfigGeneric]>(opts: [RouteOptions][RouteOptions] & \{ path: string; prefix: string }): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L174)
@@ -1536,7 +1669,7 @@ Triggered when a new route is registered. Listeners are passed a routeOptions
object as the sole parameter. The interface is synchronous, and, as such, the
listener does not get passed a callback
##### fastify.onRegisterHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(instance: [FastifyInstance][FastifyInstance], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.onRegisterHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(instance: [FastifyInstance][FastifyInstance], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L191)
@@ -1548,7 +1681,7 @@ plugin context is formed, and you want to operate in that specific context.
Note: This hook will not be called if a plugin is wrapped inside fastify-plugin.
##### fastify.onCloseHookHandler<[RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(instance: [FastifyInstance][FastifyInstance], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
##### fastify.onCloseHookHandler< [RawServer][RawServerGeneric], [RawRequest][RawRequestGeneric], [RawReply][RawReplyGeneric], [Logger][LoggerGeneric]>(instance: [FastifyInstance][FastifyInstance], done: (err?: [FastifyError][FastifyError]) => void): Promise\<unknown\> | void
[src](https://github.com/fastify/fastify/blob/main/types/hooks.d.ts#L206)

View File

@@ -1,67 +1,63 @@
<h1 align="center">Fastify</h1>
## Validation and Serialization
Fastify uses a schema-based approach, and 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 into a
highly performant function.
Fastify uses a schema-based approach. We recommend using
[JSON Schema](https://json-schema.org/) to validate routes and serialize outputs.
Fastify compiles the schema into a highly performant function.
Validation will only be attempted if the content type is `application-json`, as
described in the documentation for the [content type
parser](./ContentTypeParser.md).
Validation is only attempted if the content type is `application/json`.
All the examples in this section are using the [JSON Schema Draft
7](https://json-schema.org/specification-links.html#draft-7) specification.
All examples use the
[JSON Schema Draft 7](https://json-schema.org/specification-links.html#draft-7)
specification.
> ## ⚠ Security Notice
> Treat the schema definition as application code. Validation and serialization
> features dynamically evaluate code with `new Function()`, which is not safe to
> use with user-provided schemas. See [Ajv](https://npm.im/ajv) and
> [fast-json-stringify](https://npm.im/fast-json-stringify) for more details.
> ⚠ Warning:
> Treat schema definitions as application code. Validation and serialization
> features use `new Function()`, which is unsafe with user-provided schemas. See
> [Ajv](https://npm.im/ajv) and
> [fast-json-stringify](https://npm.im/fast-json-stringify) for details.
>
> Regardless the [`$async` Ajv
> feature](https://ajv.js.org/guide/async-validation.html) is supported
> by Fastify, it should not be used as
> part of the first validation strategy. This option is used to access Databases
> and reading them during the validation process may lead to Denial of Service
> Attacks to your application. If you need to run `async` tasks, use [Fastify's
> hooks](./Hooks.md) instead after validation completes, such as `preHandler`.
> Whilst Fastify supports the
> [`$async` Ajv feature](https://ajv.js.org/guide/async-validation.html),
> it should not be used for initial validation. Accessing databases during
> validation may lead to Denial of Service attacks. Use
> [Fastify's hooks](./Hooks.md) like `preHandler` for `async` tasks after validation.
>
> When using custom validators with async `preValidation` hooks,
> validators **must return** `{error}` objects instead of throwing errors.
> Throwing errors from custom validators will cause unhandled promise rejections
> that crash the application when combined with async hooks. See the
> [custom validator examples](#using-other-validation-libraries) below for the
> correct pattern.
### Core concepts
The validation and the serialization tasks are processed by two different, and
customizable, actors:
- [Ajv v8](https://www.npmjs.com/package/ajv) for the validation of a request
Validation and serialization are handled by two customizable dependencies:
- [Ajv v8](https://www.npmjs.com/package/ajv) for request validation
- [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify) for
the serialization of a response's body
response body serialization
These two separate entities share only the JSON schemas added to Fastify's
instance through `.addSchema(schema)`.
These dependencies share only the JSON schemas added to Fastify's instance via
`.addSchema(schema)`.
#### Adding a shared schema
<a id="shared-schema"></a>
Thanks to the `addSchema` API, you can add multiple schemas to the Fastify
instance and then reuse them in multiple parts of your application. As usual,
this API is encapsulated.
The `addSchema` API allows adding multiple schemas to the Fastify instance for
reuse throughout the application. This API is encapsulated.
The shared schemas can be reused through the JSON Schema
Shared schemas can be reused with the JSON Schema
[**`$ref`**](https://tools.ietf.org/html/draft-handrews-json-schema-01#section-8)
keyword. Here is an overview of _how_ references work:
keyword. Here is an overview of how references work:
+ `myField: { $ref: '#foo'}` will search for field with `$id: '#foo'` inside the
+ `myField: { $ref: '#foo' }` searches for `$id: '#foo'` in the current schema
+ `myField: { $ref: '#/definitions/foo' }` searches for `definitions.foo` in the
current schema
+ `myField: { $ref: '#/definitions/foo'}` will search for field
`definitions.foo` inside the current schema
+ `myField: { $ref: 'http://url.com/sh.json#'}` will search for a shared schema
added with `$id: 'http://url.com/sh.json'`
+ `myField: { $ref: 'http://url.com/sh.json#/definitions/foo'}` will search for
a shared schema added with `$id: 'http://url.com/sh.json'` and will use the
field `definitions.foo`
+ `myField: { $ref: 'http://url.com/sh.json#foo'}` will search for a shared
schema added with `$id: 'http://url.com/sh.json'` and it will look inside of
it for object with `$id: '#foo'`
+ `myField: { $ref: 'http://url.com/sh.json#' }` searches for a shared schema
with `$id: 'http://url.com/sh.json'`
+ `myField: { $ref: 'http://url.com/sh.json#/definitions/foo' }` searches for a
shared schema with `$id: 'http://url.com/sh.json'` and uses `definitions.foo`
+ `myField: { $ref: 'http://url.com/sh.json#foo' }` searches for a shared schema
with `$id: 'http://url.com/sh.json'` and looks for `$id: '#foo'` within it
**Simple usage:**
@@ -108,9 +104,9 @@ fastify.post('/', {
#### Retrieving the shared schemas
<a id="get-shared-schema"></a>
If the validator and the serializer are customized, the `.addSchema` method will
not be useful since the actors are no longer controlled by Fastify. To access
the schemas added to the Fastify instance, you can simply use `.getSchemas()`:
If the validator and serializer are customized, `.addSchema` is not useful since
Fastify no longer controls them. To access schemas added to the Fastify instance,
use `.getSchemas()`:
```js
fastify.addSchema({
@@ -125,8 +121,8 @@ const mySchemas = fastify.getSchemas()
const mySchema = fastify.getSchema('schemaId')
```
As usual, the function `getSchemas` is encapsulated and returns the shared
schemas available in the selected scope:
The `getSchemas` function is encapsulated and returns shared schemas available
in the selected scope:
```js
fastify.addSchema({ $id: 'one', my: 'hello' })
@@ -150,25 +146,22 @@ fastify.register((instance, opts, done) => {
### Validation
The route validation internally relies upon [Ajv
v8](https://www.npmjs.com/package/ajv) which is a high-performance JSON Schema
validator. Validating the input is very easy: just add the fields that you need
inside the route schema, and you are done!
Route validation relies on [Ajv v8](https://www.npmjs.com/package/ajv), a
high-performance JSON Schema validator. To validate input, add the required
fields to the route schema.
The supported validations are:
- `body`: validates the body of the request if it is a POST, PUT, or PATCH
method.
Supported validations include:
- `body`: validates the request body for POST, PUT, or PATCH methods.
- `querystring` or `query`: validates the query string.
- `params`: validates the route params.
- `params`: validates the route parameters.
- `headers`: validates the request headers.
All the validations can be a complete JSON Schema object (with a `type` property
of `'object'` and a `'properties'` object containing parameters) or a simpler
variation in which the `type` and `properties` attributes are forgone and the
parameters are listed at the top level (see the example below).
Validations can be a complete JSON Schema object with a `type` of `'object'` and
a `'properties'` object containing parameters, or a simpler variation listing
parameters at the top level.
> If you need to use the latest version of Ajv (v8) you should read how to do
> it in the [`schemaController`](./Server.md#schema-controller) section.
> For using the latest Ajv (v8), refer to the
> [`schemaController`](./Server.md#schema-controller) section.
Example:
```js
@@ -257,9 +250,9 @@ fastify.post('/the/url', {
}, handler)
```
*Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) the values
to the types specified in your schema `type` keywords, both to pass the
validation and to use the correctly typed data afterwards.*
Note that Ajv will try to [coerce](https://ajv.js.org/coercion.html) values to
the types specified in the schema `type` keywords, both to pass validation and
to use the correctly typed data afterwards.
The Ajv default configuration in Fastify supports coercing array parameters in
`querystring`. Example:
@@ -294,11 +287,11 @@ curl -X GET "http://localhost:3000/?ids=1
{"params":{"ids":["1"]}}
```
You can also specify a custom schema validator for each parameter type (body,
A custom schema validator can be specified for each parameter type (body,
querystring, params, headers).
For example, the following code disable type coercion only for the `body`
parameters, changing the ajv default options:
For example, the following code disables type coercion only for the `body`
parameters, changing the Ajv default options:
```js
const schemaCompilers = {
@@ -336,16 +329,15 @@ server.setValidatorCompiler(req => {
})
```
For further information see [here](https://ajv.js.org/coercion.html)
For more information, see [Ajv Coercion](https://ajv.js.org/coercion.html).
#### Ajv Plugins
<a id="ajv-plugins"></a>
You can provide a list of plugins you want to use with the default `ajv`
instance. Note that the plugin must be **compatible with the Ajv version shipped
within Fastify**.
A list of plugins can be provided for use with the default `ajv` instance.
Ensure the plugin is **compatible with the Ajv version shipped within Fastify**.
> Refer to [`ajv options`](./Server.md#ajv) to check plugins format
> Refer to [`ajv options`](./Server.md#ajv) to check plugins format.
```js
const fastify = require('fastify')({
@@ -406,11 +398,10 @@ fastify.post('/foo', {
#### Validator Compiler
<a id="schema-validator"></a>
The `validatorCompiler` is a function that returns a function that validates the
body, URL parameters, headers, and query string. The default
`validatorCompiler` returns a function that implements the
[ajv](https://ajv.js.org/) validation interface. Fastify uses it internally to
speed the validation up.
The `validatorCompiler` is a function that returns a function to validate the
body, URL parameters, headers, and query string. The default `validatorCompiler`
returns a function that implements the [ajv](https://ajv.js.org/) validation
interface. Fastify uses it internally to speed up validation.
Fastify's [baseline ajv
configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:
@@ -428,11 +419,11 @@ configuration](https://github.com/fastify/ajv-compiler#ajv-configuration) is:
}
```
This baseline configuration can be modified by providing
[`ajv.customOptions`](./Server.md#factory-ajv) to your Fastify factory.
Modify the baseline configuration by providing
[`ajv.customOptions`](./Server.md#factory-ajv) to the Fastify factory.
If you want to change or set additional config options, you will need to create
your own instance and override the existing one like:
To change or set additional config options, create a custom instance and
override the existing one:
```js
const fastify = require('fastify')()
@@ -448,29 +439,39 @@ fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
return ajv.compile(schema)
})
```
_**Note:** If you use a custom instance of any validator (even Ajv), you have to
add schemas to the validator instead of Fastify, since Fastify's default
validator is no longer used, and Fastify's `addSchema` method has no idea what
validator you are using._
> Note: When using a custom validator instance, add schemas to the validator
> instead of Fastify. Fastify's `addSchema` method will not recognize the custom
> validator.
##### Using other validation libraries
<a id="using-other-validation-libraries"></a>
The `setValidatorCompiler` function makes it easy to substitute `ajv` with
almost any JavaScript validation library ([joi](https://github.com/hapijs/joi/),
[yup](https://github.com/jquense/yup/), ...) or a custom one:
The `setValidatorCompiler` function allows substituting `ajv` with other
JavaScript validation libraries like [joi](https://github.com/hapijs/joi/) or
[yup](https://github.com/jquense/yup/), or a custom one:
```js
const Joi = require('joi')
fastify.setValidatorCompiler(({ schema }) => {
return (data) => {
try {
const { error, value } = schema.validate(data)
if (error) {
return { error } // Return the error, do not throw it
}
return { value }
} catch (e) {
return { error: e } // Catch any unexpected errors too
}
}
})
fastify.post('/the/url', {
schema: {
body: Joi.object().keys({
hello: Joi.string().required()
}).required()
},
validatorCompiler: ({ schema, method, url, httpPart }) => {
return data => schema.validate(data)
}
}, handler)
```
@@ -509,10 +510,44 @@ fastify.post('/the/url', {
}, handler)
```
##### Custom Validator Best Practices
When implementing custom validators, follow these patterns to ensure compatibility
with all Fastify features:
** Always return objects, never throw:**
```js
return { value: validatedData } // On success
return { error: validationError } // On failure
```
** Use try-catch for safety:**
```js
fastify.setValidatorCompiler(({ schema }) => {
return (data) => {
try {
// Validation logic here
const result = schema.validate(data)
if (result.error) {
return { error: result.error }
}
return { value: result.value }
} catch (e) {
// Catch any unexpected errors
return { error: e }
}
}
})
```
This pattern ensures validators work correctly with both sync and async
`preValidation` hooks, preventing unhandled promise rejections that can crash
an application.
##### .statusCode property
All validation errors will be added a `.statusCode` property set to `400`. This guarantees
that the default error handler will set the status code of the response to `400`.
All validation errors have a `.statusCode` property set to `400`, ensuring the
default error handler sets the response status code to `400`.
```js
fastify.setErrorHandler(function (error, request, reply) {
@@ -525,30 +560,27 @@ fastify.setErrorHandler(function (error, request, reply) {
Fastify's validation error messages are tightly coupled to the default
validation engine: errors returned from `ajv` are eventually run through the
`schemaErrorFormatter` function which is responsible for building human-friendly
error messages. However, the `schemaErrorFormatter` function is written with
`ajv` in mind. As a result, you may run into odd or incomplete error messages
when using other validation libraries.
`schemaErrorFormatter` function which builds human-friendly error messages.
However, the `schemaErrorFormatter` function is written with `ajv` in mind.
This may result in odd or incomplete error messages when using other validation
libraries.
To circumvent this issue, you have 2 main options :
To circumvent this issue, there are two main options:
1. make sure your validation function (returned by your custom `schemaCompiler`)
returns errors in the same structure and format as `ajv` (although this could
prove to be difficult and tricky due to differences between validation
engines)
2. or use a custom `errorHandler` to intercept and format your 'custom'
validation errors
1. Ensure the validation function (returned by the custom `schemaCompiler`)
returns errors in the same structure and format as `ajv`.
2. Use a custom `errorHandler` to intercept and format custom validation errors.
To help you in writing a custom `errorHandler`, Fastify adds 2 properties to all
validation errors:
Fastify adds two properties to all validation errors to help write a custom
`errorHandler`:
* `validation`: the content of the `error` property of the object returned by
the validation function (returned by your custom `schemaCompiler`)
* `validationContext`: the 'context' (body, params, query, headers) where the
the validation function (returned by the custom `schemaCompiler`)
* `validationContext`: the context (body, params, query, headers) where the
validation error occurred
A very contrived example of such a custom `errorHandler` handling validation
errors is shown below:
A contrived example of such a custom `errorHandler` handling validation errors
is shown below:
```js
const errorHandler = (error, request, reply) => {
@@ -560,9 +592,9 @@ const errorHandler = (error, request, reply) => {
// check if we have a validation error
if (validation) {
response = {
// validationContext will be 'body' or 'params' or 'headers' or 'query'
// validationContext will be 'body', 'params', 'headers', or 'query'
message: `A validation error occurred when validating the ${validationContext}...`,
// this is the result of your validation library...
// this is the result of the validation library...
errors: validation
}
} else {
@@ -581,12 +613,10 @@ const errorHandler = (error, request, reply) => {
### Serialization
<a id="serialization"></a>
Usually, you will send your data to the clients as JSON, and Fastify has a
powerful tool to help you,
[fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify), which
is used if you have provided an output schema in the route options. We encourage
you to use an output schema, as it can drastically increase throughput and help
prevent accidental disclosure of sensitive information.
Fastify uses [fast-json-stringify](https://www.npmjs.com/package/fast-json-stringify)
to send data as JSON if an output schema is provided in the route options. Using
an output schema can drastically increase throughput and help prevent accidental
disclosure of sensitive information.
Example:
```js
@@ -605,9 +635,8 @@ const schema = {
fastify.post('/the/url', { schema }, handler)
```
As you can see, the response schema is based on the status code. If you want to
use the same schema for multiple status codes, you can use `'2xx'` or `default`,
for example:
The response schema is based on the status code. To use the same schema for
multiple status codes, use `'2xx'` or `default`, for example:
```js
const schema = {
response: {
@@ -636,41 +665,51 @@ const schema = {
fastify.post('/the/url', { schema }, handler)
```
You can even have a specific response schema for different content types.
A specific response schema can be defined for different content types.
For example:
```js
const schema = {
response: {
200: {
description: 'Response schema that support different content types'
content: {
'application/json': {
schema: {
name: { type: 'string' },
image: { type: 'string' },
address: { type: 'string' }
}
},
'application/vnd.v1+json': {
schema: {
type: 'array',
items: { $ref: 'test' }
}
}
response: {
200: {
description: 'Response schema that support different content types'
content: {
'application/json': {
schema: {
name: { type: 'string' },
image: { type: 'string' },
address: { type: 'string' }
}
},
'3xx': {
content: {
'application/vnd.v2+json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'string' }
}
}
'application/vnd.v1+json': {
schema: {
type: 'array',
items: { $ref: 'test' }
}
}
}
},
'3xx': {
content: {
'application/vnd.v2+json': {
schema: {
fullName: { type: 'string' },
phone: { type: 'string' }
}
}
}
},
default: {
content: {
// */* is match-all content-type
'*/*': {
schema: {
desc: { type: 'string' }
}
}
}
}
}
}
fastify.post('/url', { schema }, handler)
```
@@ -678,10 +717,9 @@ fastify.post('/url', { schema }, handler)
#### Serializer Compiler
<a id="schema-serializer"></a>
The `serializerCompiler` is a function that returns a function that must return
a string from an input object. When you define a response JSON Schema, you can
change the default serialization method by providing a function to serialize
every route where you do.
The `serializerCompiler` returns a function that must return a string from an
input object. When defining a response JSON Schema, change the default
serialization method by providing a function to serialize each route.
```js
fastify.setSerializerCompiler(({ schema, method, url, httpStatus, contentType }) => {
@@ -695,21 +733,24 @@ fastify.get('/user', {
schema: {
response: {
'2xx': {
id: { type: 'number' },
name: { type: 'string' }
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
}
}
}
}
})
```
*If you need a custom serializer in a very specific part of your code, you can
set one with [`reply.serializer(...)`](./Reply.md#serializerfunc).*
*To set a custom serializer in a specific part of the code, use
[`reply.serializer(...)`](./Reply.md#serializerfunc).*
### Error Handling
When schema validation fails for a request, Fastify will automatically return a
status 400 response including the result from the validator in the payload. As
an example, if you have the following schema for your route
status 400 response including the result from the validator in the payload. For
example, if the following schema is used for a route:
```js
const schema = {
@@ -723,8 +764,8 @@ const schema = {
}
```
and fail to satisfy it, the route will immediately return a response with the
following payload
If the request fails to satisfy the schema, the route will return a response
with the following payload:
```js
{
@@ -734,10 +775,15 @@ following payload
}
```
If you want to handle errors inside the route, you can specify the
`attachValidation` option for your route. If there is a _validation error_, the
`validationError` property of the request will contain the `Error` object with
the raw `validation` result as shown below
> ⚠ Security Consideration: By default, validation error details from the schema
> are included in the response payload. If your organization requires sanitizing
> or customizing these error messages (e.g., to avoid exposing internal schema
> details), configure a custom error handler using
> [`setErrorHandler()`](./Server.md#seterrorhandler).
To handle errors inside the route, specify the `attachValidation` option. If
there is a validation error, the `validationError` property of the request will
contain the `Error` object with the raw validation result as shown below:
```js
const fastify = Fastify()
@@ -752,13 +798,13 @@ fastify.post('/', { schema, attachValidation: true }, function (req, reply) {
#### `schemaErrorFormatter`
If you want to format errors yourself, you can provide a sync function that must
return an error as the `schemaErrorFormatter` option to Fastify when
instantiating. The context function will be the Fastify server instance.
To format errors, provide a sync function that returns an error as the
`schemaErrorFormatter` option when instantiating Fastify. The context function
will be the Fastify server instance.
`errors` is an array of Fastify schema errors `FastifySchemaValidationError`.
`dataVar` is the currently validated part of the schema. (params | body |
querystring | headers).
`dataVar` is the currently validated part of the schema (params, body,
querystring, headers).
```js
const fastify = Fastify({
@@ -776,8 +822,8 @@ fastify.setSchemaErrorFormatter(function (errors, dataVar) {
})
```
You can also use [setErrorHandler](./Server.md#seterrorhandler) to define a
custom response for validation errors such as
Use [setErrorHandler](./Server.md#seterrorhandler) to define a custom response
for validation errors such as:
```js
fastify.setErrorHandler(function (error, request, reply) {
@@ -787,25 +833,25 @@ fastify.setErrorHandler(function (error, request, reply) {
})
```
If you want a custom error response in the schema without headaches, and
quickly, take a look at
For custom error responses in the schema, see
[`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Check out the
[example](https://github.com/fastify/example/blob/HEAD/validation-messages/custom-errors-messages.js)
usage.
> Make sure to install version 1.0.1 of `ajv-errors`, because later versions of
> it are not compatible with AJV v6 (the version shipped by Fastify v3).
> Install version 1.0.1 of `ajv-errors`, as later versions are not compatible
> with AJV v6 (the version shipped by Fastify v3).
Below is an example showing how to add **custom error messages for each
property** of a schema by supplying custom AJV options. Inline comments in the
schema below describe how to configure it to show a different error message for
each case:
schema describe how to configure it to show a different error message for each
case:
```js
const fastify = Fastify({
ajv: {
customOptions: {
jsonPointers: true,
// Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/
// Warning: Enabling this option may lead to this security issue https://www.cvedetails.com/cve/CVE-2020-8192/
allErrors: true
},
plugins: [
@@ -849,8 +895,8 @@ fastify.post('/', { schema, }, (request, reply) => {
})
```
If you want to return localized error messages, take a look at
[ajv-i18n](https://github.com/epoberezkin/ajv-i18n)
To return localized error messages, see
[ajv-i18n](https://github.com/epoberezkin/ajv-i18n).
```js
const localize = require('ajv-i18n')
@@ -884,8 +930,8 @@ fastify.setErrorHandler(function (error, request, reply) {
### JSON Schema support
JSON Schema provides utilities to optimize your schemas that, in conjunction
with Fastify's shared schema, let you reuse all your schemas easily.
JSON Schema provides utilities to optimize schemas. Combined with Fastify's
shared schema, all schemas can be easily reused.
| Use Case | Validator | Serializer |
|-----------------------------------|-----------|------------|
@@ -992,11 +1038,11 @@ const refToSharedSchemaDefinitions = {
- [JSON Schema](https://json-schema.org/)
- [Understanding JSON
Schema](https://spacetelescope.github.io/understanding-json-schema/)
Schema](https://json-schema.org/understanding-json-schema/about)
- [fast-json-stringify
documentation](https://github.com/fastify/fast-json-stringify)
- [Ajv documentation](https://github.com/epoberezkin/ajv/blob/master/README.md)
- [Ajv i18n](https://github.com/epoberezkin/ajv-i18n)
- [Ajv custom errors](https://github.com/epoberezkin/ajv-errors)
- Custom error handling with core methods with error file dumping
[example](https://github.com/fastify/example/tree/master/validation-messages)
[example](https://github.com/fastify/example/tree/main/validation-messages)

View File

@@ -8,54 +8,33 @@
- [FSTWRN001](#FSTWRN001)
- [FSTWRN002](#FSTWRN002)
- [Fastify Deprecation Codes](#fastify-deprecation-codes)
- [FSTDEP005](#FSTDEP005)
- [FSTDEP006](#FSTDEP006)
- [FSTDEP007](#FSTDEP007)
- [FSTDEP008](#FSTDEP008)
- [FSTDEP009](#FSTDEP009)
- [FSTDEP010](#FSTDEP010)
- [FSTDEP011](#FSTDEP011)
- [FSTDEP012](#FSTDEP012)
- [FSTDEP013](#FSTDEP013)
- [FSTDEP014](#FSTDEP014)
- [FSTDEP015](#FSTDEP015)
- [FSTDEP016](#FSTDEP016)
- [FSTDEP017](#FSTDEP017)
- [FSTDEP018](#FSTDEP018)
- [FSTDEP019](#FSTDEP019)
- [FSTDEP020](#FSTDEP020)
- [FSTDEP021](#FSTDEP021)
- [FSTDEP022](#FSTDEP022)
## Warnings
### Warnings In Fastify
Fastify utilizes Node.js's [warning event](https://nodejs.org/api/process.html#event-warning)
API to notify users of deprecated features and known coding mistakes. Fastify's
warnings are recognizable by the `FSTWRN` and `FSTDEP` prefixes on warning
code. When encountering such a warning, it is highly recommended that the
cause of the warning be determined through use of the
[`--trace-warnings`](https://nodejs.org/api/cli.html#--trace-warnings) and
[`--trace-deprecation`](https://nodejs.org/api/cli.html#--trace-deprecation)
flags. These will produce stack traces pointing out where the issue occurs
in the application's code. Issues opened about warnings without including
this information may be closed due to lack of information.
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.
In addition to tracing, warnings can also be disabled. It is not recommended to
disable warnings as a matter of course, but if necessary, they can be disabled
by using any of the following methods:
Warnings can also be disabled, though it is not recommended. If necessary, use
one of the following methods:
- setting the `NODE_NO_WARNINGS` environment variable to `1`
- passing the `--no-warnings` flag to the node process
- setting 'no-warnings' in the `NODE_OPTIONS` environment variable
- 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 how to disable warnings, see [node's documentation](https://nodejs.org/api/cli.html).
For more information on disabling warnings, see [Node's documentation](https://nodejs.org/api/cli.html).
However, disabling warnings is not recommended as it may cause
potential problems when upgrading Fastify versions.
Only experienced users should consider disabling warnings.
Disabling warnings may cause issues when upgrading Fastify versions. Only
experienced users should consider disabling warnings.
### Fastify Warning Codes
@@ -67,7 +46,7 @@ Only experienced users should consider disabling warnings.
### Fastify Deprecation Codes
Deprecation codes are further supported by the Node.js CLI options:
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)
@@ -76,21 +55,4 @@ Deprecation codes are further supported by the Node.js CLI options:
| Code | Description | How to solve | Discussion |
| ---- | ----------- | ------------ | ---------- |
| <a id="FSTDEP005">FSTDEP005</a> | You are accessing the deprecated `request.connection` property. | Use `request.socket`. | [#2594](https://github.com/fastify/fastify/pull/2594) |
| <a id="FSTDEP006">FSTDEP006</a> | You are decorating Request/Reply with a reference type. This reference is shared amongst all requests. | Do not use Arrays/Objects as values when decorating Request/Reply. | [#2688](https://github.com/fastify/fastify/pull/2688) |
| <a id="FSTDEP007">FSTDEP007</a> | You are trying to set a HEAD route using `exposeHeadRoute` route flag when a sibling route is already set. | Remove `exposeHeadRoutes` or explicitly set `exposeHeadRoutes` to `false` | [#2700](https://github.com/fastify/fastify/pull/2700) |
| <a id="FSTDEP008">FSTDEP008</a> | You are using route constraints via the route `{version: "..."}` option. | Use `{constraints: {version: "..."}}` option. | [#2682](https://github.com/fastify/fastify/pull/2682) |
| <a id="FSTDEP009">FSTDEP009</a> | 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) |
| <a id="FSTDEP010">FSTDEP010</a> | Modifying the `reply.sent` property is deprecated. | Use the `reply.hijack()` method. | [#3140](https://github.com/fastify/fastify/pull/3140) |
| <a id="FSTDEP011">FSTDEP011</a> | Variadic listen method is deprecated. | Use `.listen(optionsObject)`. | [#3712](https://github.com/fastify/fastify/pull/3712) |
| <a id="FSTDEP012">FSTDEP012</a> | 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) |
| <a id="FSTDEP013">FSTDEP013</a> | Direct return of "trailers" function is deprecated. | Use "callback" or "async-await" for return value. | [#4380](https://github.com/fastify/fastify/pull/4380) |
| <a id="FSTDEP014">FSTDEP014</a> | You are trying to set/access the default route. This property is deprecated. | Use `setNotFoundHandler` if you want to custom a 404 handler or the wildcard (`*`) to match all routes. | [#4480](https://github.com/fastify/fastify/pull/4480) |
| <a id="FSTDEP015">FSTDEP015</a> | You are accessing the deprecated `request.routeSchema` property. | Use `request.routeOptions.schema`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| <a id="FSTDEP016">FSTDEP016</a> | You are accessing the deprecated `request.routeConfig` property. | Use `request.routeOptions.config`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| <a id="FSTDEP017">FSTDEP017</a> | You are accessing the deprecated `request.routerPath` property. | Use `request.routeOptions.url`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| <a id="FSTDEP018">FSTDEP018</a> | You are accessing the deprecated `request.routerMethod` property. | Use `request.routeOptions.method`. | [#4470](https://github.com/fastify/fastify/pull/4470) |
| <a id="FSTDEP019">FSTDEP019</a> | 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) |
| <a id="FSTDEP020">FSTDEP020</a> | You are using the deprecated `reply.getReponseTime()` method. | Use the `reply.elapsedTime` property instead. | [#5263](https://github.com/fastify/fastify/pull/5263) |
| <a id="FSTDEP021">FSTDEP021</a> | The `reply.redirect()` method has a new signature: `reply.redirect(url: string, code?: number)`. It will be enforced in `fastify@v5`'. | [#5483](https://github.com/fastify/fastify/pull/5483) |
| <a id="FSTDEP022">FSTDEP022</a> | You are using the deprecated json shorthand schema on route %s. Specify full object schema instead. It will be removed in `fastify@v5` | [#5483](https://github.com/fastify/fastify/pull/0000) |
| <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)