fastify 3.11.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.
Files changed (76) hide show
  1. package/README.md +15 -10
  2. package/SECURITY.md +2 -2
  3. package/build/build-validation.js +25 -1
  4. package/docs/Benchmarking.md +3 -3
  5. package/docs/ContentTypeParser.md +21 -0
  6. package/docs/Decorators.md +1 -1
  7. package/docs/Ecosystem.md +38 -26
  8. package/docs/Encapsulation.md +4 -1
  9. package/docs/Errors.md +41 -41
  10. package/docs/Getting-Started.md +2 -2
  11. package/docs/HTTP2.md +1 -1
  12. package/docs/Hooks.md +35 -4
  13. package/docs/LTS.md +1 -1
  14. package/docs/Lifecycle.md +1 -1
  15. package/docs/Logging.md +4 -4
  16. package/docs/Middleware.md +3 -3
  17. package/docs/Migration-Guide-V3.md +3 -3
  18. package/docs/Plugins-Guide.md +15 -15
  19. package/docs/Plugins.md +7 -7
  20. package/docs/Recommendations.md +3 -4
  21. package/docs/Reply.md +4 -4
  22. package/docs/Request.md +1 -1
  23. package/docs/Routes.md +65 -10
  24. package/docs/Server.md +80 -14
  25. package/docs/Serverless.md +43 -64
  26. package/docs/Style-Guide.md +24 -19
  27. package/docs/Testing.md +5 -5
  28. package/docs/TypeScript.md +159 -17
  29. package/docs/Validation-and-Serialization.md +62 -6
  30. package/docs/Write-Plugin.md +3 -3
  31. package/fastify.d.ts +14 -3
  32. package/fastify.js +52 -18
  33. package/lib/configValidator.js +288 -53
  34. package/lib/contentTypeParser.js +28 -7
  35. package/lib/errors.js +1 -1
  36. package/lib/pluginOverride.js +5 -5
  37. package/lib/reply.js +35 -28
  38. package/lib/reqIdGenFactory.js +2 -1
  39. package/lib/request.js +1 -1
  40. package/lib/route.js +20 -27
  41. package/lib/schema-compilers.js +5 -3
  42. package/lib/schema-controller.js +106 -0
  43. package/lib/schemas.js +13 -23
  44. package/lib/symbols.js +1 -3
  45. package/lib/warnings.js +4 -0
  46. package/package.json +22 -17
  47. package/test/constrained-routes.test.js +184 -0
  48. package/test/content-parser.test.js +179 -7
  49. package/test/context-config.test.js +52 -0
  50. package/test/custom-parser.test.js +262 -2
  51. package/test/hooks-async.test.js +46 -0
  52. package/test/hooks.test.js +47 -0
  53. package/test/internals/initialConfig.test.js +30 -5
  54. package/test/internals/reply.test.js +2 -2
  55. package/test/internals/request.test.js +3 -9
  56. package/test/pretty-print.test.js +28 -0
  57. package/test/route.test.js +0 -2
  58. package/test/schema-feature.test.js +134 -4
  59. package/test/schema-serialization.test.js +42 -0
  60. package/test/schema-special-usage.test.js +234 -0
  61. package/test/schema-validation.test.js +1 -1
  62. package/test/stream.test.js +90 -0
  63. package/test/throw.test.js +1 -1
  64. package/test/types/content-type-parser.test-d.ts +8 -2
  65. package/test/types/fastify.test-d.ts +27 -0
  66. package/test/types/instance.test-d.ts +43 -1
  67. package/test/types/logger.test-d.ts +8 -3
  68. package/test/types/reply.test-d.ts +2 -1
  69. package/test/types/schema.test-d.ts +52 -1
  70. package/test/versioned-routes.test.js +99 -18
  71. package/types/content-type-parser.d.ts +10 -4
  72. package/types/instance.d.ts +57 -7
  73. package/types/logger.d.ts +1 -1
  74. package/types/reply.d.ts +2 -1
  75. package/types/route.d.ts +15 -11
  76. package/types/schema.d.ts +5 -4
