fastify 2.4.1 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +1 -1
  2. package/SECURITY.md +33 -0
  3. package/build/build-validation.js +1 -1
  4. package/docs/Decorators.md +2 -2
  5. package/docs/Ecosystem.md +5 -1
  6. package/docs/Fluent-Schema.md +5 -7
  7. package/docs/Hooks.md +40 -40
  8. package/docs/Logging.md +15 -3
  9. package/docs/Plugins-Guide.md +21 -21
  10. package/docs/Plugins.md +11 -11
  11. package/docs/Reply.md +14 -7
  12. package/docs/Routes.md +6 -6
  13. package/docs/Server.md +40 -19
  14. package/docs/Serverless.md +127 -28
  15. package/docs/Validation-and-Serialization.md +15 -12
  16. package/fastify.d.ts +22 -14
  17. package/fastify.js +21 -0
  18. package/lib/context.js +5 -4
  19. package/lib/decorate.js +2 -0
  20. package/lib/errors.js +1 -0
  21. package/lib/handleRequest.js +2 -2
  22. package/lib/reply.js +23 -6
  23. package/lib/request.js +2 -2
  24. package/lib/route.js +24 -15
  25. package/lib/schemas.js +24 -3
  26. package/lib/symbols.js +1 -0
  27. package/lib/validation.js +19 -0
  28. package/package.json +19 -18
  29. package/test/async-await.js +1 -1
  30. package/test/close-pipelining.test.js +43 -2
  31. package/test/close.test.js +69 -12
  32. package/test/content-length.test.js +2 -2
  33. package/test/decorator.test.js +2 -0
  34. package/test/fluent-schema.js +54 -0
  35. package/test/hooks-async.js +80 -1
  36. package/test/hooks.test.js +4 -4
  37. package/test/http2/closing.js +86 -33
  38. package/test/input-validation.test.js +1 -1
  39. package/test/internals/decorator.test.js +2 -0
  40. package/test/internals/initialConfig.test.js +1 -1
  41. package/test/internals/reply.test.js +276 -1
  42. package/test/internals/validation.test.js +57 -0
  43. package/test/logger.test.js +33 -1
  44. package/test/nullable-validation.test.js +52 -0
  45. package/test/plugin.test.js +2 -0
  46. package/test/register.test.js +2 -0
  47. package/test/route-prefix.test.js +31 -0
  48. package/test/route.test.js +35 -0
  49. package/test/shared-schemas.test.js +3 -3
  50. package/test/types/index.ts +33 -2
  51. package/test/versioned-routes.test.js +3 -3
package/docs/Routes.md CHANGED
@@ -16,7 +16,7 @@ They need to be in
16
16
 
17
17
  * `body`: validates the body of the request if it is a POST or a
18
18
  PUT.
19
- * `querystring`: validates the querystring. This can be a complete JSON
19
+ * `querystring` or `query`: validates the querystring. This can be a complete JSON
20
20
  Schema object, with the property `type` of `object` and `properties` object of parameters, or
21
21
  simply the values of what would be contained in the `properties` object as shown below.
22
22
  * `params`: validates the params.
@@ -216,16 +216,16 @@ fastify.listen(3000)
216
216
  ```
217
217
  ```js
218
218
  // routes/v1/users.js
