fastify 6.0.0-alpha.4 → 6.0.0-alpha.6

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 (63) hide show
  1. package/README.md +1 -0
  2. package/SECURITY.md +6 -0
  3. package/build/build-validation.js +2 -0
  4. package/docs/Guides/Ecosystem.md +12 -53
  5. package/docs/Guides/Migration-Guide-V5.md +2 -2
  6. package/docs/Guides/Serverless.md +33 -10
  7. package/docs/Reference/Errors.md +13 -2
  8. package/docs/Reference/Hooks.md +13 -0
  9. package/docs/Reference/Reply.md +787 -681
  10. package/docs/Reference/Request.md +308 -104
  11. package/docs/Reference/Routes.md +6 -2
  12. package/docs/Reference/Server.md +41 -4
  13. package/docs/Reference/Validation-and-Serialization.md +48 -1
  14. package/docs/Reference/Warnings.md +2 -0
  15. package/fastify.d.ts +1 -0
  16. package/fastify.js +35 -1
  17. package/lib/config-validator.js +102 -73
  18. package/lib/content-type-parser.js +35 -14
  19. package/lib/content-type.js +4 -4
  20. package/lib/context.js +2 -1
  21. package/lib/decorate.js +4 -3
  22. package/lib/error-handler.js +21 -4
  23. package/lib/error-status.js +4 -2
  24. package/lib/errors.js +15 -14
  25. package/lib/handle-request.js +7 -20
  26. package/lib/reply.js +18 -13
  27. package/lib/request.js +12 -7
  28. package/lib/route.js +40 -7
  29. package/lib/schemas.js +44 -32
  30. package/lib/validation.js +67 -10
  31. package/lib/warnings.js +10 -1
  32. package/package.json +8 -3
  33. package/test/child-logger-factory.test.js +25 -0
  34. package/test/close-disposable.test.js +154 -0
  35. package/test/constrained-routes.test.js +4 -2
  36. package/test/content-parser.test.js +99 -0
  37. package/test/decorator.test.js +29 -0
  38. package/test/diagnostics-channel/error-status.test.js +35 -2
  39. package/test/handler-timeout.test.js +224 -0
  40. package/test/internals/all.test.js +5 -0
  41. package/test/internals/errors.test.js +19 -20
  42. package/test/internals/initial-config.test.js +2 -0
  43. package/test/internals/reply-serialize.test.js +20 -0
  44. package/test/internals/reply.test.js +30 -1
  45. package/test/internals/request.test.js +50 -0
  46. package/test/internals/server.test.js +6 -4
  47. package/test/reply-error.test.js +82 -0
  48. package/test/reply-media-type.test.js +57 -0
  49. package/test/reply-trailers.test.js +159 -1
  50. package/test/rfc-10008.test.js +6 -9
  51. package/test/router-compile.test.js +241 -0
  52. package/test/schema-feature.test.js +1 -1
  53. package/test/schema-serialization.test.js +120 -7
  54. package/test/schema-validation.test.js +251 -1
  55. package/test/types/errors.tst.ts +0 -1
  56. package/test/types/fastify.tst.ts +9 -0
  57. package/test/types/instance.tst.ts +1 -1
  58. package/test/types/route.tst.ts +37 -0
  59. package/test/types/using.tst.ts +20 -0
  60. package/test/validation-error-handling.test.js +21 -1
  61. package/types/errors.d.ts +0 -1
  62. package/types/instance.d.ts +9 -5
  63. package/types/route.d.ts +4 -2
@@ -3,6 +3,7 @@ import * as http from 'node:http'
3
3
  import * as http2 from 'node:http2'
4
4
  import * as https from 'node:https'
5
5
  import { Socket } from 'node:net'
6
+ import pino from 'pino'
6
7
  import { expect } from 'tstyche'
