@shadow-library/fastify 1.6.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -663,6 +663,66 @@ childRouteHeaders: (contextService) => {
663
663
 
664
664
  ## Configuration
665
665
 
666
+ ### Default Configuration
667
+
668
+ The `FastifyModule` automatically provides sensible defaults for core server options. The `host`, `port`, and `errorHandler` properties are all **optional** — if not specified, they are resolved from `@shadow-library/common`'s `Config` or fall back to built-in defaults:
669
+
670
+ | Property | Config Key | Default Value |
671
+ | -------------- | ---------- | --------------------- |
672
+ | `host` | `app.host` | `'localhost'` |
673
+ | `port` | `app.port` | `8080` |
674
+ | `errorHandler` | — | `DefaultErrorHandler` |
675
+
676
+ The `Config.load()` calls register `app.host` and `app.port` with the `Config` system, so they can be overridden via environment variables or any config source supported by `@shadow-library/common` without changing your module setup:
677
+
678
+ ```typescript
679
+ // No explicit host/port needed — defaults are loaded from Config
680
+ FastifyModule.forRoot({
681
+ controllers: [UserController],
682
+ });
683
+ ```
684
+
685
+ > **Note:** When using `forRootAsync`, the defaults are automatically merged with the config returned by your factory function, so you only need to specify the properties you want to override.
686
+
687
+ ### Developer Options
688
+
689
+ The library registers additional config keys under the `app.dev` namespace for development and debugging convenience. These are controlled via environment variables (or any config source) and require no changes to your module setup.
690
+
691
+ | Config Key | Type | Default | Description |
692
+ | --------------------- | --------- | ------------------------------ | ------------------------------------------------- |
693
+ | `app.dev.delay` | `integer` | — (disabled) | Adds an artificial delay (in ms) to every request |
694
+ | `app.dev.stack-trace` | `boolean` | `true` in dev, `false` in prod | Appends the error `stack` to error responses |
695
+
696
+ #### `app.dev.stack-trace`
697
+
698
+ When enabled, the `DefaultErrorHandler` includes the error's `stack` property in the response body alongside the standard error fields. This is automatically enabled in non-production environments and disabled in production.
699
+
700
+ A warning is logged if stack trace logging is active in a production environment.
701
+
702
+ The response shape when enabled matches `DevErrorResponseDto`:
703
+
704
+ ```typescript
705
+ import { DevErrorResponseDto } from '@shadow-library/fastify';
706
+
707
+ // Error response shape with stack trace enabled:
708
+ // {
709
+ // code: "S001",
710
+ // message: "An unexpected error has occurred",
711
+ // stack: "Error: ...\n at ..."
712
+ // }
713
+ ```
714
+
715
+ #### `app.dev.delay`
716
+
717
+ Adds an artificial delay in milliseconds to every incoming request via an `onRequest` hook. Useful for simulating slow networks or testing loading states in your frontend without modifying application code.
718
+
719
+ A warning is logged if a dev delay is active in a production environment.
720
+
721
+ ```bash
722
+ # Add a 500ms delay to every request
723
+ APP_DEV_DELAY=500 node dist/main.js
724
+ ```
725
+
666
726
  ### Dynamic Module Configuration
667
727
 
668
728
  `FastifyModule` is a **dynamic module** that configures itself based on the options you provide. Unlike static modules, dynamic modules return a module configuration object at runtime, allowing for flexible dependency injection and configuration.
@@ -740,7 +800,7 @@ export class AppModule {}
740
800
 
741
801
  #### Asynchronous Configuration (forRootAsync)
742
802
 
743
- Use `forRootAsync` when you need to inject dependencies or load configuration dynamically:
803
+ Use `forRootAsync` when you need to inject dependencies or load configuration dynamically. The config returned by your `useFactory` is automatically merged with the default configuration, so you only need to provide the properties you want to override:
744
804
 
745
805
  ```typescript
746
806
  @Module({
@@ -758,6 +818,8 @@ Use `forRootAsync` when you need to inject dependencies or load configuration dy
758
818
  export class AppModule {}
759
819
  ```
760
820
 
821
+ If your factory returns a `Promise`, the defaults are merged after the promise resolves. This works seamlessly with both sync and async factories.
822
+
761
823
  #### Dynamic Module Benefits
762
824
 
763
825
  - **Flexible Configuration**: Configure the module differently for different environments
@@ -13,6 +13,9 @@ export interface ParsedFastifyError {
13
13
  }
14
14
  export declare class DefaultErrorHandler implements ErrorHandler {
15
15
  private readonly logger;
16
+ private readonly isStackTraceEnabled;
17
+ constructor();
16
18
  protected parseFastifyError(err: FastifyError): ParsedFastifyError;
19
+ private handleError;
17
20
  handle(err: Error, _req: HttpRequest, res: HttpResponse): HttpResponse;
18
21
  }
@@ -18,27 +18,35 @@ const validationError = new server_error_1.ServerError(server_error_1.ServerErro
18
18
  const invalidRequestError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S006);
19
19
  class DefaultErrorHandler {
20
20
  logger = common_1.Logger.getLogger(constants_1.NAMESPACE, 'DefaultErrorHandler');
21
+ isStackTraceEnabled = common_1.Config.get('app.dev.stack-trace');
22
+ constructor() {
23
+ if (this.isStackTraceEnabled && common_1.Config.isProd())
24
+ this.logger.warn('Stack trace logging is enabled in production');
25
+ }
21
26
  parseFastifyError(err) {
22
27
  if (err.statusCode === 500)
23
28
  return { statusCode: 500, error: unexpectedError.toObject() };
24
29
  return { statusCode: err.statusCode, error: { ...invalidRequestError.toObject(), message: err.message } };
25
30
  }
26
- handle(err, _req, res) {
27
- this.logger.warn('Handling error', err);
28
- if (err.cause)
29
- this.logger.warn('Caused by', err.cause);
31
+ handleError(err) {
30
32
  if (err instanceof server_error_1.ServerError)
31
- return res.status(err.getStatusCode()).send(err.toObject());
33
+ return { statusCode: err.getStatusCode(), error: err.toObject() };
32
34
  else if (err instanceof common_1.ValidationError)
33
- return res.status(validationError.getStatusCode()).send({ ...err.toObject(), ...validationError.toObject() });
35
+ return { statusCode: validationError.getStatusCode(), error: { ...err.toObject(), ...validationError.toObject() } };
34
36
  else if (err instanceof common_1.AppError)
35
- return res.status(500).send(err.toObject());
36
- else if (err.name === 'FastifyError') {
37
- const { statusCode, error } = this.parseFastifyError(err);
38
- return res.status(statusCode).send(error);
39
- }
37
+ return { statusCode: 500, error: err.toObject() };
38
+ else if (err.name === 'FastifyError')
39
+ return this.parseFastifyError(err);
40
40
  this.logger.error('Unhandled error has occurred', err);
41
- return res.status(500).send(unexpectedError.toObject());
41
+ return { statusCode: unexpectedError.getStatusCode(), error: unexpectedError.toObject() };
42
+ }
43
+ handle(err, _req, res) {
44
+ this.logger.warn('Handling error', err);
45
+ if (err.cause)
46
+ this.logger.warn('Caused by', err.cause);
47
+ const { statusCode, error } = this.handleError(err);
48
+ const payload = this.isStackTraceEnabled ? { ...error, stack: err.stack } : error;
49
+ return res.status(statusCode).send(payload);
42
50
  }
43
51
  }
44
52
  exports.DefaultErrorHandler = DefaultErrorHandler;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Importing npm packages
3
+ */
4
+ /**
5
+ * Importing user defined packages
6
+ */
7
+ /**
8
+ * Defining types
9
+ */
10
+ declare module '@shadow-library/common' {
11
+ interface ConfigRecords {
12
+ 'app.port': number;
13
+ 'app.host': string;
14
+ 'app.dev.delay': number;
15
+ 'app.dev.stack-trace': boolean;
16
+ }
17
+ }
18
+ export {};
package/cjs/config.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ /**
3
+ * Importing npm packages
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const common_1 = require("@shadow-library/common");
7
+ /**
8
+ * Declaring the constants
9
+ */
10
+ const isDevValue = String(common_1.Config.isDev());
11
+ common_1.Config.load('app.host', { defaultValue: 'localhost' });
12
+ common_1.Config.load('app.port', { defaultValue: '8080', validateType: 'integer' });
13
+ common_1.Config.load('app.dev.delay', { validateType: 'integer' });
14
+ common_1.Config.load('app.dev.stack-trace', { defaultValue: isDevValue, validateType: 'boolean' });
package/cjs/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Importing npm packages
2
+ * Importing side-effect packages
3
3
  */
4
4
  import 'reflect-metadata';
5
+ import './config.js';
5
6
  /**
6
7
  * exporting modules
7
8
  */
package/cjs/index.js CHANGED
@@ -16,9 +16,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.FASTIFY_INSTANCE = void 0;
18
18
  /**
19
- * Importing npm packages
19
+ * Importing side-effect packages
20
20
  */
21
21
  require("reflect-metadata");
22
+ require("./config.js");
22
23
  /**
23
24
  * exporting modules
24
25
  */
@@ -17,3 +17,6 @@ export declare class ErrorResponseDto {
17
17
  message: string;
18
18
  fields?: ErrorFieldDto[];
19
19
  }
20
+ export declare class DevErrorResponseDto extends ErrorResponseDto {
21
+ stack?: string;
22
+ }
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.ErrorResponseDto = exports.ErrorFieldDto = void 0;
12
+ exports.DevErrorResponseDto = exports.ErrorResponseDto = exports.ErrorFieldDto = void 0;
13
13
  /**
14
14
  * Importing npm packages
15
15
  */
@@ -65,3 +65,14 @@ __decorate([
65
65
  exports.ErrorResponseDto = ErrorResponseDto = __decorate([
66
66
  (0, class_schema_1.Schema)()
67
67
  ], ErrorResponseDto);
68
+ let DevErrorResponseDto = class DevErrorResponseDto extends ErrorResponseDto {
69
+ stack;
70
+ };
71
+ exports.DevErrorResponseDto = DevErrorResponseDto;
72
+ __decorate([
73
+ (0, class_schema_1.Field)({ optional: true }),
74
+ __metadata("design:type", String)
75
+ ], DevErrorResponseDto.prototype, "stack", void 0);
76
+ exports.DevErrorResponseDto = DevErrorResponseDto = __decorate([
77
+ (0, class_schema_1.Schema)()
78
+ ], DevErrorResponseDto);
@@ -17,19 +17,19 @@ import { ContextService } from '../services/index.js';
17
17
  export interface FastifyConfig extends FastifyServerOptions {
18
18
  /**
19
19
  * The host on which the Fastify instance is to be started
20
- * @default '127.0.0.1'
20
+ * @default localhost
21
21
  */
22
- host: string;
22
+ host?: string;
23
23
  /**
24
24
  * The port on which the Fastify instance is to be started
25
25
  * @default 8080
26
26
  */
27
- port: number;
27
+ port?: number;
28
28
  /**
29
29
  * The error handler to be used to handle errors thrown by the Fastify instance
30
30
  * @default DefaultErrorHandler
31
31
  */
32
- errorHandler: ErrorHandler;
32
+ errorHandler?: ErrorHandler;
33
33
  /**
34
34
  * The schema to be used to validate the response of the Fastify instance
35
35
  * @default { '4xx': errorResponseSchema, '5xx': errorResponseSchema }
@@ -291,6 +291,12 @@ let FastifyRouter = class FastifyRouter extends app_1.Router {
291
291
  this.instance.addHook('onRequest', this.context.init());
292
292
  this.instance.addHook('onRequest', this.getRequestLogger());
293
293
  this.logger.info('Registered global middlewares');
294
+ const delay = common_1.Config.get('app.dev.delay');
295
+ if (delay) {
296
+ if (common_1.Config.isProd())
297
+ this.logger.warn('Dev delay is enabled in production');
298
+ this.instance.addHook('onRequest', (_req, _res, done) => setTimeout(done, delay));
299
+ }
294
300
  for (const route of routes) {
295
301
  const metadata = route.metadata;
296
302
  (0, node_assert_1.default)(metadata.path, 'Route path is required');
@@ -11,6 +11,7 @@ import { FastifyModuleAsyncOptions, FastifyModuleOptions } from './fastify-modul
11
11
  */
12
12
  export declare class FastifyModule {
13
13
  private static getDefaultConfig;
14
+ private static createConfigFactory;
14
15
  static forRoot(options: FastifyModuleOptions): DynamicModule;
15
16
  static forRootAsync(options: FastifyModuleAsyncOptions): DynamicModule;
16
17
  }
@@ -32,10 +32,12 @@ const fastify_utils_1 = require("./fastify.utils.js");
32
32
  */
33
33
  let FastifyModule = FastifyModule_1 = class FastifyModule {
34
34
  static getDefaultConfig() {
35
- const errorResponseSchema = class_schema_1.ClassSchema.generate(error_response_dto_1.ErrorResponseDto);
35
+ const stackTrace = common_1.Config.get('app.dev.stack-trace');
36
+ const errorResponseClass = stackTrace ? error_response_dto_1.DevErrorResponseDto : error_response_dto_1.ErrorResponseDto;
37
+ const errorResponseSchema = class_schema_1.ClassSchema.generate(errorResponseClass);
36
38
  return {
37
- host: 'localhost',
38
- port: 8080,
39
+ host: common_1.Config.get('app.host'),
40
+ port: common_1.Config.get('app.port'),
39
41
  responseSchema: { '4xx': errorResponseSchema, '5xx': errorResponseSchema },
40
42
  errorHandler: new classes_1.DefaultErrorHandler(),
41
43
  maskSensitiveData: common_1.Config.isProd(),
@@ -47,6 +49,14 @@ let FastifyModule = FastifyModule_1 = class FastifyModule {
47
49
  },
48
50
  };
49
51
  }
52
+ static createConfigFactory(factory) {
53
+ return (...args) => {
54
+ const config = factory(...args);
55
+ if (config instanceof Promise)
56
+ return config.then(resolvedConfig => Object.assign({}, this.getDefaultConfig(), resolvedConfig));
57
+ return Object.assign({}, this.getDefaultConfig(), config);
58
+ };
59
+ }
50
60
  static forRoot(options) {
51
61
  const config = Object.assign({}, this.getDefaultConfig(), common_1.utils.object.omitKeys(options, ['imports', 'controllers', 'providers', 'exports', 'fastifyFactory']));
52
62
  return this.forRootAsync({
@@ -61,7 +71,7 @@ let FastifyModule = FastifyModule_1 = class FastifyModule {
61
71
  static forRootAsync(options) {
62
72
  const fastifyFactory = (config) => (0, fastify_utils_1.createFastifyInstance)(config, options.fastifyFactory);
63
73
  const providers = [{ token: app_1.Router, useClass: fastify_router_1.FastifyRouter }, services_1.ContextService];
64
- providers.push({ token: constants_1.FASTIFY_CONFIG, useFactory: options.useFactory, inject: options.inject });
74
+ providers.push({ token: constants_1.FASTIFY_CONFIG, useFactory: this.createConfigFactory(options.useFactory), inject: options.inject });
65
75
  providers.push({ token: constants_1.FASTIFY_INSTANCE, useFactory: fastifyFactory, inject: [constants_1.FASTIFY_CONFIG] });
66
76
  if (options.providers)
67
77
  providers.push(...options.providers);
@@ -86,7 +86,8 @@ async function createFastifyInstance(config, fastifyFactory) {
86
86
  const instance = (0, fastify_1.fastify)(options);
87
87
  instance.setSchemaErrorFormatter(formatSchemaErrors);
88
88
  instance.setNotFoundHandler(exports.notFoundHandler);
89
- instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
90
89
  instance.setValidatorCompiler(routeSchema => compileValidator(routeSchema, { strictValidator, lenientValidator }));
90
+ if (errorHandler)
91
+ instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
91
92
  return fastifyFactory ? await fastifyFactory(instance) : instance;
92
93
  }
@@ -13,6 +13,9 @@ export interface ParsedFastifyError {
13
13
  }
14
14
  export declare class DefaultErrorHandler implements ErrorHandler {
15
15
  private readonly logger;
16
+ private readonly isStackTraceEnabled;
17
+ constructor();
16
18
  protected parseFastifyError(err: FastifyError): ParsedFastifyError;
19
+ private handleError;
17
20
  handle(err: Error, _req: HttpRequest, res: HttpResponse): HttpResponse;
18
21
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Importing npm packages
3
3
  */
4
- import { AppError, Logger, ValidationError } from '@shadow-library/common';
4
+ import { AppError, Config, Logger, ValidationError } from '@shadow-library/common';
5
5
  /**
6
6
  * Importing user defined packages
7
7
  */
@@ -15,26 +15,34 @@ const validationError = new ServerError(ServerErrorCode.S003);
15
15
  const invalidRequestError = new ServerError(ServerErrorCode.S006);
16
16
  export class DefaultErrorHandler {
17
17
  logger = Logger.getLogger(NAMESPACE, 'DefaultErrorHandler');
18
+ isStackTraceEnabled = Config.get('app.dev.stack-trace');
19
+ constructor() {
20
+ if (this.isStackTraceEnabled && Config.isProd())
21
+ this.logger.warn('Stack trace logging is enabled in production');
22
+ }
18
23
  parseFastifyError(err) {
19
24
  if (err.statusCode === 500)
20
25
  return { statusCode: 500, error: unexpectedError.toObject() };
21
26
  return { statusCode: err.statusCode, error: { ...invalidRequestError.toObject(), message: err.message } };
22
27
  }
23
- handle(err, _req, res) {
24
- this.logger.warn('Handling error', err);
25
- if (err.cause)
26
- this.logger.warn('Caused by', err.cause);
28
+ handleError(err) {
27
29
  if (err instanceof ServerError)
28
- return res.status(err.getStatusCode()).send(err.toObject());
30
+ return { statusCode: err.getStatusCode(), error: err.toObject() };
29
31
  else if (err instanceof ValidationError)
30
- return res.status(validationError.getStatusCode()).send({ ...err.toObject(), ...validationError.toObject() });
32
+ return { statusCode: validationError.getStatusCode(), error: { ...err.toObject(), ...validationError.toObject() } };
31
33
  else if (err instanceof AppError)
32
- return res.status(500).send(err.toObject());
33
- else if (err.name === 'FastifyError') {
34
- const { statusCode, error } = this.parseFastifyError(err);
35
- return res.status(statusCode).send(error);
36
- }
34
+ return { statusCode: 500, error: err.toObject() };
35
+ else if (err.name === 'FastifyError')
36
+ return this.parseFastifyError(err);
37
37
  this.logger.error('Unhandled error has occurred', err);
38
- return res.status(500).send(unexpectedError.toObject());
38
+ return { statusCode: unexpectedError.getStatusCode(), error: unexpectedError.toObject() };
39
+ }
40
+ handle(err, _req, res) {
41
+ this.logger.warn('Handling error', err);
42
+ if (err.cause)
43
+ this.logger.warn('Caused by', err.cause);
44
+ const { statusCode, error } = this.handleError(err);
45
+ const payload = this.isStackTraceEnabled ? { ...error, stack: err.stack } : error;
46
+ return res.status(statusCode).send(payload);
39
47
  }
40
48
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Importing npm packages
3
+ */
4
+ /**
5
+ * Importing user defined packages
6
+ */
7
+ /**
8
+ * Defining types
9
+ */
10
+ declare module '@shadow-library/common' {
11
+ interface ConfigRecords {
12
+ 'app.port': number;
13
+ 'app.host': string;
14
+ 'app.dev.delay': number;
15
+ 'app.dev.stack-trace': boolean;
16
+ }
17
+ }
18
+ export {};
package/esm/config.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Importing npm packages
3
+ */
4
+ import { Config } from '@shadow-library/common';
5
+ /**
6
+ * Declaring the constants
7
+ */
8
+ const isDevValue = String(Config.isDev());
9
+ Config.load('app.host', { defaultValue: 'localhost' });
10
+ Config.load('app.port', { defaultValue: '8080', validateType: 'integer' });
11
+ Config.load('app.dev.delay', { validateType: 'integer' });
12
+ Config.load('app.dev.stack-trace', { defaultValue: isDevValue, validateType: 'boolean' });
package/esm/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Importing npm packages
2
+ * Importing side-effect packages
3
3
  */
4
4
  import 'reflect-metadata';
5
+ import './config.js';
5
6
  /**
6
7
  * exporting modules
7
8
  */
package/esm/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
- * Importing npm packages
2
+ * Importing side-effect packages
3
3
  */
4
4
  import 'reflect-metadata';
5
+ import './config.js';
5
6
  /**
6
7
  * exporting modules
7
8
  */
@@ -17,3 +17,6 @@ export declare class ErrorResponseDto {
17
17
  message: string;
18
18
  fields?: ErrorFieldDto[];
19
19
  }
20
+ export declare class DevErrorResponseDto extends ErrorResponseDto {
21
+ stack?: string;
22
+ }
@@ -62,3 +62,14 @@ ErrorResponseDto = __decorate([
62
62
  Schema()
63
63
  ], ErrorResponseDto);
64
64
  export { ErrorResponseDto };
65
+ let DevErrorResponseDto = class DevErrorResponseDto extends ErrorResponseDto {
66
+ stack;
67
+ };
68
+ __decorate([
69
+ Field({ optional: true }),
70
+ __metadata("design:type", String)
71
+ ], DevErrorResponseDto.prototype, "stack", void 0);
72
+ DevErrorResponseDto = __decorate([
73
+ Schema()
74
+ ], DevErrorResponseDto);
75
+ export { DevErrorResponseDto };
@@ -17,19 +17,19 @@ import { ContextService } from '../services/index.js';
17
17
  export interface FastifyConfig extends FastifyServerOptions {
18
18
  /**
19
19
  * The host on which the Fastify instance is to be started
20
- * @default '127.0.0.1'
20
+ * @default localhost
21
21
  */
22
- host: string;
22
+ host?: string;
23
23
  /**
24
24
  * The port on which the Fastify instance is to be started
25
25
  * @default 8080
26
26
  */
27
- port: number;
27
+ port?: number;
28
28
  /**
29
29
  * The error handler to be used to handle errors thrown by the Fastify instance
30
30
  * @default DefaultErrorHandler
31
31
  */
32
- errorHandler: ErrorHandler;
32
+ errorHandler?: ErrorHandler;
33
33
  /**
34
34
  * The schema to be used to validate the response of the Fastify instance
35
35
  * @default { '4xx': errorResponseSchema, '5xx': errorResponseSchema }
@@ -16,7 +16,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
16
16
  import assert from 'node:assert';
17
17
  import { Inject, Injectable, Router } from '@shadow-library/app';
18
18
  import { ClassSchema, TransformerFactory } from '@shadow-library/class-schema';
19
- import { InternalError, Logger, utils } from '@shadow-library/common';
19
+ import { Config, InternalError, Logger, utils } from '@shadow-library/common';
20
20
  import { all as deepmerge } from 'deepmerge';
21
21
  import findMyWay from 'find-my-way';
22
22
  import stringify from 'json-stable-stringify';
@@ -285,6 +285,12 @@ let FastifyRouter = class FastifyRouter extends Router {
285
285
  this.instance.addHook('onRequest', this.context.init());
286
286
  this.instance.addHook('onRequest', this.getRequestLogger());
287
287
  this.logger.info('Registered global middlewares');
288
+ const delay = Config.get('app.dev.delay');
289
+ if (delay) {
290
+ if (Config.isProd())
291
+ this.logger.warn('Dev delay is enabled in production');
292
+ this.instance.addHook('onRequest', (_req, _res, done) => setTimeout(done, delay));
293
+ }
288
294
  for (const route of routes) {
289
295
  const metadata = route.metadata;
290
296
  assert(metadata.path, 'Route path is required');
@@ -11,6 +11,7 @@ import { FastifyModuleAsyncOptions, FastifyModuleOptions } from './fastify-modul
11
11
  */
12
12
  export declare class FastifyModule {
13
13
  private static getDefaultConfig;
14
+ private static createConfigFactory;
14
15
  static forRoot(options: FastifyModuleOptions): DynamicModule;
15
16
  static forRootAsync(options: FastifyModuleAsyncOptions): DynamicModule;
16
17
  }
@@ -18,7 +18,7 @@ import { v4 as uuid } from 'uuid';
18
18
  import { DefaultErrorHandler } from '../classes/index.js';
19
19
  import { FASTIFY_CONFIG, FASTIFY_INSTANCE } from '../constants.js';
20
20
  import { ContextService } from '../services/index.js';
21
- import { ErrorResponseDto } from './error-response.dto.js';
21
+ import { DevErrorResponseDto, ErrorResponseDto } from './error-response.dto.js';
22
22
  import { FastifyRouter } from './fastify-router.js';
23
23
  import { createFastifyInstance } from './fastify.utils.js';
24
24
  /**
@@ -29,10 +29,12 @@ import { createFastifyInstance } from './fastify.utils.js';
29
29
  */
30
30
  let FastifyModule = FastifyModule_1 = class FastifyModule {
31
31
  static getDefaultConfig() {
32
- const errorResponseSchema = ClassSchema.generate(ErrorResponseDto);
32
+ const stackTrace = Config.get('app.dev.stack-trace');
33
+ const errorResponseClass = stackTrace ? DevErrorResponseDto : ErrorResponseDto;
34
+ const errorResponseSchema = ClassSchema.generate(errorResponseClass);
33
35
  return {
34
- host: 'localhost',
35
- port: 8080,
36
+ host: Config.get('app.host'),
37
+ port: Config.get('app.port'),
36
38
  responseSchema: { '4xx': errorResponseSchema, '5xx': errorResponseSchema },
37
39
  errorHandler: new DefaultErrorHandler(),
38
40
  maskSensitiveData: Config.isProd(),
@@ -44,6 +46,14 @@ let FastifyModule = FastifyModule_1 = class FastifyModule {
44
46
  },
45
47
  };
46
48
  }
49
+ static createConfigFactory(factory) {
50
+ return (...args) => {
51
+ const config = factory(...args);
52
+ if (config instanceof Promise)
53
+ return config.then(resolvedConfig => Object.assign({}, this.getDefaultConfig(), resolvedConfig));
54
+ return Object.assign({}, this.getDefaultConfig(), config);
55
+ };
56
+ }
47
57
  static forRoot(options) {
48
58
  const config = Object.assign({}, this.getDefaultConfig(), utils.object.omitKeys(options, ['imports', 'controllers', 'providers', 'exports', 'fastifyFactory']));
49
59
  return this.forRootAsync({
@@ -58,7 +68,7 @@ let FastifyModule = FastifyModule_1 = class FastifyModule {
58
68
  static forRootAsync(options) {
59
69
  const fastifyFactory = (config) => createFastifyInstance(config, options.fastifyFactory);
60
70
  const providers = [{ token: Router, useClass: FastifyRouter }, ContextService];
61
- providers.push({ token: FASTIFY_CONFIG, useFactory: options.useFactory, inject: options.inject });
71
+ providers.push({ token: FASTIFY_CONFIG, useFactory: this.createConfigFactory(options.useFactory), inject: options.inject });
62
72
  providers.push({ token: FASTIFY_INSTANCE, useFactory: fastifyFactory, inject: [FASTIFY_CONFIG] });
63
73
  if (options.providers)
64
74
  providers.push(...options.providers);
@@ -76,7 +76,8 @@ export async function createFastifyInstance(config, fastifyFactory) {
76
76
  const instance = fastify(options);
77
77
  instance.setSchemaErrorFormatter(formatSchemaErrors);
78
78
  instance.setNotFoundHandler(notFoundHandler);
79
- instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
80
79
  instance.setValidatorCompiler(routeSchema => compileValidator(routeSchema, { strictValidator, lenientValidator }));
80
+ if (errorHandler)
81
+ instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
81
82
  return fastifyFactory ? await fastifyFactory(instance) : instance;
82
83
  }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@shadow-library/fastify",
3
3
  "type": "module",
4
- "version": "1.6.3",
5
- "sideEffects": false,
4
+ "version": "1.7.0",
5
+ "sideEffects": [
6
+ "./esm/config.js",
7
+ "./cjs/config.js"
8
+ ],
6
9
  "description": "A Fastify wrapper featuring decorator-based routing, middleware and error handling",
7
10
  "repository": {
8
11
  "type": "git",
@@ -27,9 +30,9 @@
27
30
  },
28
31
  "peerDependencies": {
29
32
  "@fastify/view": "^10.0.0",
30
- "@shadow-library/app": "^1.3.1",
31
- "@shadow-library/class-schema": "^0.2.0",
32
- "@shadow-library/common": "^1.5.2",
33
+ "@shadow-library/app": "^1.3.2",
34
+ "@shadow-library/class-schema": "^0.4.2",
35
+ "@shadow-library/common": "^1.6.0",
33
36
  "reflect-metadata": "^0.2.2"
34
37
  },
35
38
  "peerDependenciesMeta": {