fastify 4.25.2 → 4.26.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 (55) hide show
  1. package/EXPENSE_POLICY.md +105 -0
  2. package/GOVERNANCE.md +2 -103
  3. package/LICENSE +1 -1
  4. package/README.md +13 -9
  5. package/SECURITY.md +2 -157
  6. package/SPONSORS.md +20 -0
  7. package/build/build-validation.js +3 -1
  8. package/docs/Guides/Ecosystem.md +29 -9
  9. package/docs/Guides/Getting-Started.md +16 -3
  10. package/docs/Guides/Style-Guide.md +7 -7
  11. package/docs/Reference/Decorators.md +1 -1
  12. package/docs/Reference/Errors.md +63 -1
  13. package/docs/Reference/Hooks.md +1 -1
  14. package/docs/Reference/Logging.md +3 -3
  15. package/docs/Reference/Reply.md +70 -1
  16. package/docs/Reference/Server.md +90 -0
  17. package/docs/Reference/Warnings.md +17 -2
  18. package/fastify.d.ts +3 -2
  19. package/fastify.js +25 -7
  20. package/lib/configValidator.js +62 -33
  21. package/lib/contentTypeParser.js +9 -2
  22. package/lib/error-handler.js +1 -1
  23. package/lib/error-serializer.js +30 -29
  24. package/lib/errors.js +6 -1
  25. package/lib/fourOhFour.js +4 -3
  26. package/lib/hooks.js +1 -5
  27. package/lib/reply.js +68 -10
  28. package/lib/reqIdGenFactory.js +5 -0
  29. package/lib/route.js +22 -6
  30. package/lib/schema-controller.js +37 -4
  31. package/lib/symbols.js +1 -0
  32. package/lib/warnings.js +6 -0
  33. package/package.json +18 -6
  34. package/test/async_hooks.test.js +69 -0
  35. package/test/findRoute.test.js +135 -0
  36. package/test/genReqId.test.js +392 -0
  37. package/test/hooks.on-listen.test.js +66 -14
  38. package/test/hooks.on-ready.test.js +1 -1
  39. package/test/internals/errors.test.js +17 -7
  40. package/test/internals/initialConfig.test.js +7 -3
  41. package/test/internals/reply.test.js +80 -5
  42. package/test/plugin.4.test.js +3 -3
  43. package/test/pretty-print.test.js +1 -1
  44. package/test/schema-serialization.test.js +41 -0
  45. package/test/schema-validation.test.js +115 -6
  46. package/test/serialize-response.test.js +187 -0
  47. package/test/types/instance.test-d.ts +14 -1
  48. package/test/types/reply.test-d.ts +4 -2
  49. package/test/types/request.test-d.ts +1 -1
  50. package/test/types/route.test-d.ts +15 -1
  51. package/test/useSemicolonDelimiter.test.js +113 -0
  52. package/test/web-api.test.js +208 -0
  53. package/types/instance.d.ts +23 -10
  54. package/types/reply.d.ts +4 -0
  55. package/types/request.d.ts +5 -4