@@ -37,7 +37,7 @@ This example will get you up and running with Fastify and TypeScript. It results
37
37
  ```
38
38
  3. Initialize a TypeScript configuration file:
39
39
  ```bash
40
- npx typescript --init
40
+ npx tsc --init
41
41
  ```
42
42
  or use one of the [recommended ones](https://github.com/tsconfig/bases#node-10-tsconfigjson).
43
43
 
@@ -82,7 +82,7 @@ The type system heavily relies on generic properties to provide the most accurat
82
82
  }
83
83
 
84
84
  interface IHeaders {
85
- 'H-Custom': string;
85
+ 'h-Custom': string;
86
86
  }
87
87
  ```
88
88
  3. Using the two interfaces, define a new API route and pass them as generics. The shorthand route methods (i.e. `.get`) accept a generic object `RequestGenericInterface` containing four named properties: `Body`, `Querystring`, `Params`, and `Headers`. The interfaces will be passed down through the route method into the route method handler `request` instance.
@@ -92,12 +92,13 @@ The type system heavily relies on generic properties to provide the most accurat
92
92
  Headers: IHeaders
93
93
  }>('/auth', async (request, reply) => {
94
94
  const { username, password } = request.query
95
- const customerHeader = request.headers['H-Custom']
95
+ const customerHeader = request.headers['h-Custom']
96
96
  // do something with request data
97
97
 
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
@@ -115,7 +116,7 @@ The type system heavily relies on generic properties to provide the most accurat
115
116
  done(username !== 'admin' ? new Error('Must be admin') : undefined) // only validate `admin` account
116
117
  }
117
118
  }, async (request, reply) => {
118
- const customerHeader = request.headers['H-Custom']
119
+ const customerHeader = request.headers['h-Custom']
119
120
  // do something with request data
120
121
  return `logged in!`
121
122
  })
@@ -126,25 +127,92 @@ 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",
140
207
  "type": "object",
141
208
  "properties": {
142
- "H-Custom": { "type": "string" }
209
+ "h-Custom": { "type": "string" }
143
210
  },
144
211
  "additionalProperties": false,
145
- "required": ["H-Custom"]
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
@@ -194,7 +266,7 @@ In the last example we used interfaces to define the types for the request query
194
266
  done(username !== 'admin' ? new Error('Must be admin') : undefined)
195
267
  }
196
268
  }, async (request, reply) => {
197
- const customerHeader = request.headers['H-Custom']
269
+ const customerHeader = request.headers['h-Custom']
198
270
  // do something with request data
199
271
  return `logged in!`
200
272
  })
@@ -211,11 +283,11 @@ In the last example we used interfaces to define the types for the request query
211
283
  },
212
284
  preHandler: (request, reply) => {
213
285
  const { username, password } = request.query
214
- const customerHeader = request.headers['H-Custom']
286
+ const customerHeader = request.headers['h-Custom']
215
287
  },
216
288
  handler: (request, reply) => {
217
289
  const { username, password } = request.query
218
- const customerHeader = request.headers['H-Custom']
290
+ const customerHeader = request.headers['h-Custom']
219
291
  }
220
292
  })
221
293
 
@@ -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
307
+
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.
233
332
 
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!
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
 
