@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 +63 -1
- package/cjs/classes/default-error-handler.d.ts +3 -0
- package/cjs/classes/default-error-handler.js +20 -12
- package/cjs/config.d.ts +18 -0
- package/cjs/config.js +14 -0
- package/cjs/index.d.ts +2 -1
- package/cjs/index.js +2 -1
- package/cjs/module/error-response.dto.d.ts +3 -0
- package/cjs/module/error-response.dto.js +12 -1
- package/cjs/module/fastify-module.interface.d.ts +4 -4
- package/cjs/module/fastify-router.js +6 -0
- package/cjs/module/fastify.module.d.ts +1 -0
- package/cjs/module/fastify.module.js +14 -4
- package/cjs/module/fastify.utils.js +2 -1
- package/esm/classes/default-error-handler.d.ts +3 -0
- package/esm/classes/default-error-handler.js +21 -13
- package/esm/config.d.ts +18 -0
- package/esm/config.js +12 -0
- package/esm/index.d.ts +2 -1
- package/esm/index.js +2 -1
- package/esm/module/error-response.dto.d.ts +3 -0
- package/esm/module/error-response.dto.js +11 -0
- package/esm/module/fastify-module.interface.d.ts +4 -4
- package/esm/module/fastify-router.js +7 -1
- package/esm/module/fastify.module.d.ts +1 -0
- package/esm/module/fastify.module.js +15 -5
- package/esm/module/fastify.utils.js +2 -1
- package/package.json +8 -5
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
|
-
|
|
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
|
|
33
|
+
return { statusCode: err.getStatusCode(), error: err.toObject() };
|
|
32
34
|
else if (err instanceof common_1.ValidationError)
|
|
33
|
-
return
|
|
35
|
+
return { statusCode: validationError.getStatusCode(), error: { ...err.toObject(), ...validationError.toObject() } };
|
|
34
36
|
else if (err instanceof common_1.AppError)
|
|
35
|
-
return
|
|
36
|
-
else if (err.name === 'FastifyError')
|
|
37
|
-
|
|
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
|
|
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;
|
package/cjs/config.d.ts
ADDED
|
@@ -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
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
|
|
19
|
+
* Importing side-effect packages
|
|
20
20
|
*/
|
|
21
21
|
require("reflect-metadata");
|
|
22
|
+
require("./config.js");
|
|
22
23
|
/**
|
|
23
24
|
* exporting modules
|
|
24
25
|
*/
|
|
@@ -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
|
|
20
|
+
* @default localhost
|
|
21
21
|
*/
|
|
22
|
-
host
|
|
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
|
|
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
|
|
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
|
|
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: '
|
|
38
|
-
port:
|
|
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
|
-
|
|
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
|
|
30
|
+
return { statusCode: err.getStatusCode(), error: err.toObject() };
|
|
29
31
|
else if (err instanceof ValidationError)
|
|
30
|
-
return
|
|
32
|
+
return { statusCode: validationError.getStatusCode(), error: { ...err.toObject(), ...validationError.toObject() } };
|
|
31
33
|
else if (err instanceof AppError)
|
|
32
|
-
return
|
|
33
|
-
else if (err.name === 'FastifyError')
|
|
34
|
-
|
|
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
|
|
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
|
}
|
package/esm/config.d.ts
ADDED
|
@@ -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
package/esm/index.js
CHANGED
|
@@ -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
|
|
20
|
+
* @default localhost
|
|
21
21
|
*/
|
|
22
|
-
host
|
|
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
|
|
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
|
|
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
|
|
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: '
|
|
35
|
-
port:
|
|
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.
|
|
5
|
-
"sideEffects":
|
|
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.
|
|
31
|
-
"@shadow-library/class-schema": "^0.2
|
|
32
|
-
"@shadow-library/common": "^1.
|
|
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": {
|