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.
Files changed (60) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +52 -28
  3. package/build/build-validation.js +12 -2
  4. package/docs/Decorators.md +136 -75
  5. package/docs/Ecosystem.md +13 -2
  6. package/docs/Errors.md +10 -0
  7. package/docs/Fluent-Schema.md +1 -3
  8. package/docs/Hooks.md +75 -37
  9. package/docs/Plugins-Guide.md +19 -2
  10. package/docs/Plugins.md +27 -0
  11. package/docs/Recommendations.md +159 -0
  12. package/docs/Reply.md +19 -2
  13. package/docs/Routes.md +96 -6
  14. package/docs/Server.md +86 -4
  15. package/docs/Serverless.md +1 -1
  16. package/docs/Testing.md +18 -3
  17. package/docs/TypeScript.md +37 -14
  18. package/docs/Validation-and-Serialization.md +214 -13
  19. package/fastify.d.ts +33 -40
  20. package/fastify.js +65 -6
  21. package/lib/configValidator.js +129 -52
  22. package/lib/contentTypeParser.js +4 -4
  23. package/lib/context.js +2 -1
  24. package/lib/decorate.js +13 -2
  25. package/lib/errors.js +8 -0
  26. package/lib/hooks.js +3 -1
  27. package/lib/logger.js +2 -2
  28. package/lib/reply.js +10 -4
  29. package/lib/route.js +37 -27
  30. package/lib/schemas.js +10 -2
  31. package/lib/server.js +11 -0
  32. package/lib/symbols.js +3 -1
  33. package/lib/validation.js +11 -5
  34. package/package.json +33 -29
  35. package/test/404s.test.js +40 -0
  36. package/test/decorator.test.js +80 -0
  37. package/test/esm/esm.mjs +13 -0
  38. package/test/esm/index.test.js +19 -0
  39. package/test/esm/other.mjs +7 -0
  40. package/test/esm/plugin.mjs +7 -0
  41. package/test/fastify-instance.test.js +29 -0
  42. package/test/genReqId.test.js +1 -1
  43. package/test/hooks-async.js +19 -0
  44. package/test/hooks.test.js +139 -7
  45. package/test/http2/closing.js +53 -0
  46. package/test/http2/plain.js +15 -0
  47. package/test/input-validation.js +63 -0
  48. package/test/input-validation.test.js +161 -0
  49. package/test/internals/decorator.test.js +37 -2
  50. package/test/internals/initialConfig.test.js +6 -2
  51. package/test/internals/reply.test.js +27 -3
  52. package/test/logger.test.js +310 -1
  53. package/test/proto-poisoning.test.js +76 -0
  54. package/test/reply-error.test.js +30 -0
  55. package/test/route-hooks.test.js +23 -14
  56. package/test/route.test.js +62 -24
  57. package/test/router-options.test.js +34 -0
  58. package/test/schemas.test.js +156 -0
  59. package/test/shared-schemas.test.js +65 -0
  60. 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](#onRequest)
11
- - [preParsing](#preParsing)
12
- - [preValidation](#preValidation)
13
- - [preHandler](#preHandler)
14
- - [preSerialization](#preSerialization)
15
- - [onError](#onError)
16
- - [onSend](#onSend)
17
- - [onResponse](#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 [preHandler](#preHandler) hook.
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
- If needed, you can respond to a request before you reach the route handler, for example when implementing an authentication hook. If you are using `onRequest` or `preHandler` use `reply.send`; if you are using a middleware, use `res.end`.
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
- // Manipulate the payload
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) {
@@ -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.key = value })
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
- You have two ways to declare a route with Fastify, the shorthand method and the full declaration. Let's start with the second one:
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#route-hooks) as soon that a request is received, it could also be an array of functions.
27
- * `preValidation(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#route-hooks) 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.
28
- * `preHandler(request, reply, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#route-hooks) called just before the request handler, it could also be an array of functions.
29
- * `preSerialization(request, reply, payload, done)`: a [function](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#route-hooks) called just before the serialization, it could also be an array of functions.
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.