7
8
  import fastify, {
8
9
  ConnectionError,
@@ -180,6 +181,10 @@ const customLogger = {
180
181
  debug: () => { },
181
182
  child: () => customLogger
182
183
  }
184
+ const pinoLogger = pino()
185
+ const pinoLoggerServer: FastifyInstance = fastify({ loggerInstance: pinoLogger })
186
+
187
+ expect(pinoLoggerServer).type.toBeAssignableTo<FastifyInstance>()
183
188
  expect(fastify({ logger: customLogger })).type.toBeAssignableTo<
184
189
  FastifyInstance<http.Server, http.IncomingMessage, http.ServerResponse, FastifyBaseLogger>
185
190
  >()
@@ -356,5 +361,9 @@ fastify({ routerOptions: { allowUnsafeRegex: true } })
356
361
  fastify({ routerOptions: { allowUnsafeRegex: false } })
357
362
  expect(fastify).type.not.toBeCallableWith({ routerOptions: { allowUnsafeRegex: 'invalid' } })
358
363
 
364
+ fastify({ routerOptions: { compile: true } })
365
+ fastify({ routerOptions: { compile: false } })
366
+ expect(fastify).type.not.toBeCallableWith({ routerOptions: { compile: 'invalid' } })
367
+
359
368
  expect(fastify({ allowErrorHandlerOverride: true })).type.toBeAssignableTo<FastifyInstance>()
360
369
  expect(fastify({ allowErrorHandlerOverride: false })).type.toBeAssignableTo<FastifyInstance>()
@@ -29,7 +29,7 @@ expect(server.addSchema({ schemas: [] })).type.toBe<FastifyInstance>()
29
29
  expect(server.pluginName).type.toBe<string>()
30
30
 
31
31
  expect(server.getSchemas()).type.toBe<Record<string, unknown>>()
32
- expect(server.addresses()).type.toBe<AddressInfo[]>()
32
+ expect(server.addresses()).type.toBe<Array<AddressInfo | string>>()
33
33
  expect(server.getSchema('SchemaId')).type.toBe<unknown>()
34
34
  expect(server.printRoutes()).type.toBe<string>()
35
35
  expect(server.printPlugins()).type.toBe<string>()
@@ -58,6 +58,15 @@ const asyncPreHandler = async (request: FastifyRequest) => {
58
58
 
59
59
  fastify().get('/', { preHandler: asyncPreHandler }, async () => 'this is an example')
60
60
 
61
+ fastify().route({
62
+ method: 'GET',
63
+ url: '/',
64
+ onRequest: async function () {
65
+ expect(this).type.toBe<FastifyInstance>()
66
+ },
67
+ handler: async () => 'ok'
68
+ })
69
+
61
70
  fastify().get(
62
71
  '/',
63
72
  { config: { foo: 'bar', bar: 100, includeMessage: true } },
@@ -74,6 +83,22 @@ fastify().get(
74
83
  }
75
84
  )
76
85
 
86
+ fastify().get(
87
+ '/',
88
+ { logLevel: 'info', logSerializers: { test: (value: number) => 'X' + value } },
89
+ routeHandler
90
+ )
91
+
92
+ fastify().route({
93
+ method: 'GET',
94
+ url: '/',
95
+ logSerializers: {
96
+ // serializers may return objects as well as strings
97
+ req: (req: FastifyRequest) => ({ url: req.url })
98
+ },
99
+ handler: routeHandler
100
+ })
101
+
77
102
  type LowerCaseHTTPMethods = 'delete' | 'get' | 'head' | 'patch' | 'post' | 'put' |
78
103
  'options' | 'query' | 'propfind' | 'proppatch' | 'mkcol' | 'copy' | 'move' | 'lock' |
79
104
  'unlock' | 'trace' | 'search' | 'mkcalendar' | 'report'
@@ -495,6 +520,12 @@ expect(fastify().hasRoute({
495
520
  method: 'GET'
496
521
  })).type.toBe<boolean>()
497
522
 
523
+ fastify().hasRoute({
524
+ url: '/',
525
+ // @ts-expect-error Type 'string[]' is not assignable to type 'HTTPMethods'.
526
+ method: ['GET', 'POST']
527
+ })
528
+
498
529
  expect(fastify().hasRoute({
499
530
  url: '/',
500
531
  method: 'GET',
@@ -539,6 +570,12 @@ expect(
539
570
  })
540
571
  ).type.toBe<Omit<FindMyWayFindResult<RawServerDefault>, 'store'>>()
541
572
 
573
+ fastify().findRoute({
574
+ url: '/',
575
+ // @ts-expect-error Type 'string[]' is not assignable to type 'HTTPMethods'.
576
+ method: ['GET', 'POST']
577
+ })
578
+
542
579
  // we should not expose store
543
580
  expect(fastify().findRoute({
544
581
  url: '/',
@@ -12,3 +12,23 @@ test("has 'Symbol.dispose'", async () => {
12
12
  expect(app).type.toBeAssignableTo<FastifyInstance>()
13
13
  expect(app[Symbol.asyncDispose]).type.toBe<() => Promise<undefined>>()
14
14
  })
15
+
16
+ test("addHook('onClose') accepts AsyncDisposable", () => {
17
+ const app = fastify()
18
+
19
+ const asyncDisposable: AsyncDisposable = {
20
+ async [Symbol.asyncDispose] () {}
21
+ }
22
+
23
+ expect(app.addHook('onClose', asyncDisposable)).type.toBeAssignableTo<FastifyInstance>()
24
+ })
25
+
26
+ test("addHook('onClose') accepts Disposable", () => {
27
+ const app = fastify()
28
+
29
+ const syncDisposable: Disposable = {
30
+ [Symbol.dispose] () {}
31
+ }
32
+
33
+ expect(app.addHook('onClose', syncDisposable)).type.toBeAssignableTo<FastifyInstance>()
34
+ })
@@ -252,6 +252,26 @@ test('attached validationError exposes the same message sent by the default hand
252
252
  t.assert.strictEqual(notAttached.statusCode, 400)
253
253
  })
254
254
 
255
+ test('default validation errors are FST_ERR_VALIDATION errors without a stack trace', async (t) => {
256
+ t.plan(5)
257
+
258
+ const fastify = Fastify()
259
+
260
+ let validationError
261
+ fastify.post('/', { schema, attachValidation: true }, function (req, reply) {
262
+ validationError = req.validationError
263
+ reply.code(400).send()
264
+ })
265
+
266
+ await fastify.inject({ method: 'POST', payload: { hello: 'michelangelo' }, url: '/' })
267
+
268
+ t.assert.ok(validationError instanceof Fastify.errorCodes.FST_ERR_VALIDATION)
269
+ t.assert.ok(validationError instanceof Error)
270
+ t.assert.strictEqual(validationError.name, 'FastifyError')
271
+ t.assert.strictEqual(validationError.message, "body must have required property 'name'")
272
+ t.assert.strictEqual(validationError.stack, undefined)
273
+ })
274
+
255
275
  test('should respect when attachValidation is explicitly set to false', async (t) => {
256
276
  t.plan(2)
257
277
 
@@ -303,7 +323,7 @@ test('Attached validation error should take precedence over setErrorHandler', as
303
323
  url: '/'
304
324
  })
305
325
 
306
- t.assert.deepStrictEqual(response.payload, "Attached: Error: body must have required property 'name'")
326
+ t.assert.deepStrictEqual(response.payload, "Attached: FastifyError [FST_ERR_VALIDATION]: body must have required property 'name'")
307
327
  t.assert.strictEqual(response.statusCode, 400)
308
328
  })
309
329
 
package/types/errors.d.ts CHANGED
@@ -83,7 +83,6 @@ export type FastifyErrorCodes = Record<
83
83
  'FST_ERR_ROUTE_HANDLER_TIMEOUT_OPTION_NOT_INT' |
84
84
  'FST_ERR_ROUTE_REWRITE_NOT_STR' |
85
85
  'FST_ERR_ROUTE_MISSING_CONTENT_TYPE' |
86
- 'FST_ERR_ROUTE_MISSING_CONTENT' |
87
86
  'FST_ERR_REOPENED_CLOSE_SERVER' |
88
87
  'FST_ERR_REOPENED_SERVER' |
89
88
  'FST_ERR_INSTANCE_ALREADY_STARTED' |
@@ -130,7 +130,7 @@ export interface FastifyInstance<
130
130
  version: string;
131
131
  log: Logger;
132
132
  listeningOrigin: string;
133
- addresses(): AddressInfo[]
133
+ addresses(): Array<AddressInfo | string>
134
134
  withTypeProvider<Provider extends FastifyTypeProvider>(): FastifyInstance<RawServer, RawRequest, RawReply, Logger,
135
135
  Provider>;
136
136
 
@@ -215,13 +215,13 @@ export interface FastifyInstance<
215
215
  RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
216
216
  ContextConfig = ContextConfigDefault,
217
217
  SchemaCompiler extends FastifySchema = FastifySchema
218
- >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'method' | 'url' | 'constraints'>): boolean;
218
+ >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'url' | 'constraints'> & { method: HTTPMethods }): boolean;
219
219
 
220
220
  findRoute<
221
221
  RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
222
222
  ContextConfig = ContextConfigDefault,
223
223
  SchemaCompiler extends FastifySchema = FastifySchema
224
- >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'method' | 'url' | 'constraints'>): Omit<FindMyWayFindResult<RawServer>, 'store'>;
224
+ >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'url' | 'constraints'> & { method: HTTPMethods }): Omit<FindMyWayFindResult<RawServer>, 'store'>;
225
225
 