@@ -0,0 +1,113 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const Fastify = require('..')
6
+ const sget = require('simple-get').concat
7
+
8
+ test('use semicolon delimiter default true', t => {
9
+ t.plan(4)
10
+
11
+ const fastify = Fastify({})
12
+
13
+ t.teardown(fastify.close.bind(fastify))
14
+
15
+ fastify.get('/1234', (req, reply) => {
16
+ reply.send(req.query)
17
+ })
18
+
19
+ fastify.listen({ port: 0 }, err => {
20
+ t.error(err)
21
+
22
+ console.log('http://localhost:' + fastify.server.address().port + '/1234;foo=bar')
23
+ sget({
24
+ method: 'GET',
25
+ url: 'http://localhost:' + fastify.server.address().port + '/1234;foo=bar'
26
+ }, (err, response, body) => {
27
+ t.error(err)
28
+ t.equal(response.statusCode, 200)
29
+ t.same(JSON.parse(body), {
30
+ foo: 'bar'
31
+ })
32
+ })
33
+ })
34
+ })
35
+
36
+ test('use semicolon delimiter set to true', t => {
37
+ t.plan(4)
38
+
39
+ const fastify = Fastify({
40
+ useSemicolonDelimiter: true
41
+ })
42
+ t.teardown(fastify.close.bind(fastify))
43
+
44
+ fastify.get('/1234', (req, reply) => {
45
+ reply.send(req.query)
46
+ })
47
+
48
+ fastify.listen({ port: 0 }, err => {
49
+ t.error(err)
50
+
51
+ sget({
52
+ method: 'GET',
53
+ url: 'http://localhost:' + fastify.server.address().port + '/1234;foo=bar'
54
+ }, (err, response, body) => {
55
+ t.error(err)
56
+ t.equal(response.statusCode, 200)
57
+ t.same(JSON.parse(body), {
58
+ foo: 'bar'
59
+ })
60
+ })
61
+ })
62
+ })
63
+
64
+ test('use semicolon delimiter set to false', t => {
65
+ t.plan(4)
66
+
67
+ const fastify = Fastify({
68
+ useSemicolonDelimiter: false
69
+ })
70
+ t.teardown(fastify.close.bind(fastify))
71
+
72
+ fastify.get('/1234;foo=bar', (req, reply) => {
73
+ reply.send(req.query)
74
+ })
75
+
76
+ fastify.listen({ port: 0 }, err => {
77
+ t.error(err)
78
+
79
+ sget({
80
+ method: 'GET',
81
+ url: 'http://localhost:' + fastify.server.address().port + '/1234;foo=bar'
82
+ }, (err, response, body) => {
83
+ t.error(err)
84
+ t.equal(response.statusCode, 200)
85
+ t.same(JSON.parse(body), {})
86
+ })
87
+ })
88
+ })
89
+
90
+ test('use semicolon delimiter set to false return 404', t => {
91
+ t.plan(3)
92
+
93
+ const fastify = Fastify({
94
+ useSemicolonDelimiter: false
95
+ })
96
+ t.teardown(fastify.close.bind(fastify))
97
+
98
+ fastify.get('/1234', (req, reply) => {
99
+ reply.send(req.query)
100
+ })
101
+
102
+ fastify.listen({ port: 0 }, err => {
103
+ t.error(err)
104
+
105
+ sget({
106
+ method: 'GET',
107
+ url: 'http://localhost:' + fastify.server.address().port + '/1234;foo=bar'
108
+ }, (err, response, body) => {
109
+ t.error(err)
110
+ t.equal(response.statusCode, 404)
111
+ })
112
+ })
113
+ })
@@ -0,0 +1,208 @@
1
+ 'use strict'
2
+
3
+ const t = require('tap')
4
+ const test = t.test
5
+ const Fastify = require('../fastify')
6
+ const fs = require('node:fs')
7
+ const semver = require('semver')
8
+ const { Readable } = require('node:stream')
9
+ const { fetch: undiciFetch } = require('undici')
10
+
11
+ if (semver.lt(process.versions.node, '18.0.0')) {
12
+ t.skip('Response or ReadableStream not available, skipping test')
13
+ process.exit(0)
14
+ }
15
+
16
+ test('should response with a ReadableStream', async (t) => {
17
+ t.plan(2)
18
+
19
+ const fastify = Fastify()
20
+
21
+ fastify.get('/', function (request, reply) {
22
+ const stream = fs.createReadStream(__filename)
23
+ reply.code(200).send(Readable.toWeb(stream))
24
+ })
25
+
26
+ const {
27
+ statusCode,
28
+ body
29
+ } = await fastify.inject({ method: 'GET', path: '/' })
30
+
31
+ const expected = await fs.promises.readFile(__filename)
32
+
33
+ t.equal(statusCode, 200)
34
+ t.equal(expected.toString(), body.toString())
35
+ })
36
+
37
+ test('should response with a Response', async (t) => {
38
+ t.plan(3)
39
+
40
+ const fastify = Fastify()
41
+
42
+ fastify.get('/', function (request, reply) {
43
+ const stream = fs.createReadStream(__filename)
44
+ reply.send(new Response(Readable.toWeb(stream), {
45
+ status: 200,
46
+ headers: {
47
+ hello: 'world'
48
+ }
49
+ }))
50
+ })
51
+
52
+ const {
53
+ statusCode,
54
+ headers,
55
+ body
56
+ } = await fastify.inject({ method: 'GET', path: '/' })
57
+
58
+ const expected = await fs.promises.readFile(__filename)
59
+
60
+ t.equal(statusCode, 200)
61
+ t.equal(expected.toString(), body.toString())
62
+ t.equal(headers.hello, 'world')
63
+ })
64
+
65
+ test('able to use in onSend hook - ReadableStream', async (t) => {
66
+ t.plan(4)
67
+
68
+ const fastify = Fastify()
69
+
70
+ fastify.get('/', function (request, reply) {
71
+ const stream = fs.createReadStream(__filename)
72
+ reply.code(500).send(Readable.toWeb(stream))
73
+ })
74
+
75
+ fastify.addHook('onSend', (request, reply, payload, done) => {
76
+ t.equal(Object.prototype.toString.call(payload), '[object ReadableStream]')
77
+ done(null, new Response(payload, {
78
+ status: 200,
79
+ headers: {
80
+ hello: 'world'
81
+ }
82
+ }))
83
+ })
84
+
85
+ const {
86
+ statusCode,
87
+ headers,
88
+ body
89
+ } = await fastify.inject({ method: 'GET', path: '/' })
90
+
91
+ const expected = await fs.promises.readFile(__filename)
92
+
93
+ t.equal(statusCode, 200)
94
+ t.equal(expected.toString(), body.toString())
95
+ t.equal(headers.hello, 'world')
96
+ })
97
+
98
+ test('able to use in onSend hook - Response', async (t) => {
99
+ t.plan(4)
100
+
101
+ const fastify = Fastify()
102
+
103
+ fastify.get('/', function (request, reply) {
104
+ const stream = fs.createReadStream(__filename)
105
+ reply.send(new Response(Readable.toWeb(stream), {
106
+ status: 500,
107
+ headers: {
108
+ hello: 'world'
109
+ }
110
+ }))
111
+ })
112
+
113
+ fastify.addHook('onSend', (request, reply, payload, done) => {
114
+ t.equal(Object.prototype.toString.call(payload), '[object Response]')
115
+ done(null, new Response(payload.body, {
116
+ status: 200,
117
+ headers: payload.headers
118
+ }))
119
+ })
120
+
121
+ const {
122
+ statusCode,
123
+ headers,
124
+ body
125
+ } = await fastify.inject({ method: 'GET', path: '/' })
126
+
127
+ const expected = await fs.promises.readFile(__filename)
128
+
129
+ t.equal(statusCode, 200)
130
+ t.equal(expected.toString(), body.toString())
131
+ t.equal(headers.hello, 'world')
132
+ })
133
+
134
+ test('Error when Response.bodyUsed', async (t) => {
135
+ t.plan(4)
136
+
137
+ const expected = await fs.promises.readFile(__filename)
138
+
139
+ const fastify = Fastify()
140
+
141
+ fastify.get('/', async function (request, reply) {
142
+ const stream = fs.createReadStream(__filename)
143
+ const response = new Response(Readable.toWeb(stream), {
144
+ status: 200,
145
+ headers: {
146
+ hello: 'world'
147
+ }
148
+ })
149
+ const file = await response.text()
150
+ t.equal(expected.toString(), file)
151
+ t.equal(response.bodyUsed, true)
152
+ return reply.send(response)
153
+ })
154
+
155
+ const response = await fastify.inject({ method: 'GET', path: '/' })
156
+
157
+ t.equal(response.statusCode, 500)
158
+ const body = response.json()
159
+ t.equal(body.code, 'FST_ERR_REP_RESPONSE_BODY_CONSUMED')
160
+ })
161
+
162
+ test('allow to pipe with fetch', async (t) => {
163
+ t.plan(2)
164
+
165
+ const fastify = Fastify()
166
+ t.teardown(fastify.close.bind(fastify))
167
+
168
+ fastify.get('/', function (request, reply) {
169
+ return fetch(`${fastify.listeningOrigin}/fetch`, {
170
+ method: 'GET'
171
+ })
172
+ })
173
+
174
+ fastify.get('/fetch', function (request, reply) {
175
+ reply.code(200).send({ ok: true })
176
+ })
177
+
178
+ await fastify.listen()
179
+
180
+ const response = await fastify.inject({ method: 'GET', path: '/' })
181
+
182
+ t.equal(response.statusCode, 200)
183
+ t.same(response.json(), { ok: true })
184
+ })
185
+
186
+ test('allow to pipe with undici.fetch', async (t) => {
187
+ t.plan(2)
188
+
189
+ const fastify = Fastify()
190
+ t.teardown(fastify.close.bind(fastify))
191
+
192
+ fastify.get('/', function (request, reply) {
193
+ return undiciFetch(`${fastify.listeningOrigin}/fetch`, {
194
+ method: 'GET'
195
+ })
196
+ })
197
+
198
+ fastify.get('/fetch', function (request, reply) {
199
+ reply.code(200).send({ ok: true })
200
+ })
201
+
202
+ await fastify.listen()
203
+
204
+ const response = await fastify.inject({ method: 'GET', path: '/' })
205
+
206
+ t.equal(response.statusCode, 200)
207
+ t.same(response.json(), { ok: true })
208
+ })
@@ -1,14 +1,15 @@
1
1
  import { FastifyError } from '@fastify/error'