@@ -393,6 +522,19 @@ However, there are a couple of suggestions to help improve this experience:
393
522
  - Make sure the `no-unused-vars` rule is enabled in [ESLint](https://eslint.org/docs/rules/no-unused-vars) and any imported plugin are actually being loaded.
394
523
  - Use a module such as [depcheck](https://www.npmjs.com/package/depcheck) or [npm-check](https://www.npmjs.com/package/npm-check) to verify plugin dependencies are being used somewhere in your project.
395
524
 
525
+ ## Code Completion In Vanilla JavaScript
526
+
527
+ Vanilla JavaScript can use the published types to provide code completion (e.g. [Intellisense](https://code.visualstudio.com/docs/editor/intellisense)) by following the [TypeScript JSDoc Reference](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html).
528
+
529
+ For example:
530
+
531
+ ```js
532
+ /** @type {import('fastify').FastifyPluginAsync<{ optionA: boolean, optionB: string }>} */
533
+ module.exports = async function (fastify, { optionA, optionB }) {
534
+ fastify.get('/look', () => 'at me');
535
+ }
536
+ ```
537
+
396
538
  ## API Type System Documentation
397
539
 
398
540
  This section is a detailed account of all the types available to you in Fastify version 3.x
@@ -704,7 +846,7 @@ import fastify, { RequestGenericInterface } from 'fastify'
704
846
 
705
847
  const server = fastify()
706
848
 
707
- const requestGeneric: RequestGenericInterface = {
849
+ interface requestGeneric extends RequestGenericInterface {
708
850
  Querystring: {
709
851
  name: string
710
852
  }
@@ -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' })
@@ -198,6 +198,62 @@ fastify.post('/the/url', { schema }, handler)
198
198
 
199
199
  *Note that Ajv will try to [coerce](https://github.com/epoberezkin/ajv#coercing-data-types) the values to the types specified in your schema `type` keywords, both to pass the validation and to use the correctly typed data afterwards.*
200
200
 
201
+ The Ajv default configuration in Fastify doesn't support coercing array parameters in querystring. However, Fastify allows [`customOptions`](Server.md#ajv) in Ajv instance. The `coerceTypes: 'array'` will coerce one parameter to a single element in array. Example:
202
+
203
+ ```js
204
+ const opts = {
205
+ schema: {
206
+ querystring: {
207
+ type: 'object',
208
+ properties: {
209
+ ids: {
210
+ type: 'array',
211
+ default: []
212
+ },
213
+ },
214
+ }
215
+ }
216
+ }
217
+
218
+ fastify.get('/', opts, (request, reply) => {
219
+ reply.send({ params: request.query })
220
+ })
221
+
222
+ fastify.listen(3000, (err) => {
223
+ if (err) throw err
224
+ })
225
+ ```
226
+
227
+ Using Fastify defaults the following request will result in `400` status code:
228
+
229
+ ```sh
230
+ curl -X GET "http://localhost:3000/?ids=1
231
+
232
+ {"statusCode":400,"error":"Bad Request","message":"querystring/hello should be array"}
233
+ ```
234
+
235
+ Using `coerceTypes` as 'array' should fix it:
236
+
237
+ ```js
238
+ const ajv = new Ajv({
239
+ removeAdditional: true,
240
+ useDefaults: true,
241
+ coerceTypes: 'array', // This line
242
+ allErrors: true
243
+ })
244
+
245
+ fastify.setValidatorCompiler(({ schema, method, url, httpPart }) => {
246
+ return ajv.compile(schema)
247
+ })
248
+ ```
249
+
250
+ ```sh
251
+ curl -X GET "http://localhost:3000/?ids=1
252
+
253
+ {"params":{"hello":["1"]}}
254
+ ```
255
+
256
+ For further information see [here](https://ajv.js.org/docs/coercion.html)
201
257
 
202
258
  <a name="ajv-plugins"></a>
203
259
  #### Ajv Plugins
@@ -488,10 +544,10 @@ const schema = {
488
544
  and fail to satisfy it, the route will immediately return a response with the following payload
489
545
 
490
546
  ```js
491
- {
547
+ {
492
548
  "statusCode": 400,
493
549
  "error": "Bad Request",
494
- "message": "body should have required property 'name'"
550
+ "message": "body should have required property 'name'"
495
551
  }
496
552
  ```
497
553
 
@@ -519,7 +575,7 @@ The context function will be the Fastify server instance.
519
575
  ```js
520
576
  const fastify = Fastify({
521
577
  schemaErrorFormatter: (errors, dataVar) => {
522
- // ... my formatting logic
578
+ // ... my formatting logic
523
579
  return new Error(myErrorMessage)
524
580
  }
525
581
  })
@@ -527,7 +583,7 @@ const fastify = Fastify({
527
583
  // or
528
584
  fastify.setSchemaErrorFormatter(function (errors, dataVar) {
529
585
  this.log.error({ err: errors }, 'Validation failed')
530
- // ... my formatting logic
586
+ // ... my formatting logic
531
587
  return new Error(myErrorMessage)
532
588
  })
533
589
  ```
@@ -542,7 +598,7 @@ fastify.setErrorHandler(function (error, request, reply) {
542
598
  })
543
599
  ```
544
600
 
545
- 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.
546
602
 
547
603
  Below is an example showing how to add **custom error messages for each property** of a schema by supplying custom AJV options.
548
604
  Inline comments in the schema below describe how to configure it to show a different error message for each case:
@@ -41,12 +41,12 @@ It is not mandatory, but we highly recommend you use a code linter in your plugi
41
41
  We use [`standard`](https://standardjs.com/) since it works without the need to configure it and is very easy integrate in a test suite.
42
42
 
43
43
  ## Continuous Integration
44
- It is not mandatory, but if you release your code as open source it helps to use Continuous Integration to ensure contributions do not break your plugin and to show that the plugin works as intended. Both [CircleCI](https://circleci.com/) and [GitHub Actions](https://github.com/features/actions) are free for open source projects and easy to setup.<br>
45
- In addition you can enable services like [Dependabot](https://dependabot.com/) or [Snyk](https://snyk.io/), that will help you keep your dependencies up to date and discover if a new release of Fastify has some issues with your plugin.
44
+ It is not mandatory, but if you release your code as open source, it helps to use Continuous Integration to ensure contributions do not break your plugin and to show that the plugin works as intended. Both [CircleCI](https://circleci.com/) and [GitHub Actions](https://github.com/features/actions) are free for open source projects and easy to setup.<br>
45
+ In addition, you can enable services like [Dependabot](https://dependabot.com/) or [Snyk](https://snyk.io/), which will help you keep your dependencies up to date and discover if a new release of Fastify has some issues with your plugin.
46
46
 
47
47
  ## Let's start!
48
48
  Awesome, now you know everything you need to know about how to write a good plugin for Fastify!
49
- After you've built one (or more!) let us know! We will add it to the [ecosystem](https://github.com/fastify/fastify#ecosystem) section of our documentation!
49
+ After you have built one (or more!) let us know! We will add it to the [ecosystem](https://github.com/fastify/fastify#ecosystem) section of our documentation!
50
50
 
51
51
  If you want to see some real world examples, checkout:
52
52
  - [`point-of-view`](https://github.com/fastify/point-of-view)
package/fastify.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as http from 'http'
2
2
  import * as http2 from 'http2'
3
3
  import * as https from 'https'
4
4
  import * as LightMyRequest from 'light-my-request'
5
+ import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
5
6
 
6
7
  import { FastifyRequest, RequestGenericInterface } from './types/request'
7
8
  import { RawServerBase, RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression } from './types/utils'
@@ -12,6 +13,7 @@ import * as ajv from 'ajv'
12
13
  import { FastifyError } from 'fastify-error'
13
14
  import { FastifyReply } from './types/reply'
14
15
  import { FastifySchemaValidationError } from './types/schema'
16
+ import { ConstructorAction, ProtoAction } from "./types/content-type-parser";
15
17
 
16
18
  /**
17
19
  * Fastify factory function for the standard fastify http, https, or http2 server instance.
@@ -69,6 +71,9 @@ export type FastifyHttpsOptions<
69
71
  > = FastifyServerOptions<Server, Logger> & {
70
72
  https: https.ServerOptions
71
73
  }
74
+
75
+ type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server ? HTTPVersion.V1 : HTTPVersion.V2
76
+
72
77
  /**
73
78
  * Options for a fastify server instance. Utilizes conditional logic on the generic server parameter to enforce certain https and http2
74
79
  */
@@ -84,8 +89,8 @@ export type FastifyServerOptions<
84
89
  maxParamLength?: number,
85
90
  disableRequestLogging?: boolean,
86
91
  exposeHeadRoutes?: boolean,
87
- onProtoPoisoning?: 'error' | 'remove' | 'ignore',
88
- onConstructorPoisoning?: 'error' | 'remove' | 'ignore',
92
+ onProtoPoisoning?: ProtoAction,
93
+ onConstructorPoisoning?: ConstructorAction,
89
94
  logger?: boolean | FastifyLoggerOptions<RawServer> | Logger,
90
95
  serverFactory?: FastifyServerFactory<RawServer>,
91
96
  caseSensitive?: boolean,
@@ -94,6 +99,9 @@ export type FastifyServerOptions<
94
99
  genReqId?: <RequestGeneric extends RequestGenericInterface = RequestGenericInterface>(req: FastifyRequest<RequestGeneric, RawServer, RawRequestDefaultExpression<RawServer>>) => string,
95
100
  trustProxy?: boolean | string | string[] | number | TrustProxyFunction,
96
101
  querystringParser?: (str: string) => { [key: string]: unknown },
102
+ /**
103
+ * @deprecated Prefer using the `constraints.version` property
104
+ */
97
105
  versioning?: {
98
106
  storage(): {
99
107
  get(version: string): string | null,
@@ -103,6 +111,9 @@ export type FastifyServerOptions<
103
111
  },
104
112
  deriveVersion<Context>(req: Object, ctx?: Context): string // not a fan of using Object here. Also what is Context? Can either of these be better defined?
105
113
  },
114
+ constraints?: {
115
+ [name: string]: ConstraintStrategy<FindMyWayVersion<RawServer>>,
116
+ },
106
117
  return503OnClosing?: boolean,
107
118
  ajv?: {
108
119
  customOptions?: ajv.Options,
@@ -142,7 +153,7 @@ export { FastifyLoggerOptions, FastifyLoggerInstance, FastifyLogFn, LogLevel } f
142
153
  export { FastifyContext } from './types/context'
143
154
  export { RouteHandler, RouteHandlerMethod, RouteOptions, RouteShorthandMethod, RouteShorthandOptions, RouteShorthandOptionsWithHandler } from './types/route'
144
155
  export * from './types/register'
145
- export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser } from './types/content-type-parser'
156
+ export { FastifyBodyParser, FastifyContentTypeParser, AddContentTypeParser, hasContentTypeParser, getDefaultJsonParser, ProtoAction, ConstructorAction } from './types/content-type-parser'
146
157
  export { FastifyError } from 'fastify-error'
147
158
  export { FastifySchema, FastifySchemaCompiler } from './types/schema'
148
159
  export { HTTPMethods, RawServerBase, RawRequestDefaultExpression, RawReplyDefaultExpression, RawServerDefault, ContextConfigDefault, RequestBodyDefault, RequestQuerystringDefault, RequestParamsDefault, RequestHeadersDefault } from './types/utils'
package/fastify.js CHANGED
@@ -15,9 +15,7 @@ const {
15
15
  kLogLevel,
16
16
  kLogSerializers,
17
17
  kHooks,
18
- kSchemas,
19
- kValidatorCompiler,
20
- kSerializerCompiler,
18
+ kSchemaController,
21
19
  kReplySerializerDefault,
22
20
  kContentTypeParser,
23
21
  kReply,
@@ -36,8 +34,8 @@ const Request = require('./lib/request')
36
34
  const supportedMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT', 'OPTIONS']
37
35
  const decorator = require('./lib/decorate')
38
36
  const ContentTypeParser = require('./lib/contentTypeParser')
37
+ const SchemaController = require('./lib/schema-controller')
39
38
  const { Hooks, hookRunnerApplication } = require('./lib/hooks')
40
- const { Schemas } = require('./lib/schemas')
41
39
  const { createLogger } = require('./lib/logger')
42
40
  const pluginUtils = require('./lib/pluginUtils')
43
41
  const reqIdGenFactory = require('./lib/reqIdGenFactory')
@@ -45,6 +43,7 @@ const { buildRouting, validateBodyLimitOption } = require('./lib/route')
45
43
  const build404 = require('./lib/fourOhFour')
46
44
  const getSecuredInitialConfig = require('./lib/initialConfigValidation')
47
45
  const override = require('./lib/pluginOverride')
46
+ const warning = require('./lib/warnings')
48
47
  const { defaultInitOptions } = getSecuredInitialConfig
49
48
 
50
49
  const {
@@ -86,6 +85,10 @@ function fastify (options) {
86
85
  throw new Error(`querystringParser option should be a function, instead got '${typeof options.querystringParser}'`)
87
86
  }
88
87
 
88
+ if (options.schemaController && options.schemaController.bucket && typeof options.schemaController.bucket !== 'function') {
89
+ throw new Error(`schemaController.bucket option should be a function, instead got '${typeof options.schemaController.bucket}'`)
90
+ }
91
+
89
92
  validateBodyLimitOption(options.bodyLimit)
90
93
 
91
94
  const requestIdHeader = options.requestIdHeader || defaultInitOptions.requestIdHeader
@@ -132,15 +135,34 @@ function fastify (options) {
132
135
 
133
136
  const initialConfig = getSecuredInitialConfig(options)
134
137
 
138
+ let constraints = options.constraints
139
+ if (options.versioning) {
140
+ warning.emit('FSTDEP009')
141
+ constraints = {
142
+ ...constraints,
143
+ version: {
144
+ name: 'version',
145
+ mustMatchWhenDerived: true,
146
+ storage: options.versioning.storage,
147
+ deriveConstraint: options.versioning.deriveVersion,
148
+ validate (value) {
149
+ if (typeof value !== 'string') {
150
+ throw new Error('Version constraint should be a string.')
151
+ }
152
+ }
153
+ }
154
+ }
155
+ }
156
+
135
157
  // Default router
136
158
  const router = buildRouting({
137
159
  config: {
138
160
  defaultRoute: defaultRoute,
139
161
  onBadUrl: onBadUrl,
162
+ constraints: constraints,
140
163
  ignoreTrailingSlash: options.ignoreTrailingSlash || defaultInitOptions.ignoreTrailingSlash,
141
164
  maxParamLength: options.maxParamLength || defaultInitOptions.maxParamLength,
142
- caseSensitive: options.caseSensitive,
143
- versioning: options.versioning
165
+ caseSensitive: options.caseSensitive
144
166
  }
145
167
  })
146
168
 
@@ -155,7 +177,7 @@ function fastify (options) {
155
177
  const { server, listen } = createServer(options, httpHandler)
156
178
 
157
179
  const setupResponseListeners = Reply.setupResponseListeners
158
- const schemas = new Schemas()
180
+ const schemaController = SchemaController.buildSchemaController(null, options.schemaController)
159
181
 
160
182
  // Public API
161
183
  const fastify = {
@@ -172,11 +194,9 @@ function fastify (options) {
172
194
  [kLogLevel]: '',
173
195
  [kLogSerializers]: null,
174
196
  [kHooks]: new Hooks(),
175
- [kSchemas]: schemas,
176
- [kValidatorCompiler]: null,
197
+ [kSchemaController]: schemaController,
177
198
  [kSchemaErrorFormatter]: null,
178
199
  [kErrorHandler]: defaultErrorHandler,
179
- [kSerializerCompiler]: null,
180
200
  [kReplySerializerDefault]: null,
181
201
  [kContentTypeParser]: new ContentTypeParser(
182
202
  bodyLimit,
@@ -230,10 +250,11 @@ function fastify (options) {
230
250
  addHook: addHook,
231
251
  // schemas
232
252
  addSchema: addSchema,
233
- getSchema: schemas.getSchema.bind(schemas),
234
- getSchemas: schemas.getSchemas.bind(schemas),
253
+ getSchema: schemaController.getSchema.bind(schemaController),
254
+ getSchemas: schemaController.getSchemas.bind(schemaController),
235
255
  setValidatorCompiler: setValidatorCompiler,
236
256
  setSerializerCompiler: setSerializerCompiler,
257
+ setSchemaController: setSchemaController,
237
258
  setReplySerializer: setReplySerializer,
238
259
  setSchemaErrorFormatter: setSchemaErrorFormatter,
239
260
  // custom parsers
@@ -247,6 +268,7 @@ function fastify (options) {
247
268
  ready: null,
248
269
  onClose: null,
249
270
  close: null,
271
+ printPlugins: null,
250
272
  // http server
251
273
  listen: listen,
252
274
  server: server,
@@ -281,10 +303,10 @@ function fastify (options) {
281
303
  get () { return this[kRoutePrefix] }
282
304
  },
283
305
  validatorCompiler: {
284
- get () { return this[kValidatorCompiler] }
306
+ get () { return this[kSchemaController].getValidatorCompiler() }
285
307
  },
286
308
  serializerCompiler: {
287
- get () { return this[kSerializerCompiler] }
309
+ get () { return this[kSchemaController].getSerializerCompiler() }
288
310
  },
289
311
  version: {
290
312
  get () {
@@ -330,6 +352,8 @@ function fastify (options) {
330
352
  avvio.on('start', () => (fastify[kState].started = true))
331
353
  fastify[kAvvioBoot] = fastify.ready // the avvio ready function
332
354
  fastify.ready = ready // overwrite the avvio ready function
355
+ fastify.printPlugins = avvio.prettyPrint.bind(avvio)
356
+
333
357
  // cache the closing value, since we are checking it in an hot path
334
358
  avvio.once('preReady', () => {
335
359
  fastify.onClose((instance, done) => {
@@ -495,7 +519,7 @@ function fastify (options) {
495
519
  // wrapper that we expose to the user for schemas handling
496
520
  function addSchema (schema) {
497
521
  throwIfAlreadyStarted('Cannot call "addSchema" when fastify instance is already started!')
498
- this[kSchemas].add(schema)
522
+ this[kSchemaController].add(schema)
499
523
  this[kChildren].forEach(child => child.addSchema(schema))
500
524
  return this
501
525
  }
@@ -516,7 +540,7 @@ function fastify (options) {
516
540
 
517
541
  // Most devs do not know what to do with this error.
518
542
  // In the vast majority of cases, it's a network error and/or some
519
- // config issue on the the load balancer side.
543
+ // config issue on the load balancer side.
520
544
  this.log.trace({ err }, 'client error')
521
545
 
522
546
  // If the socket is not writable, there is no reason to try to send data.
@@ -562,7 +586,7 @@ function fastify (options) {
562
586
 
563
587
  function setValidatorCompiler (validatorCompiler) {
564
588
  throwIfAlreadyStarted('Cannot call "setValidatorCompiler" when fastify instance is already started!')
565
- this[kValidatorCompiler] = validatorCompiler
589
+ this[kSchemaController].setValidatorCompiler(validatorCompiler)
566
590
  return this
567
591
  }
568
592
 
@@ -575,7 +599,17 @@ function fastify (options) {
575
599
 
576
600
  function setSerializerCompiler (serializerCompiler) {
577
601
  throwIfAlreadyStarted('Cannot call "setSerializerCompiler" when fastify instance is already started!')
578
- this[kSerializerCompiler] = serializerCompiler
602
+ this[kSchemaController].setSerializerCompiler(serializerCompiler)
603
+ return this
604
+ }
605
+
606
+ function setSchemaController (schemaControllerOpts) {
607
+ throwIfAlreadyStarted('Cannot call "setSchemaController" when fastify instance is already started!')
608
+ const old = this[kSchemaController]
609
+ const schemaController = SchemaController.buildSchemaController(old.parent, Object.assign({}, old.opts, schemaControllerOpts))
610
+ this[kSchemaController] = schemaController
611
+ this.getSchema = schemaController.getSchema.bind(schemaController)
612
+ this.getSchemas = schemaController.getSchemas.bind(schemaController)
579
613
  return this
580
614
  }
581
615