fastify 2.7.1 → 2.11.0
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 +15 -4
- package/build/build-validation.js +8 -0
- package/docs/Benchmarking.md +2 -2
- package/docs/ContentTypeParser.md +12 -10
- package/docs/Decorators.md +14 -14
- package/docs/Ecosystem.md +7 -1
- package/docs/Errors.md +13 -8
- package/docs/Fluent-Schema.md +9 -12
- package/docs/Getting-Started.md +29 -25
- package/docs/HTTP2.md +1 -1
- package/docs/Hooks.md +201 -186
- package/docs/LTS.md +6 -7
- package/docs/Logging.md +10 -10
- package/docs/Middleware.md +59 -0
- package/docs/Plugins-Guide.md +52 -52
- package/docs/Plugins.md +3 -0
- package/docs/Reply.md +47 -3
- package/docs/Routes.md +120 -8
- package/docs/Server.md +69 -3
- package/docs/Serverless.md +76 -4
- package/docs/TypeScript.md +33 -10
- package/docs/Validation-and-Serialization.md +137 -1
- package/examples/typescript-server.ts +1 -1
- package/fastify.d.ts +52 -13
- package/fastify.js +68 -7
- package/lib/configValidator.js +99 -52
- package/lib/contentTypeParser.js +4 -4
- package/lib/context.js +2 -1
- package/lib/errors.js +21 -18
- package/lib/fourOhFour.js +10 -10
- package/lib/handleRequest.js +1 -2
- package/lib/logger.js +2 -2
- package/lib/pluginUtils.js +32 -0
- package/lib/reply.js +41 -6
- package/lib/route.js +37 -9
- package/lib/schemas.js +23 -12
- package/lib/symbols.js +4 -1
- package/lib/validation.js +15 -9
- package/lib/wrapThenable.js +1 -1
- package/package.json +34 -26
- package/test/404s.test.js +41 -1
- package/test/async-await.js +66 -0
- package/test/custom-parser.test.js +1 -1
- package/test/custom-querystring-parser.test.js +1 -1
- package/test/decorator.test.js +48 -0
- package/test/emit-warning.test.js +3 -3
- package/test/fastify-instance.test.js +29 -0
- package/test/helper.js +7 -7
- package/test/hooks-async.js +4 -3
- package/test/hooks.test.js +27 -8
- package/test/input-validation.test.js +126 -0
- package/test/internals/errors.test.js +9 -1
- package/test/internals/initialConfig.test.js +4 -2
- package/test/internals/plugin.test.js +4 -4
- package/test/internals/reply.test.js +78 -6
- package/test/internals/schemas.test.js +30 -0
- package/test/internals/validation.test.js +18 -0
- package/test/listen.test.js +1 -1
- package/test/logger.test.js +314 -1
- package/test/plugin.test.js +171 -0
- package/test/promises.test.js +55 -0
- package/test/proto-poisoning.test.js +76 -0
- package/test/route-hooks.test.js +109 -91
- package/test/route-prefix.test.js +1 -1
- package/test/schemas.test.js +450 -0
- package/test/shared-schemas.test.js +2 -2
- package/test/stream.test.js +10 -6
- package/test/throw.test.js +48 -2
- package/test/types/index.ts +86 -1
- package/test/validation-error-handling.test.js +3 -3
- package/test/versioned-routes.test.js +1 -1
- package/docs/Middlewares.md +0 -59
package/test/stream.test.js
CHANGED
|
@@ -140,7 +140,7 @@ test('onSend hook stream', t => {
|
|
|
140
140
|
})
|
|
141
141
|
|
|
142
142
|
test('Destroying streams prematurely', t => {
|
|
143
|
-
t.plan(
|
|
143
|
+
t.plan(6)
|
|
144
144
|
|
|
145
145
|
let fastify = null
|
|
146
146
|
const logStream = split(JSON.parse)
|
|
@@ -190,7 +190,9 @@ test('Destroying streams prematurely', t => {
|
|
|
190
190
|
response.on('readable', function () {
|
|
191
191
|
response.destroy()
|
|
192
192
|
})
|
|
193
|
-
|
|
193
|
+
|
|
194
|
+
// Node bug? Node never emits 'close' here.
|
|
195
|
+
response.on('aborted', function () {
|
|
194
196
|
t.pass('Response closed')
|
|
195
197
|
})
|
|
196
198
|
})
|
|
@@ -198,7 +200,7 @@ test('Destroying streams prematurely', t => {
|
|
|
198
200
|
})
|
|
199
201
|
|
|
200
202
|
test('Destroying streams prematurely should call close method', t => {
|
|
201
|
-
t.plan(
|
|
203
|
+
t.plan(7)
|
|
202
204
|
|
|
203
205
|
let fastify = null
|
|
204
206
|
const logStream = split(JSON.parse)
|
|
@@ -249,7 +251,8 @@ test('Destroying streams prematurely should call close method', t => {
|
|
|
249
251
|
response.on('readable', function () {
|
|
250
252
|
response.destroy()
|
|
251
253
|
})
|
|
252
|
-
|
|
254
|
+
// Node bug? Node never emits 'close' here.
|
|
255
|
+
response.on('aborted', function () {
|
|
253
256
|
t.pass('Response closed')
|
|
254
257
|
})
|
|
255
258
|
})
|
|
@@ -257,7 +260,7 @@ test('Destroying streams prematurely should call close method', t => {
|
|
|
257
260
|
})
|
|
258
261
|
|
|
259
262
|
test('Destroying streams prematurely should call abort method', t => {
|
|
260
|
-
t.plan(
|
|
263
|
+
t.plan(7)
|
|
261
264
|
|
|
262
265
|
let fastify = null
|
|
263
266
|
const logStream = split(JSON.parse)
|
|
@@ -309,7 +312,8 @@ test('Destroying streams prematurely should call abort method', t => {
|
|
|
309
312
|
response.on('readable', function () {
|
|
310
313
|
response.destroy()
|
|
311
314
|
})
|
|
312
|
-
|
|
315
|
+
// Node bug? Node never emits 'close' here.
|
|
316
|
+
response.on('aborted', function () {
|
|
313
317
|
t.pass('Response closed')
|
|
314
318
|
})
|
|
315
319
|
})
|
package/test/throw.test.js
CHANGED
|
@@ -25,6 +25,52 @@ test('Fastify should throw on multiple assignment to the same route', t => {
|
|
|
25
25
|
})
|
|
26
26
|
})
|
|
27
27
|
|
|
28
|
+
test('Fastify should throw for an invalid schema, printing the error route - headers', t => {
|
|
29
|
+
t.plan(2)
|
|
30
|
+
|
|
31
|
+
const badSchema = {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
bad: {
|
|
35
|
+
type: 'bad-type'
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const fastify = Fastify()
|
|
41
|
+
fastify.get('/', { schema: { headers: badSchema } }, () => {})
|
|
42
|
+
fastify.get('/not-loaded', { schema: { headers: badSchema } }, () => {})
|
|
43
|
+
|
|
44
|
+
fastify.ready(err => {
|
|
45
|
+
t.is(err.code, 'FST_ERR_SCH_BUILD')
|
|
46
|
+
t.isLike(err.message, /Failed building the schema for GET: \//)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
test('Fastify should throw for an invalid schema, printing the error route - body', t => {
|
|
51
|
+
t.plan(2)
|
|
52
|
+
|
|
53
|
+
const badSchema = {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
bad: {
|
|
57
|
+
type: 'bad-type'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const fastify = Fastify()
|
|
63
|
+
fastify.register((instance, opts, next) => {
|
|
64
|
+
instance.post('/form', { schema: { body: badSchema } }, () => {})
|
|
65
|
+
next()
|
|
66
|
+
}, { prefix: 'hello' })
|
|
67
|
+
|
|
68
|
+
fastify.ready(err => {
|
|
69
|
+
t.is(err.code, 'FST_ERR_SCH_BUILD')
|
|
70
|
+
t.isLike(err.message, /Failed building the schema for POST: \/hello\/form/)
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
28
74
|
test('Should throw on unsupported method', t => {
|
|
29
75
|
t.plan(1)
|
|
30
76
|
const fastify = Fastify()
|
|
@@ -131,7 +177,7 @@ test('Should throw on duplicate request decorator', t => {
|
|
|
131
177
|
t.fail()
|
|
132
178
|
} catch (e) {
|
|
133
179
|
t.is(e.code, 'FST_ERR_DEC_ALREADY_PRESENT')
|
|
134
|
-
t.is(e.message,
|
|
180
|
+
t.is(e.message, "FST_ERR_DEC_ALREADY_PRESENT: The decorator 'foo' has already been added!")
|
|
135
181
|
}
|
|
136
182
|
})
|
|
137
183
|
|
|
@@ -146,7 +192,7 @@ test('Should throw if request decorator dependencies are not met', t => {
|
|
|
146
192
|
t.fail()
|
|
147
193
|
} catch (e) {
|
|
148
194
|
t.is(e.code, 'FST_ERR_DEC_MISSING_DEPENDENCY')
|
|
149
|
-
t.is(e.message,
|
|
195
|
+
t.is(e.message, "FST_ERR_DEC_MISSING_DEPENDENCY: The decorator is missing dependency 'world'.")
|
|
150
196
|
}
|
|
151
197
|
})
|
|
152
198
|
|
package/test/types/index.ts
CHANGED
|
@@ -64,7 +64,31 @@ const cors = require('cors')
|
|
|
64
64
|
maxParamLength: 200,
|
|
65
65
|
querystringParser: (str: string) => ({ str: str, strArray: [str] }),
|
|
66
66
|
modifyCoreObjects: true,
|
|
67
|
-
return503OnClosing: true
|
|
67
|
+
return503OnClosing: true,
|
|
68
|
+
genReqId: () => {
|
|
69
|
+
if (Math.random() > 0.5) {
|
|
70
|
+
return Math.random().toString()
|
|
71
|
+
}
|
|
72
|
+
return Math.random()
|
|
73
|
+
},
|
|
74
|
+
requestIdHeader: 'request-id',
|
|
75
|
+
requestIdLogLabel: 'reqId',
|
|
76
|
+
serverFactory: (handler, options) => {
|
|
77
|
+
const server = http.createServer((req, res) => {
|
|
78
|
+
handler(req, res)
|
|
79
|
+
})
|
|
80
|
+
return server
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// http2 server factory option
|
|
85
|
+
const otherHttp2Server = fastify({
|
|
86
|
+
serverFactory: (handler, options) => {
|
|
87
|
+
const server = http2.createServer((req, res) => {
|
|
88
|
+
handler(req, res)
|
|
89
|
+
})
|
|
90
|
+
return server
|
|
91
|
+
}
|
|
68
92
|
})
|
|
69
93
|
|
|
70
94
|
// custom types
|
|
@@ -102,6 +126,25 @@ server.use('/', (req, res, next) => {
|
|
|
102
126
|
console.log(`${req.method} ${req.url}`)
|
|
103
127
|
})
|
|
104
128
|
|
|
129
|
+
// Custom middleware with multiple paths
|
|
130
|
+
server.use(['/foo', '/bar'], (req, res, next) => {
|
|
131
|
+
console.log(`${req.method} ${req.url}`)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Third party plugin
|
|
135
|
+
// Also check if async functions are allowed to be passed to .register()
|
|
136
|
+
// https://github.com/fastify/fastify/pull/1841
|
|
137
|
+
// All function parameters should be inferrable and should not produce 'any'
|
|
138
|
+
const thirdPartyPlugin: fastify.Plugin<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse, {}> = (instance, options, callback) => {}
|
|
139
|
+
const thirdPartyPluginAsync: fastify.Plugin<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse, {}> = async (instance, options) => {}
|
|
140
|
+
|
|
141
|
+
server.register(thirdPartyPlugin)
|
|
142
|
+
server.register(thirdPartyPluginAsync)
|
|
143
|
+
|
|
144
|
+
// Custom plugin
|
|
145
|
+
server.register((instance, options, callback) => {})
|
|
146
|
+
server.register(async (instance, options) => {})
|
|
147
|
+
|
|
105
148
|
/**
|
|
106
149
|
* Test various hooks and different signatures
|
|
107
150
|
*/
|
|
@@ -117,6 +160,18 @@ server.addHook('preHandler', function (req, reply, next) {
|
|
|
117
160
|
}
|
|
118
161
|
})
|
|
119
162
|
|
|
163
|
+
server.addHook('preHandler', async function (req, reply) {
|
|
164
|
+
this.log.debug('`this` is not `any`')
|
|
165
|
+
if (req.body.error) {
|
|
166
|
+
throw new Error('testing if middleware errors can be passed')
|
|
167
|
+
} else {
|
|
168
|
+
// `stream` can be accessed correctly because `server` is an http2 server.
|
|
169
|
+
console.log('req stream', req.req.stream)
|
|
170
|
+
console.log('res stream', reply.res.stream)
|
|
171
|
+
reply.code(200).send('ok')
|
|
172
|
+
}
|
|
173
|
+
})
|
|
174
|
+
|
|
120
175
|
server.addHook('onRequest', function (req, reply, next) {
|
|
121
176
|
this.log.debug('`this` is not `any`')
|
|
122
177
|
console.log(`${req.raw.method} ${req.raw.url}`)
|
|
@@ -207,8 +262,13 @@ const opts: fastify.RouteShorthandOptions<http2.Http2SecureServer, http2.Http2Se
|
|
|
207
262
|
schemaCompiler: (schema: Object) => () => {},
|
|
208
263
|
bodyLimit: 5000,
|
|
209
264
|
logLevel: 'trace',
|
|
265
|
+
version: '1.0.0',
|
|
210
266
|
config: { }
|
|
211
267
|
}
|
|
268
|
+
const optsWithHandler: fastify.RouteShorthandOptions<http2.Http2SecureServer, http2.Http2ServerRequest, http2.Http2ServerResponse> = {
|
|
269
|
+
...opts,
|
|
270
|
+
handler (req, reply) { reply.send({ hello: 'route' }) }
|
|
271
|
+
}
|
|
212
272
|
|
|
213
273
|
// Chaining and route definitions
|
|
214
274
|
server
|
|
@@ -250,6 +310,7 @@ server
|
|
|
250
310
|
reply.header('Content-Type', 'application/json').code(200)
|
|
251
311
|
reply.send({ hello: 'world' })
|
|
252
312
|
})
|
|
313
|
+
.get('/optsWithHandler', optsWithHandler)
|
|
253
314
|
.get('/status', function (req, reply) {
|
|
254
315
|
reply.status(204).send()
|
|
255
316
|
})
|
|
@@ -279,17 +340,21 @@ server
|
|
|
279
340
|
.headers({ 'Content-Type': 'application/json' })
|
|
280
341
|
.send({ hello: 'world' })
|
|
281
342
|
})
|
|
343
|
+
.post('/optsWithHandler', optsWithHandler)
|
|
282
344
|
.head('/', {}, function (req, reply) {
|
|
283
345
|
reply.send()
|
|
284
346
|
})
|
|
347
|
+
.head('/optsWithHandler', optsWithHandler)
|
|
285
348
|
.delete('/', opts, function (req, reply) {
|
|
286
349
|
reply.send({ hello: 'world' })
|
|
287
350
|
})
|
|
351
|
+
.delete('/optsWithHandler', optsWithHandler)
|
|
288
352
|
.patch('/:id', opts, function (req, reply) {
|
|
289
353
|
req.log.info(`incoming id is ${req.params.id}`)
|
|
290
354
|
|
|
291
355
|
reply.send({ hello: 'world' })
|
|
292
356
|
})
|
|
357
|
+
.patch('/optsWithHandler', optsWithHandler)
|
|
293
358
|
.route({
|
|
294
359
|
method: ['GET', 'POST', 'PUT'],
|
|
295
360
|
url: '/multi-route',
|
|
@@ -317,6 +382,7 @@ server
|
|
|
317
382
|
.all('/all/with-opts', opts, function (req, reply) {
|
|
318
383
|
reply.send(req.headers)
|
|
319
384
|
})
|
|
385
|
+
.all('/optsWithHandler', optsWithHandler)
|
|
320
386
|
.route({
|
|
321
387
|
method: 'GET',
|
|
322
388
|
url: '/headers',
|
|
@@ -522,6 +588,13 @@ server.setSchemaCompiler(function (schema: object) {
|
|
|
522
588
|
return () => true
|
|
523
589
|
})
|
|
524
590
|
|
|
591
|
+
server.setSchemaResolver(function (ref: string) {
|
|
592
|
+
return {
|
|
593
|
+
$id: ref,
|
|
594
|
+
type: 'string'
|
|
595
|
+
}
|
|
596
|
+
})
|
|
597
|
+
|
|
525
598
|
server.addSchema({})
|
|
526
599
|
|
|
527
600
|
server.addContentTypeParser('*', (req, done) => {
|
|
@@ -635,3 +708,15 @@ server3.close(() => {})
|
|
|
635
708
|
done()
|
|
636
709
|
}
|
|
637
710
|
}
|
|
711
|
+
|
|
712
|
+
type TestReplyDecoration = (this: fastify.FastifyReply<http.ServerResponse>) => void
|
|
713
|
+
|
|
714
|
+
const server4 = fastify()
|
|
715
|
+
const testReplyDecoration: TestReplyDecoration = function () {
|
|
716
|
+
console.log('can access request from reply decorator', this.request.id)
|
|
717
|
+
}
|
|
718
|
+
server4.decorateReply('test-request-accessible-from-reply', testReplyDecoration)
|
|
719
|
+
|
|
720
|
+
server4.get('/', (req, reply) => {
|
|
721
|
+
reply.removeHeader('x-foo').removeHeader('x-bar').send({})
|
|
722
|
+
})
|
|
@@ -278,7 +278,7 @@ test('should return a defined output message parsing AJV errors', t => {
|
|
|
278
278
|
url: '/'
|
|
279
279
|
}, (err, res) => {
|
|
280
280
|
t.error(err)
|
|
281
|
-
t.strictEqual(res.payload,
|
|
281
|
+
t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body should have required property \'name\', body should have required property \'work\'"}')
|
|
282
282
|
})
|
|
283
283
|
})
|
|
284
284
|
|
|
@@ -306,7 +306,7 @@ test('should return a defined output message parsing JOI errors', t => {
|
|
|
306
306
|
url: '/'
|
|
307
307
|
}, (err, res) => {
|
|
308
308
|
t.error(err)
|
|
309
|
-
t.strictEqual(res.payload,
|
|
309
|
+
t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"child \\"name\\" fails because [\\"name\\" is required]"}')
|
|
310
310
|
})
|
|
311
311
|
})
|
|
312
312
|
|
|
@@ -337,6 +337,6 @@ test('should return a defined output message parsing JOI error details', t => {
|
|
|
337
337
|
url: '/'
|
|
338
338
|
}, (err, res) => {
|
|
339
339
|
t.error(err)
|
|
340
|
-
t.strictEqual(res.payload,
|
|
340
|
+
t.strictEqual(res.payload, '{"statusCode":400,"error":"Bad Request","message":"body \\"name\\" is required"}')
|
|
341
341
|
})
|
|
342
342
|
})
|
package/docs/Middlewares.md
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
<h1 align="center">Fastify</h1>
|
|
2
|
-
|
|
3
|
-
## Middlewares
|
|
4
|
-
|
|
5
|
-
Fastify out of the box provides an asynchronous [middleware engine](https://github.com/fastify/middie) compatible with [Express](https://expressjs.com/) and [Restify](http://restify.com/) middlewares.
|
|
6
|
-
|
|
7
|
-
*If you need a visual feedback to understand when the middlewares are executed take a look to the [lifecycle](https://github.com/fastify/fastify/blob/master/docs/Lifecycle.md) page.*
|
|
8
|
-
|
|
9
|
-
Fastify middlewares don't support the full syntax `middleware(err, req, res, next)`, because error handling is done inside Fastify.
|
|
10
|
-
Furthermore methods added by Express and Restify to the enhanced versions of `req` and `res` are not supported in Fastify middlewares.
|
|
11
|
-
|
|
12
|
-
Also, if you are using a middleware that bundles different, smaller middlewares, such as [*helmet*](https://helmetjs.github.io/), we recommend to use the single modules to get better performances.
|
|
13
|
-
|
|
14
|
-
```js
|
|
15
|
-
fastify.use(require('cors')())
|
|
16
|
-
fastify.use(require('dns-prefetch-control')())
|
|
17
|
-
fastify.use(require('frameguard')())
|
|
18
|
-
fastify.use(require('hide-powered-by')())
|
|
19
|
-
fastify.use(require('hsts')())
|
|
20
|
-
fastify.use(require('ienoopen')())
|
|
21
|
-
fastify.use(require('x-xss-protection')())
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
or, in the specific case of *helmet*, you can use the [*fastify-helmet*](https://github.com/fastify/fastify-helmet) [plugin](Plugins.md), which is an optimized helmet integration for fastify:
|
|
25
|
-
|
|
26
|
-
```js
|
|
27
|
-
const fastify = require('fastify')()
|
|
28
|
-
const helmet = require('fastify-helmet')
|
|
29
|
-
|
|
30
|
-
fastify.register(helmet)
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
Remember that middlewares can be encapsulated, this means that you can decide where your middlewares should run by using `register` as explained in the [plugins guide](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md).
|
|
34
|
-
|
|
35
|
-
Fastify middlewares also do not expose the `send` method or other methods specific to the Fastify [Reply]('./Reply.md' "Reply") instance. This is because Fastify wraps the incoming `req` and `res` Node instances using the [Request](./Request.md "Request") and [Reply](./Reply.md "Reply") objects internally, but this is done after the middlewares phase. If you need to create a middleware you have to use the Node `req` and `res` instances. Otherwise, you can use the `preHandler` hook that has the [Request](./Request.md "Request") and [Reply](./Reply.md "Reply") Fastify instances. For more information, see [Hooks](./Hooks.md "Hooks").
|
|
36
|
-
|
|
37
|
-
<a name="restrict-usage"></a>
|
|
38
|
-
#### Restrict middleware execution to a certain path(s)
|
|
39
|
-
If you need to run a middleware only under certain path(s), just pass the path as first parameter to `use` and you are done!
|
|
40
|
-
|
|
41
|
-
*Note that this does not support routes with parameters, (eg: `/user/:id/comments`) and wildcard is not supported in multiple paths.*
|
|
42
|
-
|
|
43
|
-
```js
|
|
44
|
-
const path = require('path')
|
|
45
|
-
const serveStatic = require('serve-static')
|
|
46
|
-
|
|
47
|
-
// Single path
|
|
48
|
-
fastify.use('/css', serveStatic(path.join(__dirname, '/assets')))
|
|
49
|
-
|
|
50
|
-
// Wildcard path
|
|
51
|
-
fastify.use('/css/*', serveStatic(path.join(__dirname, '/assets')))
|
|
52
|
-
|
|
53
|
-
// Multiple paths
|
|
54
|
-
fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets')))
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
<a name="express-middleware"></a>
|
|
58
|
-
#### Express middleware compatibility
|
|
59
|
-
Express modifies the prototype of the node core Request and Response objects heavily so Fastify cannot guarantee full middleware compatibility. Express specific functionality such as `res.sendFile()`, `res.send()` or `express.Router()` instances will not work with Fastify. For example, [cors](https://github.com/expressjs/cors) is compatible while [passport](https://github.com/jaredhanson/passport) is not.
|