fastify 4.8.1 → 4.9.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.
@@ -155,6 +155,10 @@ section.
155
155
  - [`@ethicdevs/fastify-git-server`](https://github.com/EthicDevs/fastify-git-server)
156
156
  A plugin to easily create git server and make one/many Git repositories available
157
157
  for clone/fetch/push through the standard `git` (over http) commands.
158
+ - [`@fastify-userland/request-id`](https://github.com/fastify-userland/request-id)
159
+ Fastify Request ID Plugin
160
+ - [`@fastify-userland/typeorm-query-runner`](https://github.com/fastify-userland/typeorm-query-runner)
161
+ Fastify typeorm QueryRunner plugin
158
162
  - [`@gquittet/graceful-server`](https://github.com/gquittet/graceful-server)
159
163
  Tiny (~5k), Fast, KISS, and dependency-free Node.JS library to make your
160
164
  Fastify API graceful.
@@ -235,7 +239,7 @@ section.
235
239
  send HTTP requests via [axios](https://github.com/axios/axios).
236
240
  - [`fastify-babel`](https://github.com/cfware/fastify-babel) Fastify plugin for
237
241
  development servers that require Babel transformations of JavaScript sources.
238
- - [`fastify-bcrypt`](https://github.com/heply/fastify-bcrypt) A Bcrypt hash
242
+ - [`fastify-bcrypt`](https://github.com/beliven-it/fastify-bcrypt) A Bcrypt hash
239
243
  generator & checker.
240
244
  - [`fastify-blipp`](https://github.com/PavelPolyakov/fastify-blipp) Prints your
241
245
  routes to the console, so you definitely know which endpoints are available.
@@ -263,8 +267,8 @@ section.
263
267
  Sequelize ORM.
264
268
  - [`fastify-couchdb`](https://github.com/nigelhanlon/fastify-couchdb) Fastify
265
269
  plugin to add CouchDB support via [nano](https://github.com/apache/nano).
266
- - [`fastify-crud-generator`](https://github.com/heply/fastify-crud-generator) A
267
- plugin to rapidly generate CRUD routes for any entity.
270
+ - [`fastify-crud-generator`](https://github.com/beliven-it/fastify-crud-generator)
271
+ A plugin to rapidly generate CRUD routes for any entity.
268
272
  - [`fastify-custom-healthcheck`](https://github.com/gkampitakis/fastify-custom-healthcheck)
269
273
  Fastify plugin to add health route in your server that asserts custom
270
274
  functions.
@@ -453,7 +457,7 @@ section.
453
457
  Fastify plugin for memoize responses by expressive settings.
454
458
  - [`fastify-piscina`](https://github.com/piscinajs/fastify-piscina) A worker
455
459
  thread pool plugin using [Piscina](https://github.com/piscinajs/piscina).
456
- - [`fastify-polyglot`](https://github.com/heply/fastify-polyglot) A plugin to
460
+ - [`fastify-polyglot`](https://github.com/beliven-it/fastify-polyglot) A plugin to
457
461
  handle i18n using
458
462
  [node-polyglot](https://www.npmjs.com/package/node-polyglot).
459
463
  - [`fastify-postgraphile`](https://github.com/alemagio/fastify-postgraphile)
@@ -547,7 +551,7 @@ section.
547
551
  [Tokenize](https://github.com/Bowser65/Tokenize) plugin for Fastify that
548
552
  removes the pain of managing authentication tokens, with built-in integration
549
553
  for `fastify-auth`.
550
- - [`fastify-totp`](https://github.com/heply/fastify-totp) A plugin to handle
554
+ - [`fastify-totp`](https://github.com/beliven-it/fastify-totp) A plugin to handle
551
555
  TOTP (e.g. for 2FA).
552
556
  - [`fastify-twitch-ebs-tools`](https://github.com/lukemnet/fastify-twitch-ebs-tools)
553
557
  Useful functions for Twitch Extension Backend Services (EBS).
@@ -606,6 +610,8 @@ section.
606
610
  and updated Typeorm plugin for use with Fastify.
607
611
 
608
612
  #### [Community Tools](#community-tools)
613
+ - [`@fastify-userland/workflows`](https://github.com/fastify-userland/workflows)
614
+ Reusable workflows for use in the Fastify plugin
609
615
  - [`fast-maker`](https://github.com/imjuni/fast-maker) route configuration
610
616
  generator by directory structure.
611
617
  - [`simple-tjscli`](https://github.com/imjuni/simple-tjscli) CLI tool to
@@ -114,10 +114,39 @@ fastify.get('/', async function (request, reply) {
114
114
  The `dependencies` parameter is an optional list of decorators that the
115
115
  decorator being defined relies upon. This list is simply a list of string names
116
116
  of other decorators. In the following example, the "utility" decorator depends
117
- upon "greet" and "log" decorators:
117
+ upon "greet" and "hi" decorators:
118
118
 
119
119
  ```js
120
- fastify.decorate('utility', fn, ['greet', 'log'])
120
+ async function greetDecorator (fastify, opts) {
121
+ fastify.decorate('greet', () => {
122
+ return 'greet message'
123
+ })
124
+ }
125
+
126
+ async function hiDecorator (fastify, opts) {
127
+ fastify.decorate('hi', () => {
128
+ return 'hi message'
129
+ })
130
+ }
131
+
132
+ async function utilityDecorator (fastify, opts) {
133
+ fastify.decorate('utility', () => {
134
+ return `${fastify.greet()} | ${fastify.hi()}`
135
+ })
136
+ }
137
+
138
+ fastify.register(fastifyPlugin(greetDecorator, { name: 'greet' }))
139
+ fastify.register(fastifyPlugin(hiDecorator, { name: 'hi' }))
140
+ fastify.register(fastifyPlugin(utilityDecorator, { dependencies: ['greet', 'hi'] }))
141
+
142
+ fastify.get('/', function (req, reply) {
143
+ // Response: {"hello":"greet message | hi message"}
144
+ reply.send({ hello: fastify.utility() })
145
+ })
146
+
147
+ fastify.listen({ port: 3000 }, (err, address) => {
148
+ if (err) throw err
149
+ })
121
150
  ```
122
151
 
123
152
  Note: using an arrow function will break the binding of `this` to the
@@ -332,7 +332,7 @@ The router received an invalid url.
332
332
  ### FST_ERR_ASYNC_CONSTRAINT
333
333
  <a id="FST_ERR_ASYNC_CONSTRAINT"></a>
334
334
 
335
- The router received error when using asynchronous constraints.
335
+ The router received an error when using asynchronous constraints.
336
336
 
337
337
  #### FST_ERR_DEFAULT_ROUTE_INVALID_TYPE
338
338
  <a id="FST_ERR_DEFAULT_ROUTE_INVALID_TYPE"></a>
@@ -449,9 +449,9 @@ fastify.addHook('onRoute', (routeOptions) => {
449
449
  })
450
450
  ```
451
451
 
452
- If you want to add more routes within an onRoute hook, you have to tag these
453
- routes properly. If you don't, the hook will run into an infinite loop. The
454
- recommended approach is shown below.
452
+ To add more routes within an onRoute hook, the routes must
453
+ be tagged correctly. The hook will run into an infinite loop if
454
+ not tagged. The recommended approach is shown below.
455
455
 
456
456
  ```js
457
457
  const kRouteAlreadyProcessed = Symbol('route-already-processed')
@@ -20,9 +20,9 @@
20
20
  - [.callNotFound()](#callnotfound)
21
21
  - [.getResponseTime()](#getresponsetime)
22
22
  - [.type(contentType)](#typecontenttype)
23
- - [.getSerializationFunction(schema | httpStatus)](#getserializationfunctionschema--httpstatus)
24
- - [.compileSerializationSchema(schema, httpStatus)](#compileserializationschemaschema-httpstatus)
25
- - [.serializeInput(data, [schema | httpStatus], [httpStatus])](#serializeinputdata-schema--httpstatus-httpstatus)
23
+ - [.getSerializationFunction(schema | httpStatus, [contentType])](#getserializationfunctionschema--httpstatus)
24
+ - [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschemaschema-httpstatus)
25
+ - [.serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])](#serializeinputdata-schema--httpstatus-httpstatus)
26
26
  - [.serializer(func)](#serializerfunc)
27
27
  - [.raw](#raw)
28
28
  - [.sent](#sent)
@@ -63,16 +63,17 @@ object that exposes the following functions and properties:
63
63
  - `.serialize(payload)` - Serializes the specified payload using the default
64
64
  JSON serializer or using the custom serializer (if one is set) and returns the
65
65
  serialized payload.
66
- - `.getSerializationFunction(schema | httpStatus)` - Returns the serialization
66
+ - `.getSerializationFunction(schema | httpStatus, [contentType])` - Returns the serialization
67
67
  function for the specified schema or http status, if any of either are set.
68
- - `.compileSerializationSchema(schema, httpStatus)` - Compiles the specified
69
- schema and returns a serialization function using the default (or customized)
70
- `SerializerCompiler`. The optional `httpStatus` is forwarded to the
71
- `SerializerCompiler` if provided, default to `undefined`.
72
- - `.serializeInput(data, schema, [,httpStatus])` - Serializes the specified data
73
- using the specified schema and returns the serialized payload.
74
- If the optional `httpStatus` is provided, the function will use the serializer
75
- function given for that HTTP Status Code. Default to `undefined`.
68
+ - `.compileSerializationSchema(schema, [httpStatus], [contentType])` - Compiles
69
+ the specified schema and returns a serialization function using the default
70
+ (or customized) `SerializerCompiler`. The optional `httpStatus` is forwarded
71
+ to the `SerializerCompiler` if provided, default to `undefined`.
72
+ - `.serializeInput(data, schema, [,httpStatus], [contentType])` - Serializes
73
+ the specified data using the specified schema and returns the serialized payload.
74
+ If the optional `httpStatus`, and `contentType` are provided, the function
75
+ will use the serializer function given for that specific content type and
76
+ HTTP Status Code. Default to `undefined`.
76
77
  - `.serializer(function)` - Sets a custom serializer for the payload.
77
78
  - `.send(payload)` - Sends the payload to the user, could be a plain text, a
78
79
  buffer, JSON, stream, or an Error object.
@@ -339,12 +340,12 @@ reply.type('text/html')
339
340
  If the `Content-Type` has a JSON subtype, and the charset parameter is not set,
340
341
  `utf-8` will be used as the charset by default.
341
342
 
342
- ### .getSerializationFunction(schema | httpStatus)
343
+ ### .getSerializationFunction(schema | httpStatus, [contentType])
343
344
  <a id="getserializationfunction"></a>
344
345
 
345
346
  By calling this function using a provided `schema` or `httpStatus`,
346
- it will return a `serialzation` function that can be used to
347
- serialize diverse inputs. It returns `undefined` if no
347
+ and the optional `contentType`, it will return a `serialzation` function
348
+ that can be used to serialize diverse inputs. It returns `undefined` if no
348
349
  serialization function was found using either of the provided inputs.
349
350
 
350
351
  This heavily depends of the `schema#responses` attached to the route, or
@@ -367,12 +368,18 @@ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
367
368
  const serialize = reply
368
369
  .getSerializationFunction(200)
369
370
  serialize({ foo: 'bar' }) // '{"foo":"bar"}'
371
+
372
+ // or
373
+
374
+ const serialize = reply
375
+ .getSerializationFunction(200, 'application/json')
376
+ serialize({ foo: 'bar' }) // '{"foo":"bar"}'
370
377
  ```
371
378
 
372
- See [.compileSerializationSchema(schema, [httpStatus])](#compileserializationschema)
379
+ See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
373
380
  for more information on how to compile serialization schemas.
374
381
 
375
- ### .compileSerializationSchema(schema, httpStatus)
382
+ ### .compileSerializationSchema(schema, [httpStatus], [contentType])
376
383
  <a id="compileserializationschema"></a>
377
384
 
378
385
  This function will compile a serialization schema and
@@ -381,9 +388,9 @@ The function returned (a.k.a. _serialization function_) returned is compiled
381
388
  by using the provided `SerializerCompiler`. Also this is cached by using
382
389
  a `WeakMap` for reducing compilation calls.
383
390
 
384
- The optional paramater `httpStatus`, if provided, is forwarded directly
385
- the `SerializerCompiler`, so it can be used to compile the serialization
386
- function if a custom `SerializerCompiler` is used.
391
+ The optional paramaters `httpStatus` and `contentType`, if provided,
392
+ are forwarded directly to the `SerializerCompiler`, so it can be used
393
+ to compile the serialization function if a custom `SerializerCompiler` is used.
387
394
 
388
395
  This heavily depends of the `schema#responses` attached to the route, or
389
396
  the serialization functions compiled by using `compileSerializationSchema`.
@@ -412,6 +419,23 @@ const serialize = reply
412
419
  }
413
420
  }, 200)
414
421
  serialize({ foo: 'bar' }) // '{"foo":"bar"}'
422
+
423
+ // or
424
+
425
+ const serialize = reply
426
+ .compileSerializationSchema({
427
+ '3xx': {
428
+ content: {
429
+ 'application/json': {
430
+ schema: {
431
+ name: { type: 'string' },
432
+ phone: { type: 'number' }
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }, '3xx', 'application/json')
438
+ serialize({ name: 'Jone', phone: 201090909090 }) // '{"name":"Jone", "phone":201090909090}'
415
439
  ```
416
440
 
417
441
  Note that you should be careful when using this function, as it will cache
@@ -461,14 +485,14 @@ const newSerialize = reply.compileSerializationSchema(newSchema)
461
485
  console.log(newSerialize === serialize) // false
462
486
  ```
463
487
 
464
- ### .serializeInput(data, [schema | httpStatus], [httpStatus])
488
+ ### .serializeInput(data, [schema | httpStatus], [httpStatus], [contentType])
465
489
  <a id="serializeinput"></a>
466
490
 
467
491
  This function will serialize the input data based on the provided schema,
468
492
  or http status code. If both provided, the `httpStatus` will take presedence.
469
493
 
470
494
  If there is not a serialization function for a given `schema`, a new serialization
471
- function will be compiled forwarding the `httpStatus` if provided.
495
+ function will be compiled forwarding the `httpStatus`, and the `contentType` if provided.
472
496
 
473
497
  ```js
474
498
  reply
@@ -497,9 +521,14 @@ reply
497
521
 
498
522
  reply
499
523
  .serializeInput({ foo: 'bar'}, 200) // '{"foo":"bar"}'
524
+
525
+ // or
526
+
527
+ reply
528
+ .serializeInput({ name: 'Jone', age: 18 }, '200', 'application/vnd.v1+json') // '{"name": "Jone", "age": 18}'
500
529
  ```
501
530
 
502
- See [.compileSerializationSchema(schema, [httpStatus])](#compileserializationschema)
531
+ See [.compileSerializationSchema(schema, [httpStatus], [contentType])](#compileserializationschema)
503
532
  for more information on how to compile serialization schemas.
504
533
 
505
534
  ### .serializer(func)
@@ -627,13 +656,13 @@ fastify.get('/json', options, function (request, reply) {
627
656
  #### Streams
628
657
  <a id="send-streams"></a>
629
658
 
630
- *send* can also handle streams out of the box. If you are sending a stream and
631
- you have not set a `'Content-Type'` header, *send* will set it at
659
+ *send* can also handle streams by setting the `'Content-Type'` header to
632
660
  `'application/octet-stream'`.
633
661
  ```js
634
662
  fastify.get('/streams', function (request, reply) {
635
663
  const fs = require('fs')
636
664
  const stream = fs.createReadStream('some-file', 'utf8')
665
+ reply.header('Content-Type', 'application/octet-stream')
637
666
  reply.send(stream)
638
667
  })
639
668
  ```
@@ -94,8 +94,8 @@ fastify.route(options)
94
94
  schemas for request validations. See the [Validation and
95
95
  Serialization](./Validation-and-Serialization.md#schema-validator)
96
96
  documentation.
97
- * `serializerCompiler({ { schema, method, url, httpStatus } })`: function that
98
- builds schemas for response serialization. See the [Validation and
97
+ * `serializerCompiler({ { schema, method, url, httpStatus, contentType } })`:
98
+ function that builds schemas for response serialization. See the [Validation and
99
99
  Serialization](./Validation-and-Serialization.md#schema-serializer)
100
100
  documentation.
101
101
  * `schemaErrorFormatter(errors, dataVar)`: function that formats the errors from
@@ -734,9 +734,9 @@ fastify.route({
734
734
 
735
735
  #### Asynchronous Custom Constraints
736
736
 
737
- You can provide your custom constraints and the `constraint` criteria can be
738
- fetched from other source, for example `database`. Usage of asynchronous custom
739
- constraint should place at the last resort since it impacts the router
737
+ Custom constraints can be provided and the `constraint` criteria can be
738
+ fetched from another source such as `database`. The use of asynchronous
739
+ custom constraints should be a last resort as it impacts router
740
740
  performance.
741
741
 
742
742
  ```js
@@ -1370,7 +1370,7 @@ const fastify = Fastify({
1370
1370
  buildSerializer: function factory (externalSchemas, serializerOptsServerOption) {
1371
1371
  // This factory function must return a schema serializer compiler.
1372
1372
  // See [#schema-serializer](./Validation-and-Serialization.md#schema-serializer) for details.
1373
- return function serializerCompiler ({ schema, method, url, httpStatus }) {
1373
+ return function serializerCompiler ({ schema, method, url, httpStatus, contentType }) {
1374
1374
  return data => JSON.stringify(data)
1375
1375
  }
1376
1376
  }
@@ -611,6 +611,44 @@ const schema = {
611
611
 
612
612
  fastify.post('/the/url', { schema }, handler)
613
613
  ```
614
+ You can even have a specific response schema for different content types.
615
+ For example:
616
+ ```js
617
+ const schema = {
618
+ response: {
619
+ 200: {
620
+ description: 'Response schema that support different content types'
621
+ content: {
622
+ 'application/json': {
623
+ schema: {
624
+ name: { type: 'string' },
625
+ image: { type: 'string' },
626
+ address: { type: 'string' }
627
+ }
628
+ },
629
+ 'application/vnd.v1+json': {
630
+ schema: {
631
+ type: 'array',
632
+ items: { $ref: 'test' }
633
+ }
634
+ }
635
+ }
636
+ },
637
+ '3xx': {
638
+ content: {
639
+ 'application/vnd.v2+json': {
640
+ schema: {
641
+ fullName: { type: 'string' },
642
+ phone: { type: 'string' }
643
+ }
644
+ }
645
+ }
646
+ }
647
+ }
648
+ }
649
+
650
+ fastify.post('/url', { schema }, handler)
651
+ ```
614
652
 
615
653
  #### Serializer Compiler
616
654
  <a id="schema-serializer"></a>
@@ -621,7 +659,7 @@ change the default serialization method by providing a function to serialize
621
659
  every route where you do.
622
660
 
623
661
  ```js
624
- fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {
662
+ fastify.setSerializerCompiler(({ schema, method, url, httpStatus, contentType }) => {
625
663
  return data => JSON.stringify(data)
626
664
  })
627
665
 
package/fastify.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const VERSION = '4.8.1'
3
+ const VERSION = '4.9.1'
4
4
 
5
5
  const Avvio = require('avvio')
6
6
  const http = require('http')
@@ -49,6 +49,8 @@ function handleError (reply, error, cb) {
49
49
  // In case the error handler throws, we set the next errorHandler so we can error again
50
50
  reply[kReplyNextErrorHandler] = Object.getPrototypeOf(errorHandler)
51
51
 
52
+ // we need to remove content-type to allow content-type guessing for serialization
53
+ delete reply[kReplyHeaders]['content-type']
52
54
  delete reply[kReplyHeaders]['content-length']
53
55
 
54
56
  const func = errorHandler.func
@@ -217,7 +217,7 @@ class Serializer {
217
217
  }
218
218
 
219
219
 
220
-
220
+
221
221
 
222
222
  module.exports = main
223
223
 
package/lib/errors.js CHANGED
@@ -97,7 +97,7 @@ const codes = {
97
97
  ),
98
98
  FST_ERR_HOOK_INVALID_HANDLER: createError(
99
99
  'FST_ERR_HOOK_INVALID_HANDLER',
100
- 'The hook callback must be a function',
100
+ '%s hook should be a function, instead got %s',
101
101
  500,
102
102
  TypeError
103
103
  ),
@@ -165,6 +165,10 @@ const codes = {
165
165
  'FST_ERR_MISSING_SERIALIZATION_FN',
166
166
  'Missing serialization function. Key "%s"'
167
167
  ),
168
+ FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN: createError(
169
+ 'FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN',
170
+ 'Missing serialization function. Key "%s:%s"'
171
+ ),
168
172
  FST_ERR_REQ_INVALID_VALIDATION_INVOCATION: createError(
169
173
  'FST_ERR_REQ_INVALID_VALIDATION_INVOCATION',
170
174
  'Invalid validation invocation. Missing validation function for HTTP part "%s" nor schema provided.'
@@ -181,6 +185,10 @@ const codes = {
181
185
  'FST_ERR_SCH_ALREADY_PRESENT',
182
186
  "Schema with id '%s' already declared!"
183
187
  ),
188
+ FST_ERR_SCH_CONTENT_MISSING_SCHEMA: createError(
189
+ 'FST_ERR_SCH_CONTENT_MISSING_SCHEMA',
190
+ "Schema is missing for the content type '%s'"
191
+ ),
184
192
  FST_ERR_SCH_DUPLICATE: createError(
185
193
  'FST_ERR_SCH_DUPLICATE',
186
194
  "Schema with '%s' already present!"
package/lib/hooks.js CHANGED
@@ -49,10 +49,10 @@ function Hooks () {
49
49
 
50
50
  Hooks.prototype.validate = function (hook, fn) {
51
51
  if (typeof hook !== 'string') throw new FST_ERR_HOOK_INVALID_TYPE()
52
- if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER()
53
52
  if (supportedHooks.indexOf(hook) === -1) {
54
53
  throw new Error(`${hook} hook not supported!`)
55
54
  }
55
+ if (typeof fn !== 'function') throw new FST_ERR_HOOK_INVALID_HANDLER(hook, typeof fn)
56
56
  }
57
57
 
58
58
  Hooks.prototype.add = function (hook, fn) {
package/lib/reply.js CHANGED
@@ -44,7 +44,8 @@ const {
44
44
  FST_ERR_BAD_STATUS_CODE,
45
45
  FST_ERR_BAD_TRAILER_NAME,
46
46
  FST_ERR_BAD_TRAILER_VALUE,
47
- FST_ERR_MISSING_SERIALIZATION_FN
47
+ FST_ERR_MISSING_SERIALIZATION_FN,
48
+ FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN
48
49
  } = require('./errors')
49
50
  const warning = require('./warnings')
50
51
 
@@ -312,11 +313,15 @@ Reply.prototype.code = function (code) {
312
313
 
313
314
  Reply.prototype.status = Reply.prototype.code
314
315
 
315
- Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
316
+ Reply.prototype.getSerializationFunction = function (schemaOrStatus, contentType) {
316
317
  let serialize
317
318
 
318
319
  if (typeof schemaOrStatus === 'string' || typeof schemaOrStatus === 'number') {
319
- serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
320
+ if (typeof contentType === 'string') {
321
+ serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]?.[contentType]
322
+ } else {
323
+ serialize = this[kRouteContext][kSchemaResponse]?.[schemaOrStatus]
324
+ }
320
325
  } else if (typeof schemaOrStatus === 'object') {
321
326
  serialize = this[kRouteContext][kReplySerializeWeakMap]?.get(schemaOrStatus)
322
327
  }
@@ -324,7 +329,7 @@ Reply.prototype.getSerializationFunction = function (schemaOrStatus) {
324
329
  return serialize
325
330
  }
326
331
 
327
- Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null) {
332
+ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null, contentType = null) {
328
333
  const { request } = this
329
334
  const { method, url } = request
330
335
 
@@ -346,7 +351,8 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
346
351
  schema,
347
352
  method,
348
353
  url,
349
- httpStatus
354
+ httpStatus,
355
+ contentType
350
356
  })
351
357
 
352
358
  // We create a WeakMap to compile the schema only once
@@ -363,22 +369,34 @@ Reply.prototype.compileSerializationSchema = function (schema, httpStatus = null
363
369
  return serializeFn
364
370
  }
365
371
 
366
- Reply.prototype.serializeInput = function (input, schema, httpStatus) {
372
+ Reply.prototype.serializeInput = function (input, schema, httpStatus, contentType) {
373
+ const possibleContentType = httpStatus
367
374
  let serialize
368
375
  httpStatus = typeof schema === 'string' || typeof schema === 'number'
369
376
  ? schema
370
377
  : httpStatus
371
378
 
379
+ contentType = httpStatus && possibleContentType !== httpStatus
380
+ ? possibleContentType
381
+ : contentType
382
+
372
383
  if (httpStatus != null) {
373
- serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]
384
+ if (contentType != null) {
385
+ serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]?.[contentType]
386
+ } else {
387
+ serialize = this[kRouteContext][kSchemaResponse]?.[httpStatus]
388
+ }
374
389
 
375
- if (serialize == null) throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
390
+ if (serialize == null) {
391
+ if (contentType) throw new FST_ERR_MISSING_CONTENTTYPE_SERIALIZATION_FN(httpStatus, contentType)
392
+ throw new FST_ERR_MISSING_SERIALIZATION_FN(httpStatus)
393
+ }
376
394
  } else {
377
395
  // Check if serialize function already compiled
378
396
  if (this[kRouteContext][kReplySerializeWeakMap]?.has(schema)) {
379
397
  serialize = this[kRouteContext][kReplySerializeWeakMap].get(schema)
380
398
  } else {
381
- serialize = this.compileSerializationSchema(schema, httpStatus)
399
+ serialize = this.compileSerializationSchema(schema, httpStatus, contentType)
382
400
  }
383
401
  }
384
402
 
@@ -483,7 +501,7 @@ function preserializeHookEnd (err, request, reply, payload) {
483
501
  } else if (reply[kRouteContext] && reply[kRouteContext][kReplySerializerDefault]) {
484
502
  payload = reply[kRouteContext][kReplySerializerDefault](payload, reply.raw.statusCode)
485
503
  } else {
486
- payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode)
504
+ payload = serialize(reply[kRouteContext], payload, reply.raw.statusCode, reply[kReplyHeaders]['content-type'])
487
505
  }
488
506
  } catch (e) {
489
507
  wrapSeralizationError(e, reply)
@@ -797,10 +815,11 @@ function notFound (reply) {
797
815
  * @param {object} context the request context
798
816
  * @param {object} data the JSON payload to serialize
799
817
  * @param {number} statusCode the http status code
818
+ * @param {string} contentType the reply content type
800
819
  * @returns {string} the serialized payload
801
820
  */
802
- function serialize (context, data, statusCode) {
803
- const fnSerialize = getSchemaSerializer(context, statusCode)
821
+ function serialize (context, data, statusCode, contentType) {
822
+ const fnSerialize = getSchemaSerializer(context, statusCode, contentType)
804
823
  if (fnSerialize) {
805
824
  return fnSerialize(data)
806
825
  }
package/lib/route.js CHANGED
@@ -20,7 +20,8 @@ const {
20
20
  FST_ERR_DEFAULT_ROUTE_INVALID_TYPE,
21
21
  FST_ERR_DUPLICATED_ROUTE,
22
22
  FST_ERR_INVALID_URL,
23
- FST_ERR_SEND_UNDEFINED_ERR
23
+ FST_ERR_SEND_UNDEFINED_ERR,
24
+ FST_ERR_HOOK_INVALID_HANDLER
24
25
  } = require('./errors')
25
26
 
26
27
  const {
@@ -243,6 +244,20 @@ function buildRouting (options) {
243
244
  }
244
245
  }
245
246
 
247
+ for (const hook of lifecycleHooks) {
248
+ if (opts && hook in opts) {
249
+ if (Array.isArray(opts[hook])) {
250
+ for (const func of opts[hook]) {
251
+ if (typeof func !== 'function') {
252
+ throw new FST_ERR_HOOK_INVALID_HANDLER(hook, typeof func)
253
+ }
254
+ }
255
+ } else if (opts[hook] !== undefined && typeof opts[hook] !== 'function') {
256
+ throw new FST_ERR_HOOK_INVALID_HANDLER(hook, typeof opts[hook])
257
+ }
258
+ }
259
+ }
260
+
246
261
  const constraints = opts.constraints || {}
247
262
  const config = {
248
263
  ...opts.config,
@@ -15,12 +15,16 @@ function buildSchemaController (parentSchemaCtrl, opts) {
15
15
  return new SchemaController(parentSchemaCtrl, opts)
16
16
  }
17
17
 
18
- let compilersFactory = {
19
- buildValidator: ValidatorSelector(),
20
- buildSerializer: SerializerSelector()
18
+ const compilersFactory = Object.assign({
19
+ buildValidator: null,
20
+ buildSerializer: null
21
+ }, opts?.compilersFactory)
22
+
23
+ if (!compilersFactory.buildValidator) {
24
+ compilersFactory.buildValidator = ValidatorSelector()
21
25
  }
22
- if (opts && opts.compilersFactory) {
23
- compilersFactory = Object.assign(compilersFactory, opts.compilersFactory)
26
+ if (!compilersFactory.buildSerializer) {
27
+ compilersFactory.buildSerializer = SerializerSelector()
24
28
  }
25
29
 
26
30
  const option = {