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,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)