2
- import { ConstraintStrategy, HTTPVersion } from 'find-my-way'
2
+ import { ConstraintStrategy, FindResult, HTTPVersion } from 'find-my-way'
3
3
  import * as http from 'http'
4
- import { CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, InjectOptions, Response as LightMyRequestResponse } from 'light-my-request'
5
- import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, getDefaultJsonParser, hasContentTypeParser, ProtoAction, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser'
6
- import { onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onListenAsyncHookHandler, onListenHookHandler, onRegisterHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler, preCloseHookHandler, preCloseAsyncHookHandler, LifecycleHook, ApplicationHook, HookAsyncLookup, HookLookup } from './hooks'
4
+ import { InjectOptions, CallbackFunc as LightMyRequestCallback, Chain as LightMyRequestChain, Response as LightMyRequestResponse } from 'light-my-request'
5
+ import { AddressInfo } from 'net'
6
+ import { AddContentTypeParser, ConstructorAction, FastifyBodyParser, ProtoAction, getDefaultJsonParser, hasContentTypeParser, removeAllContentTypeParsers, removeContentTypeParser } from './content-type-parser'
7
+ import { ApplicationHook, HookAsyncLookup, HookLookup, LifecycleHook, onCloseAsyncHookHandler, onCloseHookHandler, onErrorAsyncHookHandler, onErrorHookHandler, onListenAsyncHookHandler, onListenHookHandler, onReadyAsyncHookHandler, onReadyHookHandler, onRegisterHookHandler, onRequestAbortAsyncHookHandler, onRequestAbortHookHandler, onRequestAsyncHookHandler, onRequestHookHandler, onResponseAsyncHookHandler, onResponseHookHandler, onRouteHookHandler, onSendAsyncHookHandler, onSendHookHandler, onTimeoutAsyncHookHandler, onTimeoutHookHandler, preCloseAsyncHookHandler, preCloseHookHandler, preHandlerAsyncHookHandler, preHandlerHookHandler, preParsingAsyncHookHandler, preParsingHookHandler, preSerializationAsyncHookHandler, preSerializationHookHandler, preValidationAsyncHookHandler, preValidationHookHandler } from './hooks'
7
8
  import { FastifyBaseLogger, FastifyChildLoggerFactory } from './logger'