226
226
  // addHook: overloads
227
227
 
@@ -485,6 +485,10 @@ export interface FastifyInstance<
485
485
  /**
486
486
  * Triggered when fastify.close() is invoked to stop the server. It is useful when plugins need a "shutdown" event, for example to close an open connection to a database.
487
487
  */
488
+ addHook(
489
+ name: 'onClose',
490
+ hook: AsyncDisposable | Disposable,
491
+ ): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
488
492
  addHook<
489
493
  Fn extends onCloseHookHandler | onCloseAsyncHookHandler = onCloseHookHandler
490
494
  >(
@@ -579,7 +583,7 @@ export interface FastifyInstance<
579
583
  * which allows for modifying or adding child logger bindings and logger options, or
580
584
  * returning a completely custom child logger implementation.
581
585
  */
582
- childLoggerFactory: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
586
+ childLoggerFactory: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, FastifyBaseLogger, TypeProvider>;
583
587
 
584
588
  /**
585
589
  * Hook function that is called when creating a child logger instance for each request
@@ -598,7 +602,7 @@ export interface FastifyInstance<
598
602
  * }
599
603
  * ```
600
604
  */
601
- setChildLoggerFactory(factory: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, Logger,
605
+ setChildLoggerFactory(factory: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, FastifyBaseLogger,
602
606
  TypeProvider>): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
603
607
 
604
608
  /**
package/types/route.d.ts CHANGED
@@ -46,7 +46,8 @@ export interface RouteConstraint {
46
46
  /**
47
47
  * Route shorthand options for the various shorthand methods
48
48
  */
49
- type RouteShorthandHook<T extends (...args: any) => any> = (...args: Parameters<T>) => void | Promise<unknown>
49
+ type RouteShorthandHook<T extends (this: any, ...args: any) => any> =
50
+ (this: ThisParameterType<T>, ...args: Parameters<T>) => void | Promise<unknown>
50
51
 
51
52
  export interface RouteShorthandOptions<
52
53
  RawServer extends RawServerBase = RawServerDefault,
@@ -67,6 +68,7 @@ export interface RouteShorthandOptions<
67
68
  bodyLimit?: number;
68
69
  handlerTimeout?: number;
69
70
  logLevel?: LogLevel;
71
+ logSerializers?: Record<string, (value: any) => any>;
70
72
  config?: FastifyContextConfig & ContextConfig;
71
73
  constraints?: RouteConstraint,
72
74
  prefixTrailingSlash?: 'slash' | 'no-slash' | 'both';
@@ -78,7 +80,7 @@ export interface RouteShorthandOptions<
78
80
  reply: FastifyReply<RouteGeneric, RawServer, RawRequest, RawReply, ContextConfig, NoInfer<SchemaCompiler>,
79
81
  TypeProvider>
80
82
  ) => void;
81
- childLoggerFactory?: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
83
+ childLoggerFactory?: FastifyChildLoggerFactory<RawServer, RawRequest, RawReply, FastifyBaseLogger, TypeProvider>;
82
84
  schemaErrorFormatter?: SchemaErrorFormatter;
83
85
 
84
86
  // hooks