219
- module.exports = function (fastify, opts, next) {
219
+ module.exports = function (fastify, opts, done) {
220
220
  fastify.get('/user', handler_v1)
221
- next()
221
+ done()
222
222
  }
223
223
  ```
224
224
  ```js
225
225
  // routes/v2/users.js
226
- module.exports = function (fastify, opts, next) {
226
+ module.exports = function (fastify, opts, done) {
227
227
  fastify.get('/user', handler_v2)
228
- next()
228
+ done()
229
229
  }
230
230
  ```
231
231
  Fastify will not complain because you are using the same name for two different routes, because at compilation time it will handle the prefix automatically *(this also means that the performance will not be affected at all!)*.
@@ -249,7 +249,7 @@ See the `prefixTrailingSlash` route option above to change this behaviour.
249
249
 
250
250
  <a name="custom-log-level"></a>
251
251
  ### Custom Log Level
252
- It could happen that you need different log levels in your routes, with Fastify achieve this is very straightforward.<br/>
252
+ It could happen that you need different log levels in your routes, Fastify achieves this in a very straightforward way.<br/>
253
253
  You just need to pass the option `logLevel` to the plugin option or the route option with the [value](https://github.com/pinojs/pino/blob/master/docs/API.md#discussion-3) that you need.
254
254
 
255
255
  Be aware that if you set the `logLevel` at plugin level, also the [`setNotFoundHandler`](https://github.com/fastify/fastify/blob/master/docs/Server.md#setnotfoundhandler) and [`setErrorHandler`](https://github.com/fastify/fastify/blob/master/docs/Server.md#seterrorhandler) will be affected.
package/docs/Server.md CHANGED
@@ -150,14 +150,14 @@ custom `onRequest` and `onResponse` hooks.
150
150
 
151
151
  ```js
152
152
  // Examples of hooks to replicate the disabled functionality.
153
- fastify.addHook('onRequest', (req, reply, next) => {
153
+ fastify.addHook('onRequest', (req, reply, done) => {
154
154
  req.log.info({ url: req.req.url, id: req.id }, 'received request')
155
- next()
155
+ done()
156
156
  })
157
157
 
158
- fastify.addHook('onResponse', (req, reply, next) => {
158
+ fastify.addHook('onResponse', (req, reply, done) => {
159
159
  req.log.info({ url: req.req.originalUrl, statusCode: res.res.statusCode }, 'request completed')
160
- next()
160
+ done()
161
161
  })
162
162
  ```
163
163
 
@@ -230,7 +230,7 @@ Especially in distributed systems, you may want to override the default id gener
230
230
  ```js
231
231
  let i = 0
232
232
  const fastify = require('fastify')({
233
- genReqId: function (req) { return req.headers['request-id'] || i++ }
233
+ genReqId: function (req) { return i++ }
234
234
  })
235
235
  ```
236
236
 
@@ -352,6 +352,14 @@ fastify.get('/', (request, reply) => {
352
352
  })
353
353
  ```
354
354
 
355
+ <a name="factory-return-503-on-closing"></a>
356
+ ### `return503OnClosing`
357
+
358
+ Returns 503 after calling `close` server method.
359
+ If `false`, the server routes the incoming request as usual.
360
+
361
+ + Default: `true`
362
+
355
363
  ## Instance
356
364
 
357
365
  ### Server Methods
@@ -368,16 +376,16 @@ It is always executed before the method `fastify.ready`.
368
376
 
369
377
  ```js
370
378
  fastify
371
- .register((instance, opts, next) => {
379
+ .register((instance, opts, done) => {
372
380
  console.log('Current plugin')
373
- next()
381
+ done()
374
382
  })
375
383
  .after(err => {
376
384
  console.log('After current plugin')
377
385
  })
378
- .register((instance, opts, next) => {
386
+ .register((instance, opts, done) => {
379
387
  console.log('Next plugin')
380
- next()
388
+ done()
381
389
  })
382
390
  .ready(err => {
383
391
  console.log('Everything has been loaded')
@@ -516,6 +524,7 @@ Method to add routes to the server, it also has shorthand functions, check [here
516
524
  #### close
517
525
  `fastify.close(callback)`: call this function to close the server instance and run the [`'onClose'`](https://github.com/fastify/fastify/blob/master/docs/Hooks.md#on-close) hook.<br>
518
526
  Calling `close` will also cause the server to respond to every new incoming request with a `503` error and destroy that request.
527
+ See [`return503OnClosing` flags](https://github.com/fastify/fastify/blob/master/docs/Server.md#factory-return-503-on-closing) for changing this behaviour.
519
528
 
520
529
  If it is called without any arguments, it will return a Promise:
521
530
 
@@ -551,24 +560,24 @@ The full path that will be prefixed to a route.
551
560
  Example:
552
561
 
553
562
  ```js
554
- fastify.register(function (instance, opts, next) {
563
+ fastify.register(function (instance, opts, done) {
555
564
  instance.get('/foo', function (request, reply) {
556
565
  // Will log "prefix: /v1"
557
566
  request.log.info('prefix: %s', instance.prefix)
558
567
  reply.send({ prefix: instance.prefix })
559
568
  })
560
569
 
561
- instance.register(function (instance, opts, next) {
570
+ instance.register(function (instance, opts, done) {
562
571
  instance.get('/bar', function (request, reply) {
563
572
  // Will log "prefix: /v1/v2"
564
573
  request.log.info('prefix: %s', instance.prefix)
565
574
  reply.send({ prefix: instance.prefix })
566
575
  })
567
576
 
568
- next()
577
+ done()
569
578
  }, { prefix: '/v2' })
570
579
 
571
- next()
580
+ done()
572
581
  }, { prefix: '/v1' })
573
582
  ```
574
583
 
@@ -585,6 +594,18 @@ Fake http injection (for testing purposes) [here](https://github.com/fastify/fas
585
594
  `fastify.addSchema(schemaObj)`, adds a shared schema to the Fastify instance. This allows you to reuse it everywhere in your application just by writing the schema id that you need.<br/>
586
595
  To learn more, see [shared schema example](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#shared-schema) in the [Validation and Serialization](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md) documentation.
587
596
 
597
+ <a name="set-reply-serializer"></a>
598
+ #### setReplySerializer
599
+ Set the reply serializer for all the routes. This will used as default if a [Reply.serializer(func)](https://github.com/fastify/fastify/blob/master/docs/Reply.md#serializerfunc) has not been set. The handler is fully encapsulated, so different plugins can set different error handlers.
600
+ Note: the function parameter is called only for status `2xx`. Checkout the [`setErrorHandler`](https://github.com/fastify/fastify/blob/master/docs/Server.md#seterrorhandler) for errors.
601
+
602
+ ```js
603
+ fastify.setReplySerializer(function (payload, statusCode){
604
+ // serialize the payload with a sync function
605
+ return `my serialized ${statusCode} content: ${payload}`
606
+ })
607
+ ```
608
+
588
609
  <a name="set-schema-compiler"></a>
589
610
  #### setSchemaCompiler
590
611
  Set the schema compiler for all routes [here](https://github.com/fastify/fastify/blob/master/docs/Validation-and-Serialization.md#schema-compiler).
@@ -602,24 +623,24 @@ You can also register a [`preValidation`](https://www.fastify.io/docs/latest/Hoo
602
623
 
603
624
  ```js
604
625
  fastify.setNotFoundHandler({
605
- preValidation: (req, reply, next) => {
626
+ preValidation: (req, reply, done) => {
606
627
  // your code
607
- next()
628
+ done()
608
629
  },
609
- preHandler: (req, reply, next) => {
630
+ preHandler: (req, reply, done) => {
610
631
  // your code
611
- next()
632
+ done()
612
633
  }
613
634
  }, function (request, reply) {
614
635
  // Default not found handler with preValidation and preHandler hooks
615
636
  })
616
637
 
617
- fastify.register(function (instance, options, next) {
638
+ fastify.register(function (instance, options, done) {
618
639
  instance.setNotFoundHandler(function (request, reply) {
619
640
  // Handle not found request without preValidation and preHandler hooks
620
641
  // to URLs that begin with '/v1'
621
642
  })
622
- next()
643
+ done()
623
644
  }, { prefix: '/v1' })
624
645
  ```
625
646
 
@@ -2,6 +2,11 @@
2
2
 
3
3
  Run serverless applications and REST APIs using your existing Fastify application.
4
4
 
5
+ ### Contents
6
+
7
+ - [AWS Lambda](#aws-lambda)
8
+ - [Google Cloud Run](#google-cloud-run)
9
+
5
10
  ### Attention Readers:
6
11
  > Fastify is not designed to run on serverless environments.
7
12
  The Fastify framework is designed to make implementing a traditional HTTP/S server easy.
@@ -12,21 +17,20 @@ it is possible to use Fastify in a serverless environment.
12
17
  Again, keep in mind that this is not Fastify's intended use case and
13
18
  we do not test for such integration scenarios.
14
19
 
15
-
16
20
  ## AWS Lambda
17
21
 
18
22
  The sample provided allows you to easily build serverless web applications/services
19
23
  and RESTful APIs using Fastify on top of AWS Lambda and Amazon API Gateway.
20
24
 
21
- *Note: This is just one possible way.*
25
+ *Note: Using [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify) is just one possible way.*
22
26
 
23
27
  ### app.js
24
28
 
25
29
  ```js
26
30
  const fastify = require('fastify');
27
31
 
28
- function init(serverFactory) {
29
- const app = fastify({ serverFactory });
32
+ function init() {
33
+ const app = fastify();
30
34
  app.get('/', (request, reply) => reply.send({ hello: 'world' }));
31
35
  return app;
32
36
  }
@@ -43,11 +47,9 @@ if (require.main !== module) {
43
47
  }
44
48
  ```
45
49
 
46
- You can simply wrap your initialization code by offering to inject an optional [serverFactory](https://www.fastify.io/docs/latest/Server/#serverfactory).
47
-
48
50
  When executed in your lambda function we don't need to listen to a specific port,
49
51
  so we just export the wrapper function `init` in this case.
50
- The [`lambda.js`](https://www.fastify.io/docs/latest/Server/#lambda.js) file will use this export.
52
+ The [`lambda.js`](https://www.fastify.io/docs/latest/Serverless/#lambda-js) file will use this export.
51
53
 
52
54
  When you execute your Fastify application like always,
53
55
  i.e. `node app.js` *(the detection for this could be `require.main === module`)*,
@@ -56,31 +58,28 @@ you can normally listen to your port, so you can still run your Fastify function
56
58
  ### lambda.js
57
59
 
58
60
  ```js
59
- const awsServerlessExpress = require('aws-serverless-express');
61
+ const awsLambdaFastify = require('aws-lambda-fastify')
60
62
  const init = require('./app');
61
63
 
62
- let server;
63
- const serverFactory = (handler) => {
64
- server = awsServerlessExpress.createServer(handler);
65
- return server;
66
- }
67
- const app = init(serverFactory);
68
-
69
- exports.handler = (event, context, callback) => {
70
- context.callbackWaitsForEmptyEventLoop = false;
71
- app.ready((e) => {
72
- if (e) return console.error(e.stack || e);
73
- awsServerlessExpress.proxy(server, event, context, 'CALLBACK', callback);
74
- });
75
- };
64
+ const proxy = awsLambdaFastify(init())
65
+ // or
66
+ // const proxy = awsLambdaFastify(init(), { binaryMimeTypes: ['application/octet-stream'] })
67
+
68
+ exports.handler = proxy;
69
+ // or
70
+ // exports.handler = (event, context, callback) => proxy(event, context, callback);
71
+ // or
72
+ // exports.handler = (event, context) => proxy(event, context);
73
+ // or
74
+ // exports.handler = async (event, context) => proxy(event, context);
76
75
  ```
77
76
 
78
- We define a custom `serverFactory` function, in which we create a new server with the help of [`aws-serverless-express`](https://github.com/awslabs/aws-serverless-express)
79
- (make sure you install the dependency `npm i --save aws-serverless-express`).
80
- Then we call the `init` function (imported from [`app.js`](https://www.fastify.io/docs/latest/Server/#app.js)) with the `serverFactory` function as the only parameter.
81
- Finally inside the lambda `handler` function we wait for the Fastify app to be `ready`
82
- and proxy all the incoming events (API Gateway requests) to the `proxy` function from [`aws-serverless-express`](https://github.com/awslabs/aws-serverless-express).
83
-
77
+ We just require [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify)
78
+ (make sure you install the dependency `npm i --save aws-lambda-fastify`) and our
79
+ [`app.js`](https://www.fastify.io/docs/latest/Serverless/#app-js) file and call the
80
+ exported `awsLambdaFastify` function with the `app` as the only parameter.
81
+ The resulting `proxy` function has the correct signature to be used as lambda `handler` function.
82
+ This way all the incoming events (API Gateway requests) are passed to the `proxy` function of [aws-lambda-fastify](https://github.com/fastify/aws-lambda-fastify).
84
83
 
85
84
  ### Example
86
85
 
@@ -91,3 +90,103 @@ An example deployable with [claudia.js](https://claudiajs.com/tutorials/serverle
91
90
 
92
91
  - API Gateway doesn't support streams yet, so you're not able to handle [streams](https://www.fastify.io/docs/latest/Reply/#streams).
93
92
  - API Gateway has a timeout of 29 seconds, so it's important to provide a reply during this time.
93
+
94
+ ## Google Cloud Run
95
+
96
+ Unlike AWS Lambda or Google Cloud Functions, Google Cloud Run is a serverless **container** environment. It's primary purpose is to provide an infrastucture-abstracted environment to run arbitrary containers. As a result, Fastify can be deployed to Google Cloud Run with little-to-no code changes from the way you would write your Fastify app normally.
97
+
98
+ *Follow the steps below to deploy to Google Cloud Run if you are already familiar with gcloud or just follow their [quickstart](https://cloud.google.com/run/docs/quickstarts/build-and-deploy)*
99
+
100
+ ### Adjust Fastfiy server
101
+
102
+ In order for Fastify to properly listen for requests within the container, be sure to set the correct port and address:
103
+
104
+ ```js
105
+ function build() {
106
+ const fastify = Fastify({ trustProxy: true })
107
+ return fastify
108
+ }
109
+
110
+ async function start() {
111
+ // Google Cloud Run will set this environment variable for you, so
112
+ // you can also use it to detect if you are running in Cloud Run
113
+ const IS_GOOGLE_CLOUD_RUN = process.env.K_SERVICE !== undefined
114
+
115
+ // You must listen on the port Cloud Run provides
116
+ const port = process.env.PORT || 3000
117
+
118
+ // You must listen on all IPV4 addresses in Cloud Run
119
+ const address = IS_GOOGLE_CLOUD_RUN ? "0.0.0.0" : undefined
120
+
121
+ try {
122
+ const server = build()
123
+ const address = await server.listen(port, address)
124
+ console.log(`Listening on ${address}`)
125
+ } catch (err) {
126
+ console.error(err)
127
+ process.exit(1)
128
+ }
129
+ }
130
+
131
+ module.exports = build
132
+
133
+ if (require.main === module) {
134
+ start()
135
+ }
136
+ ```
137
+
138
+ ### Add a Dockerfile
139
+
140
+ You can add any valid `Dockerfile` that packages and runs a Node app. A basic `Dockerfile` can be found in the official [gcloud docs](https://github.com/knative/docs/blob/2d654d1fd6311750cc57187a86253c52f273d924/docs/serving/samples/hello-world/helloworld-nodejs/Dockerfile).
141
+
142
+ ```Dockerfile
143
+ # Use the official Node.js 10 image.
144
+ # https://hub.docker.com/_/node
145
+ FROM node:10
146
+
147
+ # Create and change to the app directory.
148
+ WORKDIR /usr/src/app
149
+
150
+ # Copy application dependency manifests to the container image.
151
+ # A wildcard is used to ensure both package.json AND package-lock.json are copied.
152
+ # Copying this separately prevents re-running npm install on every code change.
153
+ COPY package*.json ./
154
+
155
+ # Install production dependencies.
156
+ RUN npm install --only=production
157
+
158
+ # Copy local code to the container image.
159
+ COPY . .
160
+
161
+ # Run the web service on container startup.
162
+ CMD [ "npm", "start" ]
163
+ ```
164
+
165
+ ### Add a .dockerignore
166
+
167
+ To keep build artifacts out of your container (which keeps it small and improves build times), add a `.dockerignore` file like the one below:
168
+
169
+ ```.dockerignore
170
+ Dockerfile
171
+ README.md
172
+ node_modules
173
+ npm-debug.log
174
+ ```
175
+
176
+ ### Submit build
177
+
178
+ Next, submit your app to be built into a Docker image by running the following command (replacing `PROJECT-ID` and `APP-NAME` with your GCP project id and an app name:
179
+
180
+ ```bash
181
+ gcloud builds submit --tag gcr.io/PROJECT-ID/APP-NAME
182
+ ```
183
+
184
+ ### Deploy Image
185
+
186
+ After your image has built, you can deploy it with the following command:
187
+
188
+ ```bash
189
+ gcloud beta run deploy --image gcr.io/PROJECT-ID/APP-NAME --platform managed
190
+ ```
191
+
192
+ Your app will be accessible from the URL GCP provides.
@@ -4,9 +4,10 @@
4
4
  Fastify uses a schema-based approach, and even if it is not mandatory we recommend using [JSON Schema](http://json-schema.org/) to validate your routes and serialize your outputs. Internally, Fastify compiles the schema into a highly performant function.
5
5
 
6
6
  > ## ⚠ Security Notice
7
+ > Treat the schema definition as application code.
7
8
  > As both validation and serialization features dynamically evaluate
8
- > code with `new Function()`, it is not safe to use them with
9
- > user-provided data. See [Ajv](http://npm.im/ajv) and
9
+ > code with `new Function()`, it is not safe to use
10
+ > user-provided schemas. See [Ajv](http://npm.im/ajv) and
10
11
  > [fast-json-stringify](http://npm.im/fast-json-stringify) for more
11
12
  > details.
12
13
 
@@ -14,7 +15,7 @@ Fastify uses a schema-based approach, and even if it is not mandatory we recomme
14
15
  ### Validation
15
16
  The route validation internally relies upon [Ajv](https://www.npmjs.com/package/ajv), which is a high-performance JSON schema validator. Validating the input is very easy: just add the fields that you need inside the route schema, and you are done! The supported validations are:
16
17
  - `body`: validates the body of the request if it is a POST or a PUT.
17
- - `querystring`: validates the query string. This can be a complete JSON Schema object (with a `type` property of `'object'` and a `'properties'` object containing parameters) or a simpler variation in which the `type` and `properties` attributes are forgone and the query parameters are listed at the top level (see the example below).
18
+ - `querystring` or `query`: validates the query string. This can be a complete JSON Schema object (with a `type` property of `'object'` and a `'properties'` object containing parameters) or a simpler variation in which the `type` and `properties` attributes are forgone and the query parameters are listed at the top level (see the example below).
18
19
  - `params`: validates the route params.
19
20
  - `headers`: validates the request headers.
20
21
 
@@ -31,7 +32,7 @@ const bodyJsonSchema = {
31
32
  maxItems: 3,
32
33
  items: { type: 'integer' }
33
34
  },
34
- nullableKey: { type: ['number', 'null'] },
35
+ nullableKey: { type: ['number', 'null'] }, // or { type: 'number', nullable: true }
35
36
  multipleTypesKey: { type: ['boolean', 'number'] },
36
37
  multipleRestrictedTypesKey: {
37
38
  oneOf: [
@@ -155,7 +156,7 @@ fastify.route({
155
156
  handler: () => {}
156
157
  })
157
158
 
158
- fastify.register((instance, opts, next) => {
159
+ fastify.register((instance, opts, done) => {
159
160
 
160
161
  /**
161
162
  * In children's scope can use schemas defined in upper scope like 'greetings'.
@@ -179,7 +180,7 @@ fastify.register((instance, opts, next) => {
179
180
  handler: () => {}
180
181
  })
181
182
 
182
- next()
183
+ done()
183
184
  })
184
185
  ```
185
186
 
@@ -219,16 +220,16 @@ The function `getSchemas` returns the shared schemas available in the selected s
219
220
  fastify.addSchema({ $id: 'one', my: 'hello' })
220
221
  fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })
221
222
 
222
- fastify.register((instance, opts, next) => {
223
+ fastify.register((instance, opts, done) => {
223
224
  instance.addSchema({ $id: 'two', my: 'ciao' })
224
225
  instance.get('/sub', (request, reply) => { reply.send(instance.getSchemas()) })
225
226
 
226
- instance.register((subinstance, opts, next) => {
227
+ instance.register((subinstance, opts, done) => {
227
228
  subinstance.addSchema({ $id: 'three', my: 'hola' })
228
229
  subinstance.get('/deep', (request, reply) => { reply.send(subinstance.getSchemas()) })
229
- next()
230
+ done()
230
231
  })
231
- next()
232
+ done()
232
233
  })
233
234
  ```
234
235
  This example will returns:
@@ -251,7 +252,8 @@ Fastify's [baseline ajv configuration](https://github.com/epoberezkin/ajv#option
251
252
  removeAdditional: true, // remove additional properties
252
253
  useDefaults: true, // replace missing properties and items with the values from corresponding default keyword
253
254
  coerceTypes: true, // change data type of data to match type keyword
254
- allErrors: true // check for all errors
255
+ allErrors: true, // check for all errors
256
+ nullable: true // support keyword "nullable" from Open API 3 specification.
255
257
  }
256
258
  ```
257
259
 
@@ -265,7 +267,8 @@ const ajv = new Ajv({
265
267
  removeAdditional: true,
266
268
  useDefaults: true,
267
269
  coerceTypes: true,
268
- allErrors: true
270
+ allErrors: true,
271
+ nullable: true,
269
272
  // any other options
270
273
  // ...
271
274
  })
package/fastify.d.ts CHANGED
@@ -69,9 +69,9 @@ declare namespace fastify {
69
69
  }
70
70
 
71
71
  type FastifyMiddleware<
72
- HttpServer,
73
- HttpRequest,
74
- HttpResponse,
72
+ HttpServer = http.Server,
73
+ HttpRequest = http.IncomingMessage,
74
+ HttpResponse = http.ServerResponse,
75
75
  Query = DefaultQuery,
76
76
  Params = DefaultParams,
77
77
  Headers = DefaultHeaders,
@@ -84,9 +84,9 @@ declare namespace fastify {
84
84
  ) => void
85
85
 
86
86
  type FastifyMiddlewareWithPayload<
87
- HttpServer,
88
- HttpRequest,
89
- HttpResponse,
87
+ HttpServer = http.Server,
88
+ HttpRequest = http.IncomingMessage,
89
+ HttpResponse = http.ServerResponse,
90
90
  Query = DefaultQuery,
91
91
  Params = DefaultParams,
92
92
  Headers = DefaultHeaders,
@@ -100,8 +100,8 @@ declare namespace fastify {
100
100
  ) => void
101
101
 
102
102
  type RequestHandler<
103
- HttpRequest,
104
- HttpResponse,
103
+ HttpRequest = http.IncomingMessage,
104
+ HttpResponse = http.ServerResponse,
105
105
  Query = DefaultQuery,
106
106
  Params = DefaultParams,
107
107
  Headers = DefaultHeaders,
@@ -130,7 +130,7 @@ declare namespace fastify {
130
130
  * fastify's wrapped version of node.js IncomingMessage
131
131
  */
132
132
  interface FastifyRequest<
133
- HttpRequest,
133
+ HttpRequest = http.IncomingMessage,
134
134
  Query = DefaultQuery,
135
135
  Params = DefaultParams,
136
136
  Headers = DefaultHeaders,
@@ -166,6 +166,7 @@ declare namespace fastify {
166
166
  getHeader(name: string): string | undefined
167
167
  hasHeader(name: string): boolean
168
168
  callNotFound(): void
169
+ getResponseTime(): number
169
170
  type(contentType: string): FastifyReply<HttpResponse>
170
171
  redirect(url: string): FastifyReply<HttpResponse>
171
172
  redirect(statusCode: number, url: string): FastifyReply<HttpResponse>
@@ -178,6 +179,7 @@ declare namespace fastify {
178
179
  }
179
180
  type TrustProxyFunction = (addr: string, index: number) => boolean
180
181
  interface ServerOptions {
182
+ caseSensitive?: boolean,
181
183
  ignoreTrailingSlash?: boolean,
182
184
  bodyLimit?: number,
183
185
  pluginTimeout?: number,
@@ -196,7 +198,8 @@ declare namespace fastify {
196
198
  },
197
199
  deriveVersion<Context>(req: Object, ctx?: Context) : String,
198
200
  },
199
- modifyCoreObjects?: boolean
201
+ modifyCoreObjects?: boolean,
202
+ return503OnClosing?: boolean
200
203
  }
201
204
  interface ServerOptionsAsSecure extends ServerOptions {
202
205
  https: http2.SecureServerOptions
@@ -264,9 +267,9 @@ declare namespace fastify {
264
267
  * Route configuration options such as "url" and "method"
265
268
  */
266
269
  interface RouteOptions<
267
- HttpServer,
268
- HttpRequest,
269
- HttpResponse,
270
+ HttpServer = http.Server,
271
+ HttpRequest = http.IncomingMessage,
272
+ HttpResponse = http.ServerResponse,
270
273
  Query = DefaultQuery,
271
274
  Params = DefaultParams,
272
275
  Headers = DefaultHeaders,
@@ -313,7 +316,7 @@ declare namespace fastify {
313
316
  req: NodeJS.ReadableStream,
314
317
  res: http.ServerResponse
315
318
  },
316
- headers: object,
319
+ headers: Record<string, string>,
317
320
  statusCode: number,
318
321
  statusMessage: string,
319
322
  payload: string,
@@ -658,6 +661,11 @@ declare namespace fastify {
658
661
  */
659
662
  setErrorHandler(handler: (error: FastifyError, request: FastifyRequest<HttpRequest>, reply: FastifyReply<HttpResponse>) => void): void
660
663
 
664
+ /**
665
+ * Set a function that will be called whenever an error happens
666
+ */
667
+ setReplySerializer(handler: (payload: string | object | Buffer | NodeJS.ReadableStream, statusCode: number) => string): void
668
+
661
669
  /**
662
670
  * Set the schema compiler for all routes.
663
671
  */
package/fastify.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  kHooks,
14
14
  kSchemas,
15
15
  kSchemaCompiler,
16
+ kReplySerializerDefault,
16
17
  kContentTypeParser,
17
18
  kReply,
18
19
  kRequest,
@@ -118,6 +119,7 @@ function build (options) {
118
119
  [kHooks]: new Hooks(),
119
120
  [kSchemas]: schemas,
120
121
  [kSchemaCompiler]: null,
122
+ [kReplySerializerDefault]: null,
121
123
  [kContentTypeParser]: new ContentTypeParser(bodyLimit, (options.onProtoPoisoning || defaultInitOptions.onProtoPoisoning)),
122
124
  [kReply]: Reply.buildReply(Reply),
123
125
  [kRequest]: Request.buildRequest(Request),
@@ -167,6 +169,7 @@ function build (options) {
167
169
  addSchema: addSchema,
168
170
  getSchemas: schemas.getSchemas.bind(schemas),
169
171
  setSchemaCompiler: setSchemaCompiler,
172
+ setReplySerializer: setReplySerializer,
170
173
  // custom parsers
171
174
  addContentTypeParser: ContentTypeParser.helpers.addContentTypeParser,
172
175
  hasContentTypeParser: ContentTypeParser.helpers.hasContentTypeParser,
@@ -242,6 +245,7 @@ function build (options) {
242
245
  fastify[kState].closing = true
243
246
  router.closeRoutes()
244
247
  if (fastify[kState].listening) {
248
+ // No new TCP connections are accepted
245
249
  instance.server.close(done)
246
250
  } else {
247
251
  done(null)
@@ -279,6 +283,16 @@ function build (options) {
279
283
  }
280
284
 
281
285
  if (fastify[kState].started) {
286
+ if (fastify[kState].closing) {
287
+ // Force to return an error
288
+ const error = new Error('Server is closed')
289
+ if (cb) {
290
+ cb(error)
291
+ return
292
+ } else {
293
+ return Promise.reject(error)
294
+ }
295
+ }
282
296
  return lightMyRequest(httpHandler, opts, cb)
283
297
  }
284
298
 
@@ -390,6 +404,13 @@ function build (options) {
390
404
  return this
391
405
  }
392
406
 
407
+ function setReplySerializer (replySerializer) {
408
+ throwIfAlreadyStarted('Cannot call "setReplySerializer" when fastify instance is already started!')
409
+
410
+ this[kReplySerializerDefault] = replySerializer
411
+ return this
412
+ }
413
+
393
414
  // wrapper that we expose to the user for configure the custom error handler
394
415
  function setErrorHandler (func) {
395
416
  throwIfAlreadyStarted('Cannot call "setErrorHandler" when fastify instance is already started!')
package/lib/context.js CHANGED
@@ -1,10 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { kFourOhFourContext } = require('./symbols.js')
3
+ const { kFourOhFourContext, kReplySerializerDefault } = require('./symbols.js')
4
4
 
5
5
  // Objects that holds the context of every request
6
6
  // Every route holds an instance of this object.
7
- function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, attachValidation) {
7
+ function Context (schema, handler, Reply, Request, contentTypeParser, config, errorHandler, bodyLimit, logLevel, attachValidation, replySerializer) {
8
8
  this.schema = schema
9
9
  this.handler = handler
10
10
  this.Reply = Reply
@@ -22,14 +22,15 @@ function Context (schema, handler, Reply, Request, contentTypeParser, config, er
22
22
  this.logLevel = logLevel
23
23
  this[kFourOhFourContext] = null
24
24
  this.attachValidation = attachValidation
25
+ this[kReplySerializerDefault] = replySerializer
25
26
  }
26
27
 
27
28
  function defaultErrorHandler (error, request, reply) {
28
29
  var res = reply.res
29
30
  if (res.statusCode >= 500) {
30
- res.log.error({ req: reply.request.raw, res: res, err: error }, error && error.message)
31
+ reply.log.error({ req: reply.request.raw, res: res, err: error }, error && error.message)
31
32
  } else if (res.statusCode >= 400) {
32
- res.log.info({ res: res, err: error }, error && error.message)
33
+ reply.log.info({ res: res, err: error }, error && error.message)
33
34
  }
34
35
  reply.send(error)
35
36
  }