fastify 3.14.0 → 3.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <div align="center">
2
- <img src="https://github.com/fastify/graphics/raw/master/full-logo.png" width="650" height="auto"/>
2
+ <img src="https://github.com/fastify/graphics/raw/HEAD/full-logo.png" width="650" height="auto"/>
3
3
  </div>
4
4
 
5
5
  <div align="center">
@@ -8,7 +8,7 @@
8
8
  [![Package Manager CI](https://github.com/fastify/fastify/workflows/package-manager-ci/badge.svg)](https://github.com/fastify/fastify/actions/workflows/package-manager-ci.yml)
9
9
  [![Web SIte](https://github.com/fastify/fastify/workflows/website/badge.svg)](https://github.com/fastify/fastify/actions/workflows/website.yml)
10
10
  [![Known Vulnerabilities](https://snyk.io/test/github/fastify/fastify/badge.svg)](https://snyk.io/test/github/fastify/fastify)
11
- [![Coverage Status](https://coveralls.io/repos/github/fastify/fastify/badge.svg?branch=master)](https://coveralls.io/github/fastify/fastify?branch=master)
11
+ [![Coverage Status](https://coveralls.io/repos/github/fastify/fastify/badge.svg?branch=main)](https://coveralls.io/github/fastify/fastify?branch=main)
12
12
  [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)
13
13
 
14
14
  </div>
@@ -18,7 +18,7 @@
18
18
  [![NPM version](https://img.shields.io/npm/v/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify)
19
19
  [![NPM downloads](https://img.shields.io/npm/dm/fastify.svg?style=flat)](https://www.npmjs.com/package/fastify)
20
20
  [![Security Responsible
21
- Disclosure](https://img.shields.io/badge/Security-Responsible%20Disclosure-yellow.svg)](https://github.com/nodejs/security-wg/blob/master/processes/responsible_disclosure_template.md)
21
+ Disclosure](https://img.shields.io/badge/Security-Responsible%20Disclosure-yellow.svg)](https://github.com/nodejs/security-wg/blob/HEAD/processes/responsible_disclosure_template.md)
22
22
  [![Discord](https://img.shields.io/discord/725613461949906985)](https://discord.gg/fastify)
23
23
 
24
24
  </div>
@@ -191,7 +191,7 @@ matters to you.
191
191
  * <a href="./docs/Serverless.md"><code><b>Serverless</b></code></a>
192
192
  * <a href="./docs/Recommendations.md"><code><b>Recommendations</b></code></a>
193
193
 
194
- 中文文档[地址](https://github.com/fastify/docs-chinese/blob/master/README.md)
194
+ 中文文档[地址](https://github.com/fastify/docs-chinese/blob/HEAD/README.md)
195
195
 
196
196
  ## Ecosystem
197
197
 
@@ -238,6 +238,7 @@ Team members are listed in alphabetical order.
238
238
  * [__Vincent Le Goff__](https://github.com/zekth)
239
239
  * [__Salman Mitha__](https://github.com/salmanm), <https://www.npmjs.com/~salmanm>
240
240
  * [__Maksim Sinik__](https://github.com/fox1t), <https://twitter.com/maksimsinik>, <https://www.npmjs.com/~fox1t>
241
+ * [__Frazer Smith__](https://github.com/Fdawgs), <https://www.npmjs.com/~fdawgs>
241
242
  * [__Manuel Spigolon__](https://github.com/eomm), <https://twitter.com/manueomm>, <https://www.npmjs.com/~eomm>
242
243
 
243
244
  ### Great Contributors
@@ -255,9 +256,9 @@ Great contributors on a specific area in the Fastify ecosystem will be invited t
255
256
 
256
257
  ## Hosted by
257
258
 
258
- [<img src="https://github.com/openjs-foundation/cross-project-council/blob/master/logos/openjsf-color.png?raw=true" width="250px;"/>](https://openjsf.org/projects/#growth)
259
+ [<img src="https://github.com/openjs-foundation/cross-project-council/blob/HEAD/logos/openjsf-color.png?raw=true" width="250px;"/>](https://openjsf.org/projects/#growth)
259
260
 
260
- We are a [Growth Project](https://github.com/openjs-foundation/cross-project-council/blob/master/PROJECT_PROGRESSION.md#growth-stage) in the [OpenJS Foundation](https://openjsf.org/).
261
+ We are a [Growth Project](https://github.com/openjs-foundation/cross-project-council/blob/HEAD/PROJECT_PROGRESSION.md#growth-stage) in the [OpenJS Foundation](https://openjsf.org/).
261
262
 
262
263
  ## Acknowledgements
263
264
 
package/SECURITY.md CHANGED
@@ -9,7 +9,7 @@ Individuals who find potential vulnerabilities in Fastify are invited to complet
9
9
 
10
10
  ### How to report a vulnerability
11
11
 
12
- It is of the utmost importance that you read carefully [**HOW TO REPORT A VULNERABILITY**](https://github.com/nodejs/security-wg/blob/master/processes/third_party_vuln_process.md) written by the Security Working Group of Node.js.
12
+ It is of the utmost importance that you read carefully [**HOW TO REPORT A VULNERABILITY**](https://github.com/nodejs/security-wg/blob/HEAD/processes/third_party_vuln_process.md) written by the Security Working Group of Node.js.
13
13
 
14
14
 
15
15
  ## Handling vulnerability reports
@@ -24,7 +24,7 @@ When a potential vulnerability is reported and confirmed the Fastify Core Team w
24
24
 
25
25
  ## The Fastify Core team
26
26
 
27
- The core team is responsible for the management of [this](https://github.com/nodejs/security-wg/blob/master/processes/third_party_vuln_process.md#handling-vulnerability-reports) process.
27
+ The core team is responsible for the management of [this](https://github.com/nodejs/security-wg/blob/HEAD/processes/third_party_vuln_process.md#handling-vulnerability-reports) process.
28
28
 
29
29
  Members of this team are expected to keep all information that they have privileged access to by being
30
30
  on the team completely private to the team. This includes agreeing to not notify anyone outside the
@@ -33,7 +33,7 @@ branchcmp --rounds 2 --script "npm run benchmark"
33
33
  branchcmp --rounds 2 --script "npm run benchmark"
34
34
  ```
35
35
 
36
- ### Compare current branch with master (Gitflow)
36
+ ### Compare current branch with main (Gitflow)
37
37
  ```sh
38
38
  branchcmp --rounds 2 --gitflow --script "npm run benchmark"
39
39
  ```
package/docs/Ecosystem.md CHANGED
@@ -57,12 +57,19 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
57
57
  - [`@dnlup/fastify-doc`](https://github.com/dnlup/fastify-doc) A plugin for sampling process metrics.
58
58
  - [`@dnlup/fastify-traps`](https://github.com/dnlup/fastify-traps) A plugin to close the server gracefully on `SIGINT` and `SIGTERM` signals.
59
59
  - [`@gquittet/graceful-server`](https://github.com/gquittet/graceful-server) Tiny (~5k), Fast, KISS and dependency-free Node.JS library to make your Fastify API graceful.
60
+ - [`@mgcrea/fastify-graceful-exit`](https://github.com/mgcrea/fastify-graceful-exit) A plugin to close the server gracefully
61
+ - [`@mgcrea/fastify-request-logger`](https://github.com/mgcrea/fastify-request-logger) A plugin to enable compact request logging for fastify
62
+ - [`@mgcrea/fastify-session-redis-store`](https://github.com/mgcrea/fastify-session-redis-store) Redis store for @mgcrea/fastify-session using ioredis
63
+ - [`@mgcrea/fastify-session-sodium-crypto`](https://github.com/mgcrea/fastify-session-sodium-crypto) Fast sodium-based crypto for @mgcrea/fastify-session
64
+ - [`@mgcrea/fastify-session`](https://github.com/mgcrea/fastify-session) Session plugin for fastify that supports both stateless and sateful sessions
65
+ - [`@mgcrea/pino-pretty-compact`](https://github.com/mgcrea/pino-pretty-compact) A custom compact pino-base prettifier
60
66
  - [`apollo-server-fastify`](https://github.com/apollographql/apollo-server/tree/master/packages/apollo-server-fastify) Run an [Apollo Server](https://github.com/apollographql/apollo-server) to serve GraphQL with Fastify.
61
67
  - [`arecibo`](https://github.com/nucleode/arecibo) Fastify ping responder for Kubernetes Liveness and Readiness Probes.
62
68
  - [`cls-rtracer`](https://github.com/puzpuzpuz/cls-rtracer) Fastify middleware for CLS-based request id generation. An out-of-the-box solution for adding request ids into your logs.
63
69
  - [`fastify-405`](https://github.com/Eomm/fastify-405) Fastify plugin that adds 405 HTTP status to your routes
64
70
  - [`fastify-amqp`](https://github.com/RafaelGSS/fastify-amqp) Fastify AMQP connection plugin, to use with RabbitMQ or another connector. Just a wrapper to [`amqplib`](https://github.com/squaremo/amqp.node).
65
71
  - [`fastify-angular-universal`](https://github.com/exequiel09/fastify-angular-universal) Angular server-side rendering support using [`@angular/platform-server`](https://github.com/angular/angular/tree/master/packages/platform-server) for Fastify
72
+ - [`fastify-api-key`](https://github.com/arkerone/fastify-api-key) Fastify plugin to authenticate HTTP requests based on api key and signature
66
73
  - [`fastify-auth0-verify`](https://github.com/nearform/fastify-auth0-verify): Auth0 verification plugin for Fastify, internally uses [fastify-jwt](https://npm.im/fastify-jwt) and [jsonwebtoken](https://npm.im/jsonwebtoken).
67
74
  - [`fastify-autocrud`](https://github.com/paranoiasystem/fastify-autocrud) Plugin to autogenerate CRUD routes as fast as possible.
68
75
  - [`fastify-autoroutes`](https://github.com/GiovanniCardamone/fastify-autoroutes) Plugin to scan and load routes based on filesystem path from custom directory.
@@ -169,6 +176,7 @@ Plugins maintained by the Fastify team are listed under [Core](#core) while plug
169
176
  - [`fastify-twitch-ebs-tools`](https://github.com/lukemnet/fastify-twitch-ebs-tools) Useful functions for Twitch Extension Backend Services (EBS).
170
177
  - [`fastify-typeorm-plugin`](https://github.com/inthepocket/fastify-typeorm-plugin) Fastify plugin to work with TypeORM.
171
178
  - [`fastify-vhost`](https://github.com/patrickpissurno/fastify-vhost) Proxy subdomain http requests to another server (useful if you want to point multiple subdomains to the same IP address, while running different servers on the same machine).
179
+ - [`fastify-vite`](https://github.com/galvez/fastify-vite) [Vite](https://vitejs.dev/) plugin for Fastify with SSR data support.
172
180
  - [`fastify-vue-plugin`](https://github.com/TheNoim/fastify-vue) [Nuxt.js](https://nuxtjs.org) plugin for Fastify. Control the routes nuxt should use.
173
181
  - [`fastify-wamp-router`](https://github.com/lependu/fastify-wamp-router) Web Application Messaging Protocol router for Fastify.
174
182
  - [`fast-water`](https://github.com/tswayne/fast-water) A Fastify plugin for waterline. Decorates Fastify with waterline models.
package/docs/Hooks.md CHANGED
@@ -85,19 +85,23 @@ fastify.addHook('preParsing', async (request, reply, payload) => {
85
85
  **Notice**: The old syntaxes `function(request, reply, done)` and `async function(request, reply)` for the parser are still supported but they are deprecated.
86
86
 
87
87
  ### preValidation
88
+
89
+ If you are using the `preValidation` hook, you can change the payload before it is validated. For example:
90
+
88
91
  ```js
89
92
  fastify.addHook('preValidation', (request, reply, done) => {
90
- // Some code
93
+ req.body = { ...req.body, importantKey: 'randomString' }
91
94
  done()
92
95
  })
93
96
  ```
94
97
  Or `async/await`:
95
98
  ```js
96
99
  fastify.addHook('preValidation', async (request, reply) => {
97
- // Some code
98
- await asyncMethod()
100
+ const importantKey = await generateRandomString()
101
+ req.body = { ...req.body, importantKey }
99
102
  })
100
103
  ```
104
+
101
105
  ### preHandler
102
106
  ```js
103
107
  fastify.addHook('preHandler', (request, reply, done) => {
package/docs/Logging.md CHANGED
@@ -59,7 +59,7 @@ const fastify = require('fastify')({
59
59
 
60
60
  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`](Server.md#factory-request-id-header) and Fastify Factory [`genReqId`](Server.md#gen-request-id) for customization options.
61
61
 
62
- 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.
62
+ 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.
63
63
  This behaviour can be customized by specifying custom serializers.
64
64
  ```js
65
65
  const fastify = require('fastify')({
@@ -267,7 +267,7 @@ fastify.get('/', (request, reply) => {
267
267
 
268
268
  ## Further additions and improvements
269
269
 
270
- - Hooks now have consistent context irregardless of how they are registered
270
+ - Hooks now have consistent context regardless of how they are registered
271
271
  ([#2005](https://github.com/fastify/fastify/pull/2005))
272
272
  - Deprecated `request.req` and `reply.res` for [`request.raw`](Request.md) and
273
273
  [`reply.raw`](Reply.md) ([#2008](https://github.com/fastify/fastify/pull/2008))
package/docs/Routes.md CHANGED
@@ -416,7 +416,7 @@ Fastify supports constraining routes to match only certain requests based on som
416
416
  #### Version Constraints
417
417
 
418
418
  You can provide a `version` key in the `constraints` option to a route. Versioned routes allows 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](http://semver.org/) specification, and routes should be declared with exact semver versions for matching.<br/>
419
- 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 ranges and pre-releases currently are not supported.<br/>
419
+ 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.<br/>
420
420
  *Be aware that using this feature will cause a degradation of the overall performances of the router.*
421
421
 
422
422
  ```js
package/docs/Server.md CHANGED
@@ -4,10 +4,9 @@
4
4
  ## Factory
5
5
 
6
6
  The Fastify module exports a factory function that is used to create new
7
- <a href="./Server.md"><code><b>Fastify server</b></code></a>
8
- instances. This factory function accepts an options object which is used to
9
- customize the resulting instance. This document describes the properties
10
- available in that options object.
7
+ <code><b>Fastify server</b></code> instances. This factory function accepts
8
+ an options object which is used to customize the resulting instance. This
9
+ document describes the properties available in that options object.
11
10
 
12
11
  <a name="factory-http2"></a>
13
12
  ### `http2`
@@ -192,7 +191,7 @@ fastify.addHook('onResponse', (req, reply, done) => {
192
191
  })
193
192
  ```
194
193
 
195
- Please note that this setting will also disable an error log written by the the default `onResponse` hook on reply callback errors.
194
+ Please note that this setting will also disable an error log written by the default `onResponse` hook on reply callback errors.
196
195
 
197
196
  <a name="custom-http-server"></a>
198
197
  ### `serverFactory`
@@ -986,7 +985,7 @@ fastify.ready(() => {
986
985
  <a name="addContentTypeParser"></a>
987
986
  #### addContentTypeParser
988
987
 
989
- `fastify.addContentTypeParser(content-type, options, parser)` is used to pass custom parser for a given content type. Useful for adding parsers for custom content types, e.g. `text/json, application/vnd.oasis.opendocument.text`. `content-type` can be a string or string array.
988
+ `fastify.addContentTypeParser(content-type, options, parser)` is used to pass custom parser for a given content type. Useful for adding parsers for custom content types, e.g. `text/json, application/vnd.oasis.opendocument.text`. `content-type` can be a string, string array or RegExp.
990
989
 
991
990
  ```js
992
991
  // The two arguments passed to getDefaultJsonParser are for ProtoType poisoning and Constructor Poisoning configuration respectively. The possible values are 'ignore', 'remove', 'error'. ignore skips all validations and it is similar to calling JSON.parse() directly. See the <a href="https://github.com/fastify/secure-json-parse#api">`secure-json-parse` documentation</a> for more information.
@@ -269,7 +269,7 @@ module.exports = {
269
269
 
270
270
  ### Scripts
271
271
 
272
- Add this command to your `package.json` *scripts*
272
+ Add this command to your `package.json` *scripts*
273
273
 
274
274
  ```json
275
275
  "scripts": {
@@ -291,9 +291,9 @@ configuring your `vercel.json` file like the following:
291
291
  ```json
292
292
  {
293
293
  "rewrites": [
294
- {
295
- "source": "/(.*)",
296
- "destination": "/api/serverless.js"
294
+ {
295
+ "source": "/(.*)",
296
+ "destination": "/api/serverless.js"
297
297
  }
298
298
  ]
299
299
  }
@@ -163,7 +163,7 @@ Here is how your hyperlink should look:
163
163
  ```MD
164
164
  <!-- More like this -->
165
165
 
166
- // Add clear & breif description
166
+ // Add clear & brief description
167
167
  [Fastify Plugins] (https://www.fastify.io/docs/latest/Plugins/)
168
168
 
169
169
  <!--Less like this -->
@@ -98,6 +98,7 @@ The type system heavily relies on generic properties to provide the most accurat
98
98
  return `logged in!`
99
99
  })
100
100
  ```
101
+
101
102
  4. Build and run the server code with `npm run build` and `npm run start`
102
103
  5. Query the api
103
104
  ```bash
@@ -126,14 +127,80 @@ The type system heavily relies on generic properties to provide the most accurat
126
127
 
127
128
  ### JSON Schema
128
129
 
130
+ To validate your requests and responses you can use JSON Schema files. If you didn't know already, defining schemas for your Fastify routes can increase their throughput! Check out the [Validation and Serialization](Validation-and-Serialization.md) documentation for more info.
131
+
132
+ Also it has the advantage to use the defined type within your handlers (including pre-validation, etc.).
133
+
134
+ Here are some options how to achieve this.
135
+
136
+
137
+ #### typebox
138
+
139
+ A useful library for building types and a schema at once is [typebox](https://www.npmjs.com/package/@sinclair/typebox).
140
+ With typebox you define your schema within your code and use them directly as types or schemas as you need them.
141
+
142
+ When you want to use it for validation of some payload in a fastify route you can do it as follows:
143
+
144
+ 1. Install `typebox` in your project.
145
+
146
+ ```bash
147
+ npm i @sinclair/typebox
148
+ ```
149
+
150
+ 2. Define the schema you need with `Type` and create the respective type with `Static`.
151
+
152
+ ```typescript
153
+ import { Static, Type } from '@sinclair/typebox'
154
+
155
+ const User = Type.Object({
156
+ name: Type.String(),
157
+ mail: Type.Optional(Type.String({ format: "email" })),
158
+ });
159
+ type UserType = Static<typeof User>;
160
+ ```
161
+
162
+ 3. Use the defined type and schema during the definition of your route
163
+
164
+ ```typescript
165
+ const app = fastify();
166
+
167
+ app.post<{ Body: UserType; Response: UserType }>(
168
+ "/",
169
+ {
170
+ schema: {
171
+ body: User,
172
+ response: {
173
+ 200: User,
174
+ },
175
+ },
176
+ },
177
+ (req, rep) => {
178
+ const { body: user } = req;
179
+ /* user has type
180
+ * const user: StaticProperties<{
181
+ * name: TString;
182
+ * mail: TOptional<TString>;
183
+ * }>
184
+ */
185
+ //...
186
+ rep.status(200).send(user);
187
+ }
188
+ );
189
+ ```
190
+
191
+ #### Schemas in JSON Files
192
+
129
193
  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!
130
194
 
131
195
  1. If you did not complete the 'Getting Started' example, go back and follow steps 1-4 first.
132
196
  2. Install the `json-schema-to-typescript` module:
133
- ```
197
+
198
+ ```bash
134
199
  npm i -D json-schema-to-typescript
135
200
  ```
201
+
136
202
  3. Create a new folder called `schemas` and add two files `headers.json` and `querystring.json`. Copy and paste the following schema definitions into the respective files:
203
+
137
204
  ```json
138
205
  {
139
206
  "title": "Headers Schema",
@@ -145,6 +212,7 @@ In the last example we used interfaces to define the types for the request query
145
212
  "required": ["h-Custom"]
146
213
  }
147
214
  ```
215
+
148
216
  ```json
149
217
  {
150
218
  "title": "Querystring Schema",
@@ -157,18 +225,22 @@ In the last example we used interfaces to define the types for the request query
157
225
  "required": ["username", "password"]
158
226
  }
159
227
  ```
228
+
160
229
  4. Add a `compile-schemas` script to the package.json:
161
- ```json
230
+
231
+ ```json
162
232
  {
163
233
  "scripts": {
164
234
  "compile-schemas": "json2ts -i schemas -o types"
165
235
  }
166
236
  }
167
- ```
237
+ ```
238
+
168
239
  `json2ts` is a CLI utility included in `json-schema-to-typescript`. `schemas` is the input path, and `types` is the output path.
169
240
  5. Run `npm run compile-schemas`. Two new files should have been created in the `types` directory.
170
241
  6. Update `index.ts` to have the following code:
171
- ```typescript
242
+
243
+ ```typescript
172
244
  import fastify from 'fastify'
173
245
 
174
246
  // import json schemas as normal
@@ -229,10 +301,67 @@ In the last example we used interfaces to define the types for the request query
229
301
  ```
230
302
  Pay special attention to the imports at the top of this file. It might seem redundant, but you need to import both the schema files and the generated interfaces.
231
303
 
232
- Great work! Now you can make use of both JSON Schemas and TypeScript definitions. If you didn't know already, defining schemas for your Fastify routes can increase their throughput! Check out the [Validation and Serialization](Validation-and-Serialization.md) documentation for more info.
304
+ Great work! Now you can make use of both JSON Schemas and TypeScript definitions.
305
+
306
+ #### json-schema-to-ts
233
307
 
234
- Some additional notes:
235
- - Currently, there is no type definition support for inline JSON schemas. If you can come up with a solution please open a PR!
308
+ If you do not want to generate types from your schemas, but want to use them diretly from your code, you can use the package
309
+ [json-schema-to-ts](https://www.npmjs.com/package/json-schema-to-ts).
310
+
311
+ You can install it as dev-dependency.
312
+
313
+ ```bash
314
+ npm install -D json-schema-to-ts
315
+ ```
316
+
317
+ In your code you can define your schema like a normal object. But be aware of making it *const* like explained in the docs of the module.
318
+
319
+ ```typescript
320
+ const todo = {
321
+ type: 'object',
322
+ properties: {
323
+ name: { type: 'string' },
324
+ description: { type: 'string' },
325
+ done: { type: 'boolean' },
326
+ },
327
+ required: ['name'],
328
+ } as const;
329
+ ```
330
+
331
+ With the provided type `FromSchema` you can build a type from your schema and use it in your handler.
332
+
333
+ ```typescript
334
+ fastify.post<{ Body: FromSchema<typeof todo> }>(
335
+ '/todo',
336
+ {
337
+ schema: {
338
+ body: todo,
339
+ response: {
340
+ 201: {
341
+ type: 'string',
342
+ },
343
+ },
344
+ }
345
+ },
346
+ async (request, reply): Promise<void> => {
347
+
348
+ /*
349
+ request.body has type
350
+ {
351
+ [x: string]: unknown;
352
+ description?: string;
353
+ done?: boolean;
354
+ name: string;
355
+ }
356
+ */
357
+
358
+ request.body.name // will not throw type error
359
+ request.body.notthere // will throw type error
360
+
361
+ reply.status(201).send();
362
+ },
363
+ );
364
+ ```
236
365
 
237
366
  ### Plugins
238
367
 
@@ -101,7 +101,7 @@ As usual, the function `getSchemas` is encapsulated and returns the shared schem
101
101
  ```js
102
102
  fastify.addSchema({ $id: 'one', my: 'hello' })
103
103
  // will return only `one` schema
104
- fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })
104
+ fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })
105
105
 
106
106
  fastify.register((instance, opts, done) => {
107
107
  instance.addSchema({ $id: 'two', my: 'ciao' })
@@ -544,10 +544,10 @@ const schema = {
544
544
  and fail to satisfy it, the route will immediately return a response with the following payload
545
545
 
546
546
  ```js
547
- {
547
+ {
548
548
  "statusCode": 400,
549
549
  "error": "Bad Request",
550
- "message": "body should have required property 'name'"
550
+ "message": "body should have required property 'name'"
551
551
  }
552
552
  ```
553
553
 
@@ -575,7 +575,7 @@ The context function will be the Fastify server instance.
575
575
  ```js
576
576
  const fastify = Fastify({
577
577
  schemaErrorFormatter: (errors, dataVar) => {
578
- // ... my formatting logic
578
+ // ... my formatting logic
579
579
  return new Error(myErrorMessage)
580
580
  }
581
581
  })
@@ -583,7 +583,7 @@ const fastify = Fastify({
583
583
  // or
584
584
  fastify.setSchemaErrorFormatter(function (errors, dataVar) {
585
585
  this.log.error({ err: errors }, 'Validation failed')
586
- // ... my formatting logic
586
+ // ... my formatting logic
587
587
  return new Error(myErrorMessage)
588
588
  })
589
589
  ```
@@ -598,7 +598,7 @@ fastify.setErrorHandler(function (error, request, reply) {
598
598
  })
599
599
  ```
600
600
 
601
- If you want custom error response in schema without headaches and quickly, you can take a look at [`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Checkout the [example](https://github.com/fastify/example/blob/master/validation-messages/custom-errors-messages.js) usage.
601
+ If you want custom error response in schema without headaches and quickly, you can take a look at [`ajv-errors`](https://github.com/epoberezkin/ajv-errors). Checkout the [example](https://github.com/fastify/example/blob/HEAD/validation-messages/custom-errors-messages.js) usage.
602
602
 
603
603
  Below is an example showing how to add **custom error messages for each property** of a schema by supplying custom AJV options.
604
604
  Inline comments in the schema below describe how to configure it to show a different error message for each case:
package/fastify.d.ts CHANGED
@@ -13,6 +13,7 @@ import * as ajv from 'ajv'
13
13
  import { FastifyError } from 'fastify-error'
14
14
  import { FastifyReply } from './types/reply'
15
15
  import { FastifySchemaValidationError } from './types/schema'
16
+ import { ConstructorAction, ProtoAction } from "./types/content-type-parser";
16
17
 
17
18
  /**
18
19
  * Fastify factory function for the standard fastify http, https, or http2 server instance.
@@ -88,8 +89,8 @@ export type FastifyServerOptions<
88
89
  maxParamLength?: number,
89
90
  disableRequestLogging?: boolean,
90
91
  exposeHeadRoutes?: boolean,
91
- onProtoPoisoning?: 'error' | 'remove' | 'ignore',
92
- onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
92
+ onProtoPoisoning?: ProtoAction,
93
+ onConstructorPoisoning?: ConstructorAction,
93
94
  logger?: boolean | FastifyLoggerOptions<RawServer> | Logger,
94
95
  serverFactory?: FastifyServerFactory<RawServer>,
95
96
  caseSensitive?: boolean,
@@ -152,7 +153,7 @@ export { FastifyLoggerOptions, FastifyLoggerInstance, FastifyLogFn, LogLevel } f
152
153
  export { FastifyContext } from './types/context'
153
154
  export { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
154
155
  export * from './types/register'
155
- export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser } from './types/content-type-parser'
156
+ export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction } from './types/content-type-parser'
156
157
  export { FastifyError } from 'fastify-error'
157
158
  export { FastifySchema, FastifySchemaCompiler } from './types/schema'
158
159
  export { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
package/fastify.js CHANGED
@@ -540,7 +540,7 @@ function fastify (options) {
540
540
 
541
541
  // Most devs do not know what to do with this error.
542
542
  // In the vast majority of cases, it's a network error and/or some
543
- // config issue on the the load balancer side.
543
+ // config issue on the load balancer side.
544
544
  this.log.trace({ err }, 'client error')
545
545
 
546
546
  // If the socket is not writable, there is no reason to try to send data.
package/lib/reply.js CHANGED
@@ -539,19 +539,30 @@ function handleError (reply, error, cb) {
539
539
  return
540
540
  }
541
541
 
542
- const serializerFn = getSchemaSerializer(reply.context, statusCode)
543
- const payload = (serializerFn === false)
544
- ? serializeError({
545
- error: statusCodes[statusCode + ''],
546
- code: error.code,
547
- message: error.message || '',
548
- statusCode: statusCode
549
- })
550
- : serializerFn(Object.create(error, {
551
- error: { value: statusCodes[statusCode + ''] },
552
- message: { value: error.message || '' },
553
- statusCode: { value: statusCode }
554
- }))
542
+ let payload
543
+ try {
544
+ const serializerFn = getSchemaSerializer(reply.context, statusCode)
545
+ payload = (serializerFn === false)
546
+ ? serializeError({
547
+ error: statusCodes[statusCode + ''],
548
+ code: error.code,
549
+ message: error.message || '',
550
+ statusCode: statusCode
551
+ })
552
+ : serializerFn(Object.create(error, {
553
+ error: { value: statusCodes[statusCode + ''] },
554
+ message: { value: error.message || '' },
555
+ statusCode: { value: statusCode }
556
+ }))
557
+ } catch (err) {
558
+ reply.log.error({ err, statusCode: res.statusCode }, 'The serializer for the given status code failed')
559
+ res.statusCode = 500
560
+ payload = serializeError({
561
+ error: statusCodes['500'],
562
+ message: err.message || '',
563
+ statusCode: 500
564
+ })
565
+ }
555
566
 
556
567
  flatstr(payload)
557
568
  reply[kReplyHeaders]['content-type'] = CONTENT_TYPE.JSON
package/lib/route.js CHANGED
@@ -298,7 +298,7 @@ function buildRouting (options) {
298
298
  context.schema = normalizeSchema(context.schema)
299
299
 
300
300
  const schemaController = this[kSchemaController]
301
- if (!opts.validatorCompiler) {
301
+ if (!opts.validatorCompiler && (opts.schema.body || opts.schema.headers || opts.schema.querystring || opts.schema.params)) {
302
302
  schemaController.setupValidator(this[kOptions])
303
303
  }
304
304
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastify",
3
- "version": "3.14.0",
3
+ "version": "3.14.1",
4
4
  "description": "Fast and low overhead web framework, for Node.js",
5
5
  "main": "fastify.js",
6
6
  "type": "commonjs",
@@ -177,7 +177,7 @@
177
177
  "abstract-logging": "^2.0.0",
178
178
  "ajv": "^6.12.2",
179
179
  "avvio": "^7.1.2",
180
- "fast-json-stringify": "^2.5.0",
180
+ "fast-json-stringify": "^2.5.2",
181
181
  "fastify-error": "^0.3.0",
182
182
  "fastify-warning": "^0.2.0",
183
183
  "find-my-way": "^4.0.0",
@@ -318,6 +318,52 @@ test('preValidation hooks should be able to block a request', t => {
318
318
  })
319
319
  })
320
320
 
321
+ test('preValidation hooks should be able to change request body before validation', t => {
322
+ t.plan(4)
323
+ const fastify = Fastify()
324
+
325
+ fastify.addHook('preValidation', async (req, _reply) => {
326
+ const buff = Buffer.from(req.body.message, 'base64')
327
+ req.body = JSON.parse(buff.toString('utf-8'))
328
+ })
329
+
330
+ fastify.post(
331
+ '/',
332
+ {
333
+ schema: {
334
+ body: {
335
+ type: 'object',
336
+ properties: {
337
+ foo: {
338
+ type: 'string'
339
+ },
340
+ bar: {
341
+ type: 'number'
342
+ }
343
+ },
344
+ required: ['foo', 'bar']
345
+ }
346
+ }
347
+ },
348
+ (req, reply) => {
349
+ t.pass()
350
+ reply.status(200).send('hello')
351
+ }
352
+ )
353
+
354
+ fastify.inject({
355
+ url: '/',
356
+ method: 'POST',
357
+ payload: {
358
+ message: Buffer.from(JSON.stringify({ foo: 'example', bar: 1 })).toString('base64')
359
+ }
360
+ }, (err, res) => {
361
+ t.error(err)
362
+ t.is(res.statusCode, 200)
363
+ t.is(res.payload, 'hello')
364
+ })
365
+ })
366
+
321
367
  test('preSerialization hooks should be able to modify the payload', t => {
322
368
  t.plan(3)
323
369
  const fastify = Fastify()
@@ -1437,6 +1437,53 @@ test('preValidation hooks should be able to block a request', t => {
1437
1437
  })
1438
1438
  })
1439
1439
 
1440
+ test('preValidation hooks should be able to change request body before validation', t => {
1441
+ t.plan(4)
1442
+ const fastify = Fastify()
1443
+
1444
+ fastify.addHook('preValidation', (req, _reply, done) => {
1445
+ const buff = Buffer.from(req.body.message, 'base64')
1446
+ req.body = JSON.parse(buff.toString('utf-8'))
1447
+ done()
1448
+ })
1449
+
1450
+ fastify.post(
1451
+ '/',
1452
+ {
1453
+ schema: {
1454
+ body: {
1455
+ type: 'object',
1456
+ properties: {
1457
+ foo: {
1458
+ type: 'string'
1459
+ },
1460
+ bar: {
1461
+ type: 'number'
1462
+ }
1463
+ },
1464
+ required: ['foo', 'bar']
1465
+ }
1466
+ }
1467
+ },
1468
+ (req, reply) => {
1469
+ t.pass()
1470
+ reply.status(200).send('hello')
1471
+ }
1472
+ )
1473
+
1474
+ fastify.inject({
1475
+ url: '/',
1476
+ method: 'POST',
1477
+ payload: {
1478
+ message: Buffer.from(JSON.stringify({ foo: 'example', bar: 1 })).toString('base64')
1479
+ }
1480
+ }, (err, res) => {
1481
+ t.error(err)
1482
+ t.is(res.statusCode, 200)
1483
+ t.is(res.payload, 'hello')
1484
+ })
1485
+ })
1486
+
1440
1487
  test('preParsing hooks should be able to block a request', t => {
1441
1488
  t.plan(5)
1442
1489
  const fastify = Fastify()
@@ -498,3 +498,45 @@ test('The schema changes the default error handler output', async t => {
498
498
  t.equals(res.statusCode, 500)
499
499
  t.deepEquals(res.json(), { error: 'Internal Server Error', message: '500 message', customId: 42 })
500
500
  })
501
+
502
+ test('do not crash if status code serializer errors', async t => {
503
+ const fastify = Fastify()
504
+
505
+ const requiresFoo = {
506
+ properties: { foo: { type: 'string' } },
507
+ required: ['foo']
508
+ }
509
+
510
+ const someUserErrorType2 = {
511
+ properties: {
512
+ code: { type: 'number' }
513
+ },
514
+ required: ['code']
515
+ }
516
+
517
+ fastify.get(
518
+ '/',
519
+ {
520
+ schema: {
521
+ query: requiresFoo,
522
+ response: { 400: someUserErrorType2 }
523
+ }
524
+ },
525
+ (request, reply) => {
526
+ t.fail('handler, should not be called')
527
+ }
528
+ )
529
+
530
+ const res = await fastify.inject({
531
+ path: '/',
532
+ query: {
533
+ notfoo: true
534
+ }
535
+ })
536
+ t.equals(res.statusCode, 500)
537
+ t.deepEquals(res.json(), {
538
+ statusCode: 500,
539
+ error: 'Internal Server Error',
540
+ message: '"code" is required!'
541
+ })
542
+ })
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { test } = require('tap')
4
4
  const AJV = require('ajv')
5
+ const S = require('fluent-json-schema')
5
6
  const Fastify = require('..')
6
7
  const ajvMergePatch = require('ajv-merge-patch')
7
8
 
@@ -403,3 +404,54 @@ test('side effect on schema let the server crash', async t => {
403
404
 
404
405
  await fastify.ready()
405
406
  })
407
+
408
+ test('only response schema trigger AJV pollution', async t => {
409
+ const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
410
+ const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
411
+
412
+ const fastify = Fastify()
413
+ fastify.addSchema(ListSchema)
414
+ fastify.addSchema(ShowSchema)
415
+
416
+ const routeResponseSchemas = {
417
+ schema: { response: { 200: S.ref('ListSchema#') } }
418
+ }
419
+
420
+ fastify.register(
421
+ async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
422
+ { prefix: '/prefix1' }
423
+ )
424
+ fastify.register(
425
+ async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
426
+ { prefix: '/prefix2' }
427
+ )
428
+
429
+ await fastify.ready()
430
+ })
431
+
432
+ test('only response schema trigger AJV pollution #2', async t => {
433
+ const ShowSchema = S.object().id('ShowSchema').prop('name', S.string())
434
+ const ListSchema = S.array().id('ListSchema').items(S.ref('ShowSchema#'))
435
+
436
+ const fastify = Fastify()
437
+ fastify.addSchema(ListSchema)
438
+ fastify.addSchema(ShowSchema)
439
+
440
+ const routeResponseSchemas = {
441
+ schema: {
442
+ params: S.ref('ListSchema#'),
443
+ response: { 200: S.ref('ListSchema#') }
444
+ }
445
+ }
446
+
447
+ fastify.register(
448
+ async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
449
+ { prefix: '/prefix1' }
450
+ )
451
+ fastify.register(
452
+ async (app) => { app.get('/resource/', routeResponseSchemas, () => ({})) },
453
+ { prefix: '/prefix2' }
454
+ )
455
+
456
+ await fastify.ready()
457
+ })
@@ -1,5 +1,5 @@
1
- import fastify from '../../fastify'
2
- import { expectType } from 'tsd'
1
+ import fastify, { FastifyContentTypeParser } from '../../fastify'
2
+ import { expectError, expectType } from 'tsd'
3
3
  import { IncomingMessage } from 'http'
4
4
  import { FastifyRequest } from '../../types/request'
5
5
 
@@ -56,3 +56,9 @@ expectType<void>(fastify().addContentTypeParser<Buffer>('bodyContentType', { par
56
56
  expectType<Buffer>(body)
57
57
  return null
58
58
  }))
59
+
60
+ expectType<FastifyContentTypeParser>(fastify().getDefaultJsonParser('error', 'ignore'))
61
+
62
+ expectError(fastify().getDefaultJsonParser('error', 'skip'))
63
+
64
+ expectError(fastify().getDefaultJsonParser('nothing', 'ignore'))
@@ -1,5 +1,8 @@
1
- import fastify, { FastifyError, FastifyInstance, ValidationResult } from '../../fastify'
1
+ import fastify, { FastifyContentTypeParser, FastifyError, FastifyInstance, ValidationResult } from '../../fastify'
2
2
  import { expectAssignable, expectError, expectType } from 'tsd'
3
+ import { FastifyRequest } from '../../types/request'
4
+ import { FastifyReply } from '../../types/reply'
5
+ import { HookHandlerDoneFunction } from '../../types/hooks'
3
6
 
4
7
  const server = fastify()
5
8
 
@@ -38,6 +41,19 @@ server.setErrorHandler(fastifyErrorHandler)
38
41
  function nodeJSErrorHandler (error: NodeJS.ErrnoException) {}
39
42
  server.setErrorHandler(nodeJSErrorHandler)
40
43
 
44
+ function notFoundHandler (request: FastifyRequest, reply: FastifyReply) {}
45
+ function notFoundpreHandlerHandler (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { done() }
46
+ async function notFoundpreHandlerAsyncHandler (request: FastifyRequest, reply: FastifyReply) {}
47
+ function notFoundpreValidationHandler (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { done() }
48
+ async function notFoundpreValidationAsyncHandler (request: FastifyRequest, reply: FastifyReply) {}
49
+
50
+ server.setNotFoundHandler(notFoundHandler)
51
+ server.setNotFoundHandler({ preHandler: notFoundpreHandlerHandler }, notFoundHandler)
52
+ server.setNotFoundHandler({ preHandler: notFoundpreHandlerAsyncHandler }, notFoundHandler)
53
+ server.setNotFoundHandler({ preValidation: notFoundpreValidationHandler }, notFoundHandler)
54
+ server.setNotFoundHandler({ preValidation: notFoundpreValidationAsyncHandler }, notFoundHandler)
55
+ server.setNotFoundHandler({ preHandler: notFoundpreHandlerHandler, preValidation: notFoundpreValidationHandler }, notFoundHandler)
56
+
41
57
  function invalidErrorHandler (error: number) {}
42
58
  expectError(server.setErrorHandler(invalidErrorHandler))
43
59
 
@@ -106,3 +122,7 @@ type InitialConfig = Readonly<{
106
122
  }>
107
123
 
108
124
  expectType<InitialConfig>(fastify().initialConfig)
125
+
126
+ expectType<FastifyContentTypeParser>(server.defaultTextParser)
127
+
128
+ expectType<FastifyContentTypeParser>(server.getDefaultJsonParser('ignore', 'error'))
@@ -54,3 +54,9 @@ export interface AddContentTypeParser<
54
54
  * Checks for a type parser of a content type
55
55
  */
56
56
  export type hasContentTypeParser = (contentType: string | RegExp) => boolean
57
+
58
+ export type ProtoAction = 'error' | 'remove' | 'ignore'
59
+
60
+ export type ConstructorAction = 'error' | 'remove' | 'ignore'
61
+
62
+ export type getDefaultJsonParser = (onProtoPoisoning: ProtoAction, onConstructorPoisoning: ConstructorAction) => FastifyContentTypeParser
@@ -8,7 +8,14 @@ import { onRequestHookHandler, preParsingHookHandler, onSendHookHandler, preVali
8
8
  import { FastifyRequest } from './request'
9
9
  import { FastifyReply } from './reply'
10
10
  import { FastifyError } from 'fastify-error'
11
- import { AddContentTypeParser, hasContentTypeParser } from './content-type-parser'
11
+ import {
12
+ AddContentTypeParser,
13
+ hasContentTypeParser,
14
+ getDefaultJsonParser,
15
+ FastifyContentTypeParser,
16
+ ProtoAction,
17
+ ConstructorAction
18
+ } from './content-type-parser'
12
19
 
13
20
  /**
14
21
  * Fastify server instance. Returned by the core `fastify()` method.
@@ -312,10 +319,18 @@ export interface FastifyInstance<
312
319
  /**
313
320
  * Set the 404 handler
314
321
  */
315
- setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface>(
322
+ setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface> (
316
323
  handler: (request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, reply: FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric>) => void
317
324
  ): FastifyInstance<RawServer, RawRequest, RawReply, Logger>;
318
325
 
326
+ setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, ContextConfig extends ContextConfigDefault = ContextConfigDefault> (
327
+ opts: {
328
+ preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig>[];
329
+ preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig>[];
330
+ },
331
+ handler: (request: FastifyRequest<RouteGeneric, RawServer, RawRequest>, reply: FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric>) => void
332
+ ): FastifyInstance<RawServer, RawRequest, RawReply, Logger>
333
+
319
334
  /**
320
335
  * Fastify default error handler
321
336
  */
@@ -352,6 +367,14 @@ export interface FastifyInstance<
352
367
  */
353
368
  addContentTypeParser: AddContentTypeParser<RawServer, RawRequest>;
354
369
  hasContentTypeParser: hasContentTypeParser;
370
+ /**
371
+ * Fastify default JSON parser
372
+ */
373
+ getDefaultJsonParser: getDefaultJsonParser;
374
+ /**
375
+ * Fastify default plain text parser
376
+ */
377
+ defaultTextParser: FastifyContentTypeParser;
355
378
 
356
379
  /**
357
380
  * Prints the representation of the internal radix tree used by the router
@@ -376,8 +399,8 @@ export interface FastifyInstance<
376
399
  ignoreTrailingSlash?: boolean,
377
400
  disableRequestLogging?: boolean,
378
401
  maxParamLength?: number,
379
- onProtoPoisoning?: 'error' | 'remove' | 'ignore',
380
- onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
402
+ onProtoPoisoning?: ProtoAction,
403
+ onConstructorPoisoning?: ConstructorAction,
381
404
  pluginTimeout?: number,
382
405
  requestIdHeader?: string,
383
406
  requestIdLogLabel?: string,