fastify 2.10.0 → 2.13.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/LICENSE +1 -1
- package/README.md +52 -28
- package/build/build-validation.js +12 -2
- package/docs/Decorators.md +136 -75
- package/docs/Ecosystem.md +13 -2
- package/docs/Errors.md +10 -0
- package/docs/Fluent-Schema.md +1 -3
- package/docs/Hooks.md +75 -37
- package/docs/Plugins-Guide.md +19 -2
- package/docs/Plugins.md +27 -0
- package/docs/Recommendations.md +159 -0
- package/docs/Reply.md +19 -2
- package/docs/Routes.md +96 -6
- package/docs/Server.md +86 -4
- package/docs/Serverless.md +1 -1
- package/docs/Testing.md +18 -3
- package/docs/TypeScript.md +37 -14
- package/docs/Validation-and-Serialization.md +214 -13
- package/fastify.d.ts +33 -40
- package/fastify.js +65 -6
- package/lib/configValidator.js +129 -52
- package/lib/contentTypeParser.js +4 -4
- package/lib/context.js +2 -1
- package/lib/decorate.js +13 -2
- package/lib/errors.js +8 -0
- package/lib/hooks.js +3 -1
- package/lib/logger.js +2 -2
- package/lib/reply.js +10 -4
- package/lib/route.js +37 -27
- package/lib/schemas.js +10 -2
- package/lib/server.js +11 -0
- package/lib/symbols.js +3 -1
- package/lib/validation.js +11 -5
- package/package.json +33 -29
- package/test/404s.test.js +40 -0
- package/test/decorator.test.js +80 -0
- package/test/esm/esm.mjs +13 -0
- package/test/esm/index.test.js +19 -0
- package/test/esm/other.mjs +7 -0
- package/test/esm/plugin.mjs +7 -0
- package/test/fastify-instance.test.js +29 -0
- package/test/genReqId.test.js +1 -1
- package/test/hooks-async.js +19 -0
- package/test/hooks.test.js +139 -7
- package/test/http2/closing.js +53 -0
- package/test/http2/plain.js +15 -0
- package/test/input-validation.js +63 -0
- package/test/input-validation.test.js +161 -0
- package/test/internals/decorator.test.js +37 -2
- package/test/internals/initialConfig.test.js +6 -2
- package/test/internals/reply.test.js +27 -3
- package/test/logger.test.js +310 -1
- package/test/proto-poisoning.test.js +76 -0
- package/test/reply-error.test.js +30 -0
- package/test/route-hooks.test.js +23 -14
- package/test/route.test.js +62 -24
- package/test/router-options.test.js +34 -0
- package/test/schemas.test.js +156 -0
- package/test/shared-schemas.test.js +65 -0
- package/test/types/index.ts +75 -2
package/docs/Hooks.md
CHANGED
|
@@ -7,14 +7,14 @@ Hooks are registered with the `fastify.addHook` method and allow you to listen t
|
|
|
7
7
|
By using hooks you can interact directly with the lifecycle of Fastify. There are Request/Reply hooks and application hooks:
|
|
8
8
|
|
|
9
9
|
- [Request/Reply Hooks](#requestreply-hooks)
|
|
10
|
-
- [onRequest](#
|
|
11
|
-
- [preParsing](#
|
|
12
|
-
- [preValidation](#
|
|
13
|
-
- [preHandler](#
|
|
14
|
-
- [preSerialization](#
|
|
15
|
-
- [onError](#
|
|
16
|
-
- [onSend](#
|
|
17
|
-
- [onResponse](#
|
|
10
|
+
- [onRequest](#onrequest)
|
|
11
|
+
- [preParsing](#preparsing)
|
|
12
|
+
- [preValidation](#prevalidation)
|
|
13
|
+
- [preHandler](#prehandler)
|
|
14
|
+
- [preSerialization](#preserialization)
|
|
15
|
+
- [onError](#onerror)
|
|
16
|
+
- [onSend](#onsend)
|
|
17
|
+
- [onResponse](#onresponse)
|
|
18
18
|
- [Application Hooks](#application-hooks)
|
|
19
19
|
- [onClose](#onclose)
|
|
20
20
|
- [onRoute](#onroute)
|
|
@@ -44,15 +44,11 @@ Or `async/await`:
|
|
|
44
44
|
fastify.addHook('onRequest', async (request, reply) => {
|
|
45
45
|
// Some code
|
|
46
46
|
await asyncMethod()
|
|
47
|
-
// Error occurred
|
|
48
|
-
if (err) {
|
|
49
|
-
throw new Error('Some errors occurred.')
|
|
50
|
-
}
|
|
51
47
|
return
|
|
52
48
|
})
|
|
53
49
|
```
|
|
54
50
|
|
|
55
|
-
**Notice:** in the [onRequest](#onRequest) hook, `request.body` will always be `null`, because the body parsing happens before the [
|
|
51
|
+
**Notice:** in the [onRequest](#onRequest) hook, `request.body` will always be `null`, because the body parsing happens before the [preValidation](#preValidation) hook.
|
|
56
52
|
|
|
57
53
|
### preParsing
|
|
58
54
|
```js
|
|
@@ -66,13 +62,12 @@ Or `async/await`:
|
|
|
66
62
|
fastify.addHook('preParsing', async (request, reply) => {
|
|
67
63
|
// Some code
|
|
68
64
|
await asyncMethod()
|
|
69
|
-
// Error occurred
|
|
70
|
-
if (err) {
|
|
71
|
-
throw new Error('Some errors occurred.')
|
|
72
|
-
}
|
|
73
65
|
return
|
|
74
66
|
})
|
|
75
67
|
```
|
|
68
|
+
|
|
69
|
+
**Notice:** in the [preParsing](#preParsing) hook, `request.body` will always be `null`, because the body parsing happens before the [preValidation](#preValidation) hook.
|
|
70
|
+
|
|
76
71
|
### preValidation
|
|
77
72
|
```js
|
|
78
73
|
fastify.addHook('preValidation', (request, reply, done) => {
|
|
@@ -85,14 +80,9 @@ Or `async/await`:
|
|
|
85
80
|
fastify.addHook('preValidation', async (request, reply) => {
|
|
86
81
|
// Some code
|
|
87
82
|
await asyncMethod()
|
|
88
|
-
// Error occurred
|
|
89
|
-
if (err) {
|
|
90
|
-
throw new Error('Some errors occurred.')
|
|
91
|
-
}
|
|
92
83
|
return
|
|
93
84
|
})
|
|
94
85
|
```
|
|
95
|
-
**Notice:** in the [preValidation](#preValidation) hook, `request.body` will always be `null`, because the body parsing happens before the [preHandler](#preHandler) hook.
|
|
96
86
|
|
|
97
87
|
### preHandler
|
|
98
88
|
```js
|
|
@@ -106,10 +96,6 @@ Or `async/await`:
|
|
|
106
96
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
107
97
|
// Some code
|
|
108
98
|
await asyncMethod()
|
|
109
|
-
// Error occurred
|
|
110
|
-
if (err) {
|
|
111
|
-
throw new Error('Some errors occurred.')
|
|
112
|
-
}
|
|
113
99
|
return
|
|
114
100
|
})
|
|
115
101
|
```
|
|
@@ -198,10 +184,6 @@ Or `async/await`:
|
|
|
198
184
|
fastify.addHook('onResponse', async (request, reply) => {
|
|
199
185
|
// Some code
|
|
200
186
|
await asyncMethod()
|
|
201
|
-
// Error occurred
|
|
202
|
-
if (err) {
|
|
203
|
-
throw new Error('Some errors occurred.')
|
|
204
|
-
}
|
|
205
187
|
return
|
|
206
188
|
})
|
|
207
189
|
```
|
|
@@ -228,7 +210,23 @@ fastify.addHook('preHandler', (request, reply, done) => {
|
|
|
228
210
|
*The error will be handled by [`Reply`](https://github.com/fastify/fastify/blob/master/docs/Reply.md#errors).*
|
|
229
211
|
|
|
230
212
|
### Respond to a request from a hook
|
|
231
|
-
|
|
213
|
+
|
|
214
|
+
If needed, you can respond to a request before you reach the route handler,
|
|
215
|
+
for example when implementing an authentication hook.
|
|
216
|
+
Replying from an hook implies that the hook chain is __stopped__ and
|
|
217
|
+
the rest of hooks and the handlers are not executed. If the hook is
|
|
218
|
+
using the callback approach, i.e. it is not an `async` function or it
|
|
219
|
+
returns a `Promise`, it is as simple as calling `reply.send()` and avoiding
|
|
220
|
+
calling the callback. If the hook is `async`, `reply.send()` __must__ be
|
|
221
|
+
called _before_ the function returns or the promise resolves, otherwise the
|
|
222
|
+
request will proceed. When `reply.send()` is called outside of the
|
|
223
|
+
promise chain, it is important to `return reply` otherwise the request
|
|
224
|
+
will be executed twice.
|
|
225
|
+
|
|
226
|
+
It is important to __not mix callbacks and `async`/`Promise`__, otherwise
|
|
227
|
+
the hook chain will be executed twice.
|
|
228
|
+
|
|
229
|
+
If you are using `onRequest` or `preHandler` use `reply.send`; if you are using a middleware, use `res.end`.
|
|
232
230
|
|
|
233
231
|
```js
|
|
234
232
|
fastify.addHook('onRequest', (request, reply, done) => {
|
|
@@ -237,7 +235,9 @@ fastify.addHook('onRequest', (request, reply, done) => {
|
|
|
237
235
|
|
|
238
236
|
// Works with async functions too
|
|
239
237
|
fastify.addHook('preHandler', async (request, reply) => {
|
|
238
|
+
await something()
|
|
240
239
|
reply.send({ hello: 'world' })
|
|
240
|
+
return reply // optional in this case, but it is a good practice
|
|
241
241
|
})
|
|
242
242
|
```
|
|
243
243
|
|
|
@@ -250,6 +250,26 @@ fastify.addHook('onRequest', (request, reply, done) => {
|
|
|
250
250
|
})
|
|
251
251
|
```
|
|
252
252
|
|
|
253
|
+
If you are sending a response without `await` on it, make sure to always
|
|
254
|
+
`return reply`:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
fastify.addHook('preHandler', async (request, reply) => {
|
|
258
|
+
setImmediate(() => { reply.send('hello') })
|
|
259
|
+
|
|
260
|
+
// This is needed to signal the handler to wait for a response
|
|
261
|
+
// to be sent outside of the promise chain
|
|
262
|
+
return reply
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
fastify.addHook('preHandler', async (request, reply) => {
|
|
266
|
+
// the fastify-static plugin will send a file asynchronously,
|
|
267
|
+
// so we should return reply
|
|
268
|
+
reply.sendFile('myfile')
|
|
269
|
+
return reply
|
|
270
|
+
})
|
|
271
|
+
```
|
|
272
|
+
|
|
253
273
|
## Application Hooks
|
|
254
274
|
|
|
255
275
|
You can hook into the application-lifecycle as well. It's important to note that these hooks aren't fully encapsulated. The `this` inside the hooks are encapsulated but the handlers can respond to an event outside the encapsulation boundaries.
|
|
@@ -280,9 +300,24 @@ fastify.addHook('onRoute', (routeOptions) => {
|
|
|
280
300
|
routeOptions.url
|
|
281
301
|
routeOptions.bodyLimit
|
|
282
302
|
routeOptions.logLevel
|
|
303
|
+
routeOptions.logSerializers
|
|
283
304
|
routeOptions.prefix
|
|
284
305
|
})
|
|
285
306
|
```
|
|
307
|
+
|
|
308
|
+
If you are authoring a plugin and you need to customize application routes, like modifying the options or adding new route hooks, this is the right place.
|
|
309
|
+
|
|
310
|
+
```js
|
|
311
|
+
fastify.addHook('onRoute', (routeOptions) => {
|
|
312
|
+
function onPreSerialization(request, reply, payload, done) {
|
|
313
|
+
// Your code
|
|
314
|
+
done(null, payload)
|
|
315
|
+
}
|
|
316
|
+
// preSerialization can be an array or undefined
|
|
317
|
+
routeOptions.preSerialization = [...(routeOptions.preSerialization || []), onPreSerialization]
|
|
318
|
+
})
|
|
319
|
+
```
|
|
320
|
+
|
|
286
321
|
<a name="on-register"></a>
|
|
287
322
|
### onRegister
|
|
288
323
|
Triggered when a new plugin is registered and a new encapsulation context is created. The hook will be executed **before** the registered code.<br/>
|
|
@@ -298,19 +333,22 @@ fastify.register(async (instance, opts) => {
|
|
|
298
333
|
instance.register(async (instance, opts) => {
|
|
299
334
|
instance.data.push('world')
|
|
300
335
|
console.log(instance.data) // ['hello', 'world']
|
|
301
|
-
})
|
|
302
|
-
})
|
|
336
|
+
}, { prefix: '/hola' })
|
|
337
|
+
}, { prefix: '/ciao' })
|
|
303
338
|
|
|
304
339
|
fastify.register(async (instance, opts) => {
|
|
305
340
|
console.log(instance.data) // []
|
|
306
|
-
})
|
|
341
|
+
}, { prefix: '/hello' })
|
|
307
342
|
|
|
308
|
-
fastify.addHook('onRegister', (instance) => {
|
|
343
|
+
fastify.addHook('onRegister', (instance, opts) => {
|
|
309
344
|
// Create a new array from the old one
|
|
310
345
|
// but without keeping the reference
|
|
311
346
|
// allowing the user to have encapsulated
|
|
312
347
|
// instances of the `data` property
|
|
313
348
|
instance.data = instance.data.slice()
|
|
349
|
+
|
|
350
|
+
// the options of the new registered instance
|
|
351
|
+
console.log(opts.prefix)
|
|
314
352
|
})
|
|
315
353
|
```
|
|
316
354
|
|
|
@@ -362,7 +400,7 @@ fastify.addHook('preHandler', (request, reply, done) => {
|
|
|
362
400
|
|
|
363
401
|
fastify.addHook('preSerialization', (request, reply, payload, done) => {
|
|
364
402
|
// Your code
|
|
365
|
-
done()
|
|
403
|
+
done(null, payload)
|
|
366
404
|
})
|
|
367
405
|
|
|
368
406
|
fastify.route({
|
|
@@ -396,7 +434,7 @@ fastify.route({
|
|
|
396
434
|
// done()
|
|
397
435
|
// }],
|
|
398
436
|
preSerialization: (request, reply, payload, done) => {
|
|
399
|
-
//
|
|
437
|
+
// This hook will always be executed after the shared `preSerialization` hooks
|
|
400
438
|
done(null, payload)
|
|
401
439
|
},
|
|
402
440
|
handler: function (request, reply) {
|
package/docs/Plugins-Guide.md
CHANGED
|
@@ -11,6 +11,7 @@ Fastify was built from the beginning to be an extremely modular system. We built
|
|
|
11
11
|
- [Hooks](#hooks)
|
|
12
12
|
- [Middlewares](#middlewares)
|
|
13
13
|
- [How to handle encapsulation and distribution](#distribution)
|
|
14
|
+
- [ESM support](#esm-support)
|
|
14
15
|
- [Handle errors](#handle-errors)
|
|
15
16
|
- [Let's start!](#start)
|
|
16
17
|
|
|
@@ -92,7 +93,7 @@ fastify.register((instance, opts, done) => {
|
|
|
92
93
|
Inside the second register call `instance.util` will throw an error, because `util` exists only inside the first register context.<br>
|
|
93
94
|
Let's step back for a moment and dig deeper into this: every time you use the `register` API, a new context is created which avoids the negative situations mentioned above.
|
|
94
95
|
|
|
95
|
-
Do note that encapsulation applies to the ancestors and siblings, but not the children.
|
|
96
|
+
Do note that encapsulation applies to the ancestors and siblings, but not the children.
|
|
96
97
|
```js
|
|
97
98
|
fastify.register((instance, opts, done) => {
|
|
98
99
|
instance.decorate('util', (a, b) => a + b)
|
|
@@ -180,7 +181,7 @@ We've seen how to extend server functionality and how to handle the encapsulatio
|
|
|
180
181
|
## Hooks
|
|
181
182
|
You just built an amazing utility, but now you need to execute that for every request, this is what you will likely do:
|
|
182
183
|
```js
|
|
183
|
-
fastify.decorate('util', (request, key, value) => { request
|
|
184
|
+
fastify.decorate('util', (request, key, value) => { request[key] = value })
|
|
184
185
|
|
|
185
186
|
fastify.get('/plugin1', (request, reply) => {
|
|
186
187
|
fastify.util(request, 'timestamp', new Date())
|
|
@@ -293,6 +294,22 @@ fastify.register(require('your-plugin'), parent => {
|
|
|
293
294
|
```
|
|
294
295
|
In the above example, the `parent` variable of the function passed in as the second argument of `register` is a copy of the **external fastify instance** that the plugin was registered at. This means that we are able to access any variables that were injected by preceding plugins in the order of declaration.
|
|
295
296
|
|
|
297
|
+
<a name="esm-support"></a>
|
|
298
|
+
## ESM support
|
|
299
|
+
|
|
300
|
+
ESM is supported as well from [Node.js `v13.3.0`](https://nodejs.org/api/esm.html) and above! Just export your plugin as ESM module and you are good to go!
|
|
301
|
+
|
|
302
|
+
```js
|
|
303
|
+
// plugin.mjs
|
|
304
|
+
async function plugin (fastify, opts) {
|
|
305
|
+
fastify.get('/', async (req, reply) => {
|
|
306
|
+
return { hello: 'world' }
|
|
307
|
+
})
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export default plugin
|
|
311
|
+
```
|
|
312
|
+
|
|
296
313
|
<a name="handle-errors"></a>
|
|
297
314
|
## Handle errors
|
|
298
315
|
It can happen that one of your plugins fails during startup. Maybe you expect it and you have a custom logic that will be triggered in that case. How can you implement this?
|
package/docs/Plugins.md
CHANGED
|
@@ -16,6 +16,7 @@ fastify.register(plugin, [options])
|
|
|
16
16
|
The optional `options` parameter for `fastify.register` supports a predefined set of options that Fastify itself will use, except when the plugin has been wrapped with [fastify-plugin](https://github.com/fastify/fastify-plugin). This options object will also be passed to the plugin upon invocation, regardless of whether or not the plugin has been wrapped. The currently supported list of Fastify specific options is:
|
|
17
17
|
|
|
18
18
|
+ [`logLevel`](https://github.com/fastify/fastify/blob/master/docs/Routes.md#custom-log-level)
|
|
19
|
+
+ [`logSerializers`](https://github.com/fastify/fastify/blob/master/docs/Routes.md#custom-log-serializer)
|
|
19
20
|
+ [`prefix`](https://github.com/fastify/fastify/blob/master/docs/Plugins.md#route-prefixing-options)
|
|
20
21
|
|
|
21
22
|
**Note: Those options will be ignored when used with fastify-plugin**
|
|
@@ -97,6 +98,32 @@ await fastify.ready()
|
|
|
97
98
|
|
|
98
99
|
await fastify.listen(3000)
|
|
99
100
|
```
|
|
101
|
+
|
|
102
|
+
<a name="esm-support"></a>
|
|
103
|
+
#### ESM support
|
|
104
|
+
|
|
105
|
+
ESM is supported as well from [Node.js `v13.3.0`](https://nodejs.org/api/esm.html) and above!
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
// main.mjs
|
|
109
|
+
import Fastify from 'fastify'
|
|
110
|
+
const fastify = Fastify()
|
|
111
|
+
|
|
112
|
+
fastify.register(import('./plugin.mjs'))
|
|
113
|
+
|
|
114
|
+
fastify.listen(3000, console.log)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
// plugin.mjs
|
|
118
|
+
async function plugin (fastify, opts) {
|
|
119
|
+
fastify.get('/', async (req, reply) => {
|
|
120
|
+
return { hello: 'world' }
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export default plugin
|
|
125
|
+
```
|
|
126
|
+
|
|
100
127
|
<a name="create-plugin"></a>
|
|
101
128
|
### Create a plugin
|
|
102
129
|
Creating a plugin is very easy, you just need to create a function that takes three parameters, the `fastify` instance, an `options` object and the `done` callback.<br>
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
<h1 align="center">Fastify</h1>
|
|
2
|
+
|
|
3
|
+
## Recommendations
|
|
4
|
+
|
|
5
|
+
This document contains a set recommendations, or best practices, when using
|
|
6
|
+
Fastify.
|
|
7
|
+
|
|
8
|
+
* [Use A Reverse Proxy](#reverseproxy)
|
|
9
|
+
|
|
10
|
+
## Use A Reverse Proxy
|
|
11
|
+
<a id="reverseproxy"></a>
|
|
12
|
+
|
|
13
|
+
Node.js is an early adopter of frameworks shipping with an easy to use web
|
|
14
|
+
server within the standard library. Previously, with languages like PHP or
|
|
15
|
+
Python, one would need either a web server with specific support for the
|
|
16
|
+
language or the ability to setup some sort of [CGI gateway][cgi] that works
|
|
17
|
+
with the language. With Node.js, one can simply write an application that
|
|
18
|
+
_directly_ handles HTTP requests. As a result, the temptation is to write
|
|
19
|
+
applications that handle requests for multiple domains, listen on multiple
|
|
20
|
+
ports (i.e. HTTP _and_ HTTPS), and various other scenarios and combinations
|
|
21
|
+
thereof. Further, the temptation is to then expose these applications directly
|
|
22
|
+
to the Internet to handle requests.
|
|
23
|
+
|
|
24
|
+
The Fastify team **strongly** considers this to be an anti-pattern and extremely
|
|
25
|
+
bad practice:
|
|
26
|
+
|
|
27
|
+
1. It adds unnecessary complexity to the application by diluting its focus.
|
|
28
|
+
2. It prevents [horizontal scalability][scale-horiz].
|
|
29
|
+
|
|
30
|
+
See [Why should I use a Reverse Proxy if Node.js is Production Ready?][why-use]
|
|
31
|
+
for a more thorough discussion of why one should opt to use a reverse proxy.
|
|
32
|
+
|
|
33
|
+
For a concrete example, consider the situation where:
|
|
34
|
+
|
|
35
|
+
1. The app needs multiple instances to handle load.
|
|
36
|
+
1. The app needs TLS termination.
|
|
37
|
+
1. The app needs to redirect HTTP requests to HTTPS.
|
|
38
|
+
1. The app needs to serve multiple domains.
|
|
39
|
+
1. The app needs to serve static resources, e.g. jpeg files.
|
|
40
|
+
|
|
41
|
+
There are many reverse proxy solutions available, and your environment may
|
|
42
|
+
dictate the solution to use, e.g. AWS or GCP. But given the above, we could use
|
|
43
|
+
[HAProxy][haproxy] to solve these requirements:
|
|
44
|
+
|
|
45
|
+
```conf
|
|
46
|
+
# The global section defines base HAProxy (engine) instance configuration.
|
|
47
|
+
global
|
|
48
|
+
log /dev/log syslog
|
|
49
|
+
maxconn 4096
|
|
50
|
+
chroot /var/lib/haproxy
|
|
51
|
+
user haproxy
|
|
52
|
+
group haproxy
|
|
53
|
+
|
|
54
|
+
# Set some baseline TLS options.
|
|
55
|
+
tune.ssl.default-dh-param 2048
|
|
56
|
+
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
|
|
57
|
+
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
|
|
58
|
+
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11
|
|
59
|
+
ssl-default-server-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
|
|
60
|
+
|
|
61
|
+
# Each defaults section defines options that will apply to each subsequent
|
|
62
|
+
# subsection until another defaults section is encountered.
|
|
63
|
+
defaults
|
|
64
|
+
log global
|
|
65
|
+
mode http
|
|
66
|
+
option httplog
|
|
67
|
+
option dontlognull
|
|
68
|
+
retries 3
|
|
69
|
+
option redispatch
|
|
70
|
+
maxconn 2000
|
|
71
|
+
timeout connect 5000
|
|
72
|
+
timeout client 50000
|
|
73
|
+
timeout server 50000
|
|
74
|
+
|
|
75
|
+
# Enable content compression for specific content types.
|
|
76
|
+
compression algo gzip
|
|
77
|
+
compression type text/html text/plain text/css application/javascript
|
|
78
|
+
|
|
79
|
+
# A "frontend" section defines a public listener, i.e. an "http server"
|
|
80
|
+
# as far as clients are concerned.
|
|
81
|
+
frontend proxy
|
|
82
|
+
# The IP address here would be the _public_ IP address of the server.
|
|
83
|
+
# Here, we use a private address as an example.
|
|
84
|
+
bind 10.0.0.10:80
|
|
85
|
+
# This redirect rule will redirect all traffic that is not TLS traffic
|
|
86
|
+
# to the same incoming request URL on the HTTPS port.
|
|
87
|
+
redirect scheme https code 308 if !{ ssl_fc }
|
|
88
|
+
# Technically this use_backend directive is useless since we are simply
|
|
89
|
+
# redirecting all traffic to this frontend to the HTTPS frontend. It is
|
|
90
|
+
# merely included here for completeness sake.
|
|
91
|
+
use_backend default-server
|
|
92
|
+
|
|
93
|
+
# This frontend defines our primary, TLS only, listener. It is here where
|
|
94
|
+
# we will define the TLS certificates to expose and how to direct incoming
|
|
95
|
+
# requests.
|
|
96
|
+
frontend proxy-ssl
|
|
97
|
+
# The `/etc/haproxy/certs` directory in this example contains a set of
|
|
98
|
+
# certificate PEM files that are named for the domains the certificates are
|
|
99
|
+
# issued for. When HAProxy starts, it will read this directory, load all of
|
|
100
|
+
# the certificates it finds here, and use SNI matching to apply the correct
|
|
101
|
+
# certificate to the connection.
|
|
102
|
+
bind 10.0.0.10:443 ssl crt /etc/haproxy/certs
|
|
103
|
+
|
|
104
|
+
# Here we define rule pairs to handle static resources. Any incoming request
|
|
105
|
+
# that has a path starting with `/static`, e.g.
|
|
106
|
+
# `https://one.example.com/static/foo.jpeg`, will be redirected to the
|
|
107
|
+
# static resources server.
|
|
108
|
+
acl is_static path -i -m beg /static
|
|
109
|
+
use_backend static-backend if is_static
|
|
110
|
+
|
|
111
|
+
# Here we define rule pairs to direct requests to appropriate Node.js
|
|
112
|
+
# servers based on the requested domain. The `acl` line is used to match
|
|
113
|
+
# the incoming hostname and define a boolean indicating if it is a match.
|
|
114
|
+
# The `use_backend` line is used to direct the traffic if the boolean is
|
|
115
|
+
# true.
|
|
116
|
+
acl example1 hdr_sub(Host) one.example.com
|
|
117
|
+
use_backend example1-backend if example1
|
|
118
|
+
|
|
119
|
+
acl example2 hdr_sub(Host) two.example.com
|
|
120
|
+
use_backend example2-backend if example2
|
|
121
|
+
|
|
122
|
+
# Finally, we have a fallback redirect if none of the requested hosts
|
|
123
|
+
# match the above rules.
|
|
124
|
+
default_backend default-server
|
|
125
|
+
|
|
126
|
+
# A "backend" is used to tell HAProxy where to request information for the
|
|
127
|
+
# proxied request. These sections are where we will define where our Node.js
|
|
128
|
+
# apps live and any other servers for things like static assets.
|
|
129
|
+
backend default-server
|
|
130
|
+
# In this example we are defaulting unmatched domain requests to a single
|
|
131
|
+
# backend server for all requests. Notice that the backend server does not
|
|
132
|
+
# have to be serving TLS requests. This is called "TLS termination": the TLS
|
|
133
|
+
# connection is "terminated" at the reverse proxy.
|
|
134
|
+
# It is possible to also proxy to backend servers that are themselves serving
|
|
135
|
+
# requests over TLS, but that is outside the scope of this example.
|
|
136
|
+
server server1 10.10.10.2:80
|
|
137
|
+
|
|
138
|
+
# This backend configuration will serve requests for `https://one.example.com`
|
|
139
|
+
# by proxying requests to three backend servers in a round-robin manner.
|
|
140
|
+
backend example1-backend
|
|
141
|
+
server example1-1 10.10.11.2:80
|
|
142
|
+
server example1-2 10.10.11.2:80
|
|
143
|
+
server example2-2 10.10.11.3:80
|
|
144
|
+
|
|
145
|
+
# This one serves requests for `https://two.example.com`
|
|
146
|
+
backend example2-backend
|
|
147
|
+
server example2-1 10.10.12.2:80
|
|
148
|
+
server example2-2 10.10.12.2:80
|
|
149
|
+
server example2-3 10.10.12.3:80
|
|
150
|
+
|
|
151
|
+
# This backend handles the static resources requests.
|
|
152
|
+
backend static-backend
|
|
153
|
+
server static-server1 10.10.9.2:80
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
[cgi]: https://en.wikipedia.org/wiki/Common_Gateway_Interface
|
|
157
|
+
[scale-horiz]: https://en.wikipedia.org/wiki/Scalability#Horizontal
|
|
158
|
+
[why-use]: https://web.archive.org/web/20190821102906/https://medium.com/intrinsic/why-should-i-use-a-reverse-proxy-if-node-js-is-production-ready-5a079408b2ca
|
|
159
|
+
[haproxy]: https://www.haproxy.org/
|
package/docs/Reply.md
CHANGED
|
@@ -122,12 +122,29 @@ reply.getHeader('x-foo') // undefined
|
|
|
122
122
|
Returns a boolean indicating if the specified header has been set.
|
|
123
123
|
|
|
124
124
|
<a name="redirect"></a>
|
|
125
|
-
### .redirect(dest)
|
|
125
|
+
### .redirect([code ,] dest)
|
|
126
126
|
Redirects a request to the specified url, the status code is optional, default to `302` (if status code is not already set by calling `code`).
|
|
127
|
+
|
|
128
|
+
Example (no `reply.code()` call) sets status code to `302` and redirects to `/home`
|
|
127
129
|
```js
|
|
128
130
|
reply.redirect('/home')
|
|
129
131
|
```
|
|
130
132
|
|
|
133
|
+
Example (no `reply.code()` call) sets status code to `303` and redirects to `/home`
|
|
134
|
+
```js
|
|
135
|
+
reply.redirect(303, '/home')
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Example (`reply.code()` call) sets status code to `303` and redirects to `/home`
|
|
139
|
+
```js
|
|
140
|
+
reply.code(303).redirect('/home')
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Example (`reply.code()` call) sets status code to `302` and redirects to `/home`
|
|
144
|
+
```js
|
|
145
|
+
reply.code(303).redirect(302, '/home')
|
|
146
|
+
```
|
|
147
|
+
|
|
131
148
|
<a name="call-not-found"></a>
|
|
132
149
|
### .callNotFound()
|
|
133
150
|
Invokes the custom not found handler.
|
|
@@ -321,7 +338,7 @@ fastify.get('/async-await', options, async function (request, reply) {
|
|
|
321
338
|
})
|
|
322
339
|
```
|
|
323
340
|
|
|
324
|
-
Rejected promises default to a `500` HTTP status code. Reject the promise, or `throw` in an `async function`, with an object that has `statusCode` (or `status`) and `message` properties to modify the reply.
|
|
341
|
+
Rejected promises default to a `500` HTTP status code. Reject the promise, or `throw` in an `async function`, with an _Error_ object that has `statusCode` (or `status`) and `message` properties to modify the reply. Throwing plain objects is not supported, it must be an instance of _Error_, see:
|
|
325
342
|
|
|
326
343
|
```js
|
|
327
344
|
fastify.get('/teapot', async function (request, reply) => {
|
package/docs/Routes.md
CHANGED
|
@@ -1,14 +1,34 @@
|
|
|
1
1
|
<h1 align="center">Fastify</h1>
|
|
2
2
|
|
|
3
3
|
## Routes
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
The routes methods will configure the endpoints of your application.
|
|
6
|
+
You have two ways to declare a route with Fastify, the shorthand method and the full declaration.
|
|
7
|
+
|
|
8
|
+
- [Full Declaration](#full-declaration)
|
|
9
|
+
- [Route Options](#options)
|
|
10
|
+
- [Shorthand Declaration](#shorthand-declaration)
|
|
11
|
+
- [URL Parameters](#url-building)
|
|
12
|
+
- [Use `async`/`await`](#async-await)
|
|
13
|
+
- [Promise resolution](#promise-resolution)
|
|
14
|
+
- [Route Prefixing](#route-prefixing)
|
|
15
|
+
- Logs
|
|
16
|
+
- [Custom Log Level](#custom-log-level)
|
|
17
|
+
- [Custom Log Serializer](#custom-log-serializer)
|
|
18
|
+
- [Route handler configuration](#routes-config)
|
|
19
|
+
- [Route's Versioning](#version)
|
|
20
|
+
|
|
5
21
|
<a name="full-declaration"></a>
|
|
6
22
|
### Full declaration
|
|
23
|
+
|
|
7
24
|
```js
|
|
8
25
|
fastify.route(options)
|
|
9
26
|
```
|
|
10
|
-
* `method`: currently it supports `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`, `'POST'`, `'PUT'` and `'OPTIONS'`. It could also be an array of methods.
|
|
11
27
|
|
|
28
|
+
<a name="options"></a>
|
|
29
|
+
### Routes option
|
|
30
|
+
|
|
31
|
+
* `method`: currently it supports `'DELETE'`, `'GET'`, `'HEAD'`, `'PATCH'`, `'POST'`, `'PUT'` and `'OPTIONS'`. It could also be an array of methods.
|
|
12
32
|
* `url`: the path of the url to match this route (alias: `path`).
|
|
13
33
|
* `schema`: an object containing the schemas for the request and response.
|
|
14
34
|
They need to be in
|
|
@@ -23,14 +43,18 @@ They need to be in
|
|
|
23
43
|
* `response`: filter and generate a schema for the response, setting a
|
|
24
44
|
schema allows us to have 10-20% more throughput.
|
|
25
45
|
* `attachValidation`: attach `validationError` to request, if there is a schema validation error, instead of sending the error to the error handler.
|
|
26
|
-
* `onRequest(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#
|
|
27
|
-
* `
|
|
28
|
-
* `
|
|
29
|
-
* `
|
|
46
|
+
* `onRequest(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#onrequest) as soon that a request is received, it could also be an array of functions.
|
|
47
|
+
* `preParsing(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#preparsing) called before parsing the request, it could also be an array of functions.
|
|
48
|
+
* `preValidation(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#prevalidation) called after the shared `preValidation` hooks, useful if you need to perform authentication at route level for example, it could also be an array of functions.
|
|
49
|
+
* `preHandler(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#prehandler) called just before the request handler, it could also be an array of functions.
|
|
50
|
+
* `preSerialization(request, reply, payload, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#preserialization) called just before the serialization, it could also be an array of functions.
|
|
51
|
+
* `onSend(request, reply, payload, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#route-hooks) called right before a response is sent, it could also be an array of functions.
|
|
52
|
+
* `onResponse(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#onresponse) called when a response has been sent, so you will not be able to send more data to the client. It could also be an array of functions.
|
|
30
53
|
* `handler(request, reply)`: the function that will handle this request.
|
|
31
54
|
* `schemaCompiler(schema)`: the function that build the schema for the validations. See [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#schema-compiler)
|
|
32
55
|
* `bodyLimit`: prevents the default JSON body parser from parsing request bodies larger than this number of bytes. Must be an integer. You may also set this option globally when first creating the Fastify instance with `fastify(options)`. Defaults to `1048576` (1 MiB).
|
|
33
56
|
* `logLevel`: set log level for this route. See below.
|
|
57
|
+
* `logSerializers`: set serializers to log for this route.
|
|
34
58
|
* `config`: object used to store custom configuration.
|
|
35
59
|
* `version`: a [semver](http://semver.org/) compatible string that defined the version of the endpoint. [Example](https://github.com/fastify/fastify/blob/master/docs/Routes.md#version).
|
|
36
60
|
* `prefixTrailingSlash`: string used to determine how to handle passing `/` as a route with a prefix.
|
|
@@ -237,6 +261,7 @@ fastify.register(require('./routes/v2/users'), { prefix: '/v2' })
|
|
|
237
261
|
|
|
238
262
|
fastify.listen(3000)
|
|
239
263
|
```
|
|
264
|
+
|
|
240
265
|
```js
|
|
241
266
|
// routes/v1/users.js
|
|
242
267
|
module.exports = function (fastify, opts, done) {
|
|
@@ -244,6 +269,7 @@ module.exports = function (fastify, opts, done) {
|
|
|
244
269
|
done()
|
|
245
270
|
}
|
|
246
271
|
```
|
|
272
|
+
|
|
247
273
|
```js
|
|
248
274
|
// routes/v2/users.js
|
|
249
275
|
module.exports = function (fastify, opts, done) {
|
|
@@ -286,6 +312,7 @@ fastify.register(require('./routes/events'), { logLevel: 'debug' })
|
|
|
286
312
|
|
|
287
313
|
fastify.listen(3000)
|
|
288
314
|
```
|
|
315
|
+
|
|
289
316
|
Or you can directly pass it to a route:
|
|
290
317
|
```js
|
|
291
318
|
fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
|
|
@@ -294,6 +321,65 @@ fastify.get('/', { logLevel: 'warn' }, (request, reply) => {
|
|
|
294
321
|
```
|
|
295
322
|
*Remember that the custom log level is applied only to the routes, and not to the global Fastify Logger, accessible with `fastify.log`*
|
|
296
323
|
|
|
324
|
+
<a name="custom-log-serializer"></a>
|
|
325
|
+
### Custom Log Serializer
|
|
326
|
+
|
|
327
|
+
In some context, you may need to log a large object but it could be a waste of resources for some routes. In this case, you can define some [`serializer`](https://github.com/pinojs/pino/blob/master/docs/api.md#bindingsserializers-object) and attach them in the right context!
|
|
328
|
+
|
|
329
|
+
```js
|
|
330
|
+
const fastify = require('fastify')({ logger: true })
|
|
331
|
+
|
|
332
|
+
fastify.register(require('./routes/user'), {
|
|
333
|
+
logSerializers: {
|
|
334
|
+
user: (value) => `My serializer one - ${value.name}`
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
fastify.register(require('./routes/events'), {
|
|
338
|
+
logSerializers: {
|
|
339
|
+
user: (value) => `My serializer two - ${value.name} ${value.surname}`
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
fastify.listen(3000)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
You can inherit serializers by context:
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
const fastify = Fastify({
|
|
350
|
+
logger: {
|
|
351
|
+
level: 'info',
|
|
352
|
+
serializers: {
|
|
353
|
+
user (req) {
|
|
354
|
+
return {
|
|
355
|
+
method: req.method,
|
|
356
|
+
url: req.url,
|
|
357
|
+
headers: req.headers,
|
|
358
|
+
hostname: req.hostname,
|
|
359
|
+
remoteAddress: req.ip,
|
|
360
|
+
remotePort: req.connection.remotePort
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
fastify.register(context1, {
|
|
368
|
+
logSerializers: {
|
|
369
|
+
user: value => `My serializer father - ${value}`
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
async function context1 (fastify, opts) {
|
|
374
|
+
fastify.get('/', (req, reply) => {
|
|
375
|
+
req.log.info({ user: 'call father serializer', key: 'another key' })
|
|
376
|
+
// shows: { user: 'My serializer father - call father serializer', key: 'another key' }
|
|
377
|
+
reply.send({})
|
|
378
|
+
})
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
fastify.listen(3000)
|
|
382
|
+
```
|
|
297
383
|
|
|
298
384
|
<a name="routes-config"></a>
|
|
299
385
|
### Config
|
|
@@ -315,10 +401,12 @@ fastify.listen(3000)
|
|
|
315
401
|
|
|
316
402
|
<a name="version"></a>
|
|
317
403
|
### Version
|
|
404
|
+
|
|
318
405
|
#### Default
|
|
319
406
|
If needed you can provide a version option, which will allow you to declare multiple versions of the same route. The versioning should follow the [semver](http://semver.org/) specification.<br/>
|
|
320
407
|
Fastify will automatically detect the `Accept-Version` header and route the request accordingly (advanced ranges and pre-releases currently are not supported).<br/>
|
|
321
408
|
*Be aware that using this feature will cause a degradation of the overall performances of the router.*
|
|
409
|
+
|
|
322
410
|
```js
|
|
323
411
|
fastify.route({
|
|
324
412
|
method: 'GET',
|
|
@@ -339,7 +427,9 @@ fastify.inject({
|
|
|
339
427
|
// { hello: 'world' }
|
|
340
428
|
})
|
|
341
429
|
```
|
|
430
|
+
|
|
342
431
|
If you declare multiple versions with the same major or minor, Fastify will always choose the highest compatible with the `Accept-Version` header value.<br/>
|
|
343
432
|
If the request will not have the `Accept-Version` header, a 404 error will be returned.
|
|
433
|
+
|
|
344
434
|
#### Custom
|
|
345
435
|
It's possible to define a custom versioning logic. This can be done through the [`versioning`](https://github.com/fastify/fastify/blob/master/docs/Server.md#versioning) configuration, when creating a fastify server instance.
|