@shadow-library/fastify 1.6.2 → 1.6.4

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,27 @@ 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
+
666
687
  ### Dynamic Module Configuration
667
688
 
668
689
  `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 +761,7 @@ export class AppModule {}
740
761
 
741
762
  #### Asynchronous Configuration (forRootAsync)
742
763
 
743
- Use `forRootAsync` when you need to inject dependencies or load configuration dynamically:
764
+ 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
765
 
745
766
  ```typescript
746
767
  @Module({
@@ -758,6 +779,8 @@ Use `forRootAsync` when you need to inject dependencies or load configuration dy
758
779
  export class AppModule {}
759
780
  ```
760
781
 
782
+ If your factory returns a `Promise`, the defaults are merged after the promise resolves. This works seamlessly with both sync and async factories.
783
+
761
784
  #### Dynamic Module Benefits
762
785
 
763
786
  - **Flexible Configuration**: Configure the module differently for different environments
@@ -9,5 +9,5 @@ const app_1 = require("@shadow-library/app");
9
9
  * Declaring the constants
10
10
  */
11
11
  function ApiOperation(options) {
12
- return (0, app_1.Route)({ operation: options });
12
+ return (0, app_1.Route)({ operation: options }, { arrayStrategy: 'replace' });
13
13
  }
@@ -14,22 +14,28 @@ import { ContextService } from '../services/index.js';
14
14
  /**
15
15
  * Defining types
16
16
  */
17
+ declare module '@shadow-library/common' {
18
+ interface ConfigRecords {
19
+ 'app.port': number;
20
+ 'app.host': string;
21
+ }
22
+ }
17
23
  export interface FastifyConfig extends FastifyServerOptions {
18
24
  /**
19
25
  * The host on which the Fastify instance is to be started
20
- * @default '127.0.0.1'
26
+ * @default localhost
21
27
  */
22
- host: string;
28
+ host?: string;
23
29
  /**
24
30
  * The port on which the Fastify instance is to be started
25
31
  * @default 8080
26
32
  */
27
- port: number;
33
+ port?: number;
28
34
  /**
29
35
  * The error handler to be used to handle errors thrown by the Fastify instance
30
36
  * @default DefaultErrorHandler
31
37
  */
32
- errorHandler: ErrorHandler;
38
+ errorHandler?: ErrorHandler;
33
39
  /**
34
40
  * The schema to be used to validate the response of the Fastify instance
35
41
  * @default { '4xx': errorResponseSchema, '5xx': errorResponseSchema }
@@ -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
+ common_1.Config.load('app.host', { defaultValue: 'localhost' });
36
+ common_1.Config.load('app.port', { defaultValue: '8080', validateType: 'number' });
35
37
  const errorResponseSchema = class_schema_1.ClassSchema.generate(error_response_dto_1.ErrorResponseDto);
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
  }
@@ -23,6 +23,10 @@ const ERROR_STATUS_CODES = {
23
23
  [common_1.ErrorType.UNAUTHORIZED]: 403,
24
24
  [common_1.ErrorType.VALIDATION_ERROR]: 422,
25
25
  [common_1.ErrorType.CONFLICT]: 409,
26
+ [common_1.ErrorType.INVALID_REQUEST]: 400,
27
+ [common_1.ErrorType.IO_ERROR]: 400,
28
+ [common_1.ErrorType.PERMISSION_DENIED]: 403,
29
+ [common_1.ErrorType.INTERNAL_ERROR]: 500,
26
30
  };
27
31
  class ServerError extends common_1.AppError {
28
32
  getStatusCode() {
@@ -6,5 +6,5 @@ import { Route } from '@shadow-library/app';
6
6
  * Declaring the constants
7
7
  */
8
8
  export function ApiOperation(options) {
9
- return Route({ operation: options });
9
+ return Route({ operation: options }, { arrayStrategy: 'replace' });
10
10
  }
@@ -14,22 +14,28 @@ import { ContextService } from '../services/index.js';
14
14
  /**
15
15
  * Defining types
16
16
  */
17
+ declare module '@shadow-library/common' {
18
+ interface ConfigRecords {
19
+ 'app.port': number;
20
+ 'app.host': string;
21
+ }
22
+ }
17
23
  export interface FastifyConfig extends FastifyServerOptions {
18
24
  /**
19
25
  * The host on which the Fastify instance is to be started
20
- * @default '127.0.0.1'
26
+ * @default localhost
21
27
  */
22
- host: string;
28
+ host?: string;
23
29
  /**
24
30
  * The port on which the Fastify instance is to be started
25
31
  * @default 8080
26
32
  */
27
- port: number;
33
+ port?: number;
28
34
  /**
29
35
  * The error handler to be used to handle errors thrown by the Fastify instance
30
36
  * @default DefaultErrorHandler
31
37
  */
32
- errorHandler: ErrorHandler;
38
+ errorHandler?: ErrorHandler;
33
39
  /**
34
40
  * The schema to be used to validate the response of the Fastify instance
35
41
  * @default { '4xx': errorResponseSchema, '5xx': errorResponseSchema }
@@ -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
  }
@@ -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
+ Config.load('app.host', { defaultValue: 'localhost' });
33
+ Config.load('app.port', { defaultValue: '8080', validateType: 'number' });
32
34
  const errorResponseSchema = ClassSchema.generate(ErrorResponseDto);
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
  }
@@ -20,6 +20,10 @@ const ERROR_STATUS_CODES = {
20
20
  [ErrorType.UNAUTHORIZED]: 403,
21
21
  [ErrorType.VALIDATION_ERROR]: 422,
22
22
  [ErrorType.CONFLICT]: 409,
23
+ [ErrorType.INVALID_REQUEST]: 400,
24
+ [ErrorType.IO_ERROR]: 400,
25
+ [ErrorType.PERMISSION_DENIED]: 403,
26
+ [ErrorType.INTERNAL_ERROR]: 500,
23
27
  };
24
28
  export class ServerError extends AppError {
25
29
  getStatusCode() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shadow-library/fastify",
3
3
  "type": "module",
4
- "version": "1.6.2",
4
+ "version": "1.6.4",
5
5
  "sideEffects": false,
6
6
  "description": "A Fastify wrapper featuring decorator-based routing, middleware and error handling",
7
7
  "repository": {
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "peerDependencies": {
29
29
  "@fastify/view": "^10.0.0",
30
- "@shadow-library/app": "^1.0.11",
31
- "@shadow-library/class-schema": "^0.1.3",
32
- "@shadow-library/common": "^1.0.22",
30
+ "@shadow-library/app": "^1.3.1",
31
+ "@shadow-library/class-schema": "^0.2.0",
32
+ "@shadow-library/common": "^1.5.2",
33
33
  "reflect-metadata": "^0.2.2"
34
34
  },
35
35
  "peerDependenciesMeta": {