8
9
  import { FastifyRegister } from './register'
9
10
  import { FastifyReply } from './reply'
10
11
  import { FastifyRequest } from './request'
11
- import { DefaultRoute, RouteGenericInterface, RouteOptions, RouteShorthandMethod, RouteHandlerMethod } from './route'
12
+ import { DefaultRoute, RouteGenericInterface, RouteHandlerMethod, RouteOptions, RouteShorthandMethod } from './route'
12
13
  import {
13
14
  FastifySchema,
14
15
  FastifySchemaCompiler,
@@ -20,8 +21,7 @@ import {
20
21
  FastifyTypeProvider,
21
22
  FastifyTypeProviderDefault
22
23
  } from './type-provider'
23
- import { HTTPMethods, ContextConfigDefault, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
24
- import { AddressInfo } from 'net'
24
+ import { ContextConfigDefault, HTTPMethods, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerBase, RawServerDefault } from './utils'
25
25
 
26
26
  export interface PrintRoutesOptions {
27
27
  method?: HTTPMethods;
@@ -86,6 +86,7 @@ export interface FastifyListenOptions {
86
86
 
87
87
  type NotInInterface<Key, _Interface> = Key extends keyof _Interface ? never : Key
88
88
  type FindMyWayVersion<RawServer extends RawServerBase> = RawServer extends http.Server ? HTTPVersion.V1 : HTTPVersion.V2
89
+ type FindMyWayFindResult<RawServer extends RawServerBase> = FindResult<FindMyWayVersion<RawServer>>
89
90
 
90
91
  type GetterSetter<This, T> = T | {
91
92
  getter: (this: This) => T,
@@ -219,6 +220,12 @@ export interface FastifyInstance<
219
220
  SchemaCompiler extends FastifySchema = FastifySchema,
220
221
  >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'method' | 'url' | 'constraints'>): boolean;
221
222
 
223
+ findRoute<
224
+ RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
225
+ ContextConfig = ContextConfigDefault,
226
+ SchemaCompiler extends FastifySchema = FastifySchema,
227
+ >(opts: Pick<RouteOptions<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>, 'method' | 'url' | 'constraints'>): Omit<FindMyWayFindResult<RawServer>, 'store'>;
228
+
222
229
  // addHook: overloads
223
230
 
224
231
  // Lifecycle addHooks
@@ -458,8 +465,8 @@ export interface FastifyInstance<
458
465
 
459
466
  setNotFoundHandler<RouteGeneric extends RouteGenericInterface = RouteGenericInterface, ContextConfig extends ContextConfigDefault = ContextConfigDefault, TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault, SchemaCompiler extends FastifySchema = FastifySchema> (
460
467
  opts: {
461
- preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
462
- preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
468
+ preValidation?: preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preValidationHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[] | preValidationAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
469
+ preHandler?: preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider> | preHandlerHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[] | preHandlerAsyncHookHandler<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>[];
463
470
  },
464
471
  handler: RouteHandlerMethod<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider, Logger>
465
472
  ): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>
@@ -476,6 +483,11 @@ export interface FastifyInstance<
476
483
  handler: (this: FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>, error: TError, request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider>, reply: FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfigDefault, SchemaCompiler, TypeProvider>) => any | Promise<any>
477
484
  ): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
478
485
 
486
+ /**
487
+ * Set a function that will generate a request-ids
488
+ */
489
+ setGenReqId(fn: (req: RawRequestDefaultExpression<RawServer>) => string): FastifyInstance<RawServer, RawRequest, RawReply, Logger, TypeProvider>;
490
+
479
491
  /**
480
492
  * Hook function that is called when creating a child logger instance for each request
481
493
  * which allows for modifying or adding child logger bindings and logger options, or
@@ -589,6 +601,7 @@ export interface FastifyInstance<
589
601
  pluginTimeout?: number,
590
602
  requestIdHeader?: string | false,
591
603
  requestIdLogLabel?: string,
592
- http2SessionTimeout?: number
604
+ http2SessionTimeout?: number,
605
+ useSemicolonDelimiter?: boolean,
593
606
  }>
594
607
  }
package/types/reply.d.ts CHANGED
@@ -40,6 +40,7 @@ export interface FastifyReply<
40
40
  > {
41
41
  raw: RawReply;
42
42
  context: FastifyReplyContext<ContextConfig>;
43
+ elapsedTime: number;
43
44
  log: FastifyBaseLogger;
44
45
  request: FastifyRequest<RouteGeneric, RawServer, RawRequest, SchemaCompiler, TypeProvider>;
45
46
  server: FastifyInstance;
@@ -59,6 +60,9 @@ export interface FastifyReply<
59
60
  redirect(url: string): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;
60
61
  hijack(): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;
61
62
  callNotFound(): void;
63
+ /**
64
+ * @deprecated Use the Reply#elapsedTime property instead
65
+ */
62
66
  getResponseTime(): number;
63
67
  type(contentType: string): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;
64
68
  serializer(fn: (payload: any) => string): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;
@@ -22,10 +22,11 @@ export interface ValidationFunction {
22
22
 
23
23
  export interface RequestRouteOptions<ContextConfig = ContextConfigDefault, SchemaCompiler = FastifySchema> {
24
24
  method: string;
25
- url: string;
26
- bodyLimit:number;
27
- attachValidation:boolean;
28
- logLevel:string;
25
+ // `url` can be `undefined` for instance when `request.is404` is true
26
+ url: string | undefined;
27
+ bodyLimit: number;
28
+ attachValidation: boolean;
29
+ logLevel: string;
29
30
  version: string | undefined;
30
31
  exposeHeadRoute: boolean;
31
32
  prefixTrailingSlash: string;