@shadow-library/fastify 0.0.3 → 0.0.5
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/cjs/classes/default-error-handler.js +3 -9
- package/cjs/constants.d.ts +1 -0
- package/cjs/constants.js +2 -1
- package/cjs/module/fastify-router.d.ts +19 -1
- package/cjs/module/fastify-router.js +41 -7
- package/cjs/module/fastify.module.js +0 -1
- package/cjs/module/fastify.utils.d.ts +3 -1
- package/cjs/module/fastify.utils.js +28 -0
- package/cjs/services/context.service.d.ts +3 -3
- package/esm/classes/default-error-handler.js +3 -9
- package/esm/constants.d.ts +1 -0
- package/esm/constants.js +1 -0
- package/esm/module/fastify-router.d.ts +19 -1
- package/esm/module/fastify-router.js +42 -8
- package/esm/module/fastify.module.js +0 -1
- package/esm/module/fastify.utils.d.ts +3 -1
- package/esm/module/fastify.utils.js +24 -0
- package/esm/services/context.service.d.ts +3 -3
- package/package.json +10 -7
|
@@ -2,22 +2,16 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DefaultErrorHandler = void 0;
|
|
4
4
|
const common_1 = require("@shadow-library/common");
|
|
5
|
+
const constants_1 = require("../constants.js");
|
|
5
6
|
const server_error_1 = require("../server.error.js");
|
|
6
7
|
const unexpectedError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S001);
|
|
7
8
|
const validationError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S003);
|
|
8
9
|
class DefaultErrorHandler {
|
|
9
|
-
logger = common_1.Logger.getLogger(DefaultErrorHandler
|
|
10
|
+
logger = common_1.Logger.getLogger(constants_1.NAMESPACE, 'DefaultErrorHandler');
|
|
10
11
|
parseFastifyError(err) {
|
|
11
12
|
if (err.statusCode === 500)
|
|
12
13
|
return { statusCode: 500, error: unexpectedError.toObject() };
|
|
13
|
-
return {
|
|
14
|
-
statusCode: err.statusCode,
|
|
15
|
-
error: {
|
|
16
|
-
code: err.code,
|
|
17
|
-
type: common_1.ErrorType.CLIENT_ERROR,
|
|
18
|
-
message: err.message,
|
|
19
|
-
},
|
|
20
|
-
};
|
|
14
|
+
return { statusCode: err.statusCode, error: { code: err.code, type: common_1.ErrorType.CLIENT_ERROR, message: err.message } };
|
|
21
15
|
}
|
|
22
16
|
handle(err, _req, res) {
|
|
23
17
|
this.logger.warn('Handling error', err);
|
package/cjs/constants.d.ts
CHANGED
package/cjs/constants.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HTTP_CONTROLLER_INPUTS = exports.HTTP_CONTROLLER_TYPE = exports.FASTIFY_INSTANCE = exports.FASTIFY_CONFIG = exports.PARAMTYPES_METADATA = void 0;
|
|
3
|
+
exports.HTTP_CONTROLLER_INPUTS = exports.HTTP_CONTROLLER_TYPE = exports.FASTIFY_INSTANCE = exports.FASTIFY_CONFIG = exports.PARAMTYPES_METADATA = exports.NAMESPACE = void 0;
|
|
4
|
+
exports.NAMESPACE = '@shadow-library/fastify';
|
|
4
5
|
exports.PARAMTYPES_METADATA = 'design:paramtypes';
|
|
5
6
|
exports.FASTIFY_CONFIG = Symbol('fastify-config');
|
|
6
7
|
exports.FASTIFY_INSTANCE = Symbol('fastify-instance');
|
|
@@ -3,6 +3,7 @@ import { type FastifyInstance } from 'fastify';
|
|
|
3
3
|
import { Chain as MockRequestChain, InjectOptions as MockRequestOptions, Response as MockResponse } from 'light-my-request';
|
|
4
4
|
import { JsonObject } from 'type-fest';
|
|
5
5
|
import { HttpRequest, HttpResponse, ServerMetadata } from '../interfaces/index.js';
|
|
6
|
+
import { Context } from '../services/index.js';
|
|
6
7
|
import { type FastifyConfig } from './fastify-module.interface.js';
|
|
7
8
|
declare module 'fastify' {
|
|
8
9
|
interface FastifyRequest {
|
|
@@ -19,13 +20,30 @@ export interface RequestContext {
|
|
|
19
20
|
query: Record<string, string>;
|
|
20
21
|
body: JsonObject;
|
|
21
22
|
}
|
|
23
|
+
export interface RequestMetadata {
|
|
24
|
+
rid?: string;
|
|
25
|
+
srid?: string;
|
|
26
|
+
method?: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
status?: number;
|
|
29
|
+
reqLen?: string;
|
|
30
|
+
reqIp?: string;
|
|
31
|
+
resLen?: string;
|
|
32
|
+
timeTaken?: string;
|
|
33
|
+
body?: any;
|
|
34
|
+
query?: object;
|
|
35
|
+
service?: string;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
22
38
|
export declare class FastifyRouter extends Router {
|
|
23
39
|
private readonly config;
|
|
24
40
|
private readonly instance;
|
|
41
|
+
private readonly context;
|
|
25
42
|
private readonly logger;
|
|
26
|
-
constructor(config: FastifyConfig, instance: FastifyInstance);
|
|
43
|
+
constructor(config: FastifyConfig, instance: FastifyInstance, context: Context);
|
|
27
44
|
getInstance(): FastifyInstance;
|
|
28
45
|
private registerRawBody;
|
|
46
|
+
private getRequestLogger;
|
|
29
47
|
private parseControllers;
|
|
30
48
|
private getStatusCode;
|
|
31
49
|
private generateRouteHandler;
|
|
@@ -14,7 +14,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
14
14
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
15
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
16
|
};
|
|
17
|
-
var FastifyRouter_1;
|
|
18
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
18
|
exports.FastifyRouter = void 0;
|
|
20
19
|
const assert_1 = __importDefault(require("assert"));
|
|
@@ -23,15 +22,18 @@ const common_1 = require("@shadow-library/common");
|
|
|
23
22
|
const deepmerge_1 = __importDefault(require("deepmerge"));
|
|
24
23
|
const constants_1 = require("../constants.js");
|
|
25
24
|
const decorators_1 = require("../decorators/index.js");
|
|
25
|
+
const services_1 = require("../services/index.js");
|
|
26
26
|
const httpMethods = Object.values(decorators_1.HttpMethod).filter(m => m !== decorators_1.HttpMethod.ALL);
|
|
27
|
-
let FastifyRouter =
|
|
27
|
+
let FastifyRouter = class FastifyRouter extends app_1.Router {
|
|
28
28
|
config;
|
|
29
29
|
instance;
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
context;
|
|
31
|
+
logger = common_1.Logger.getLogger(constants_1.NAMESPACE, 'FastifyRouter');
|
|
32
|
+
constructor(config, instance, context) {
|
|
32
33
|
super();
|
|
33
34
|
this.config = config;
|
|
34
35
|
this.instance = instance;
|
|
36
|
+
this.context = context;
|
|
35
37
|
}
|
|
36
38
|
getInstance() {
|
|
37
39
|
return this.instance;
|
|
@@ -46,6 +48,33 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends app_1.Router {
|
|
|
46
48
|
return parser(req, body.toString(), done);
|
|
47
49
|
});
|
|
48
50
|
}
|
|
51
|
+
getRequestLogger() {
|
|
52
|
+
return (req, res, done) => {
|
|
53
|
+
const startTime = process.hrtime();
|
|
54
|
+
res.raw.on('finish', () => {
|
|
55
|
+
const isLoggingDisabled = this.context.get('DISABLE_REQUEST_LOGGING') ?? false;
|
|
56
|
+
if (isLoggingDisabled)
|
|
57
|
+
return done();
|
|
58
|
+
const metadata = {};
|
|
59
|
+
metadata.rid = this.context.getRID();
|
|
60
|
+
metadata.url = req.url;
|
|
61
|
+
metadata.method = req.method;
|
|
62
|
+
metadata.status = res.statusCode;
|
|
63
|
+
metadata.service = req.headers['x-service'];
|
|
64
|
+
metadata.reqLen = req.headers['content-length'];
|
|
65
|
+
metadata.reqIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
|
66
|
+
metadata.resLen = res.getHeader('content-length');
|
|
67
|
+
const resTime = process.hrtime(startTime);
|
|
68
|
+
metadata.timeTaken = (resTime[0] * 1e3 + resTime[1] * 1e-6).toFixed(3);
|
|
69
|
+
if (req.query)
|
|
70
|
+
metadata.query = req.query;
|
|
71
|
+
if (req.body)
|
|
72
|
+
metadata.body = req.body;
|
|
73
|
+
this.logger.http('http', metadata);
|
|
74
|
+
});
|
|
75
|
+
return done();
|
|
76
|
+
};
|
|
77
|
+
}
|
|
49
78
|
parseControllers(controllers) {
|
|
50
79
|
const parsedControllers = { middlewares: [], routes: [] };
|
|
51
80
|
for (const controller of controllers) {
|
|
@@ -123,6 +152,10 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends app_1.Router {
|
|
|
123
152
|
const hasRawBody = routes.some(r => r.metadata.rawBody);
|
|
124
153
|
if (hasRawBody)
|
|
125
154
|
this.registerRawBody();
|
|
155
|
+
this.logger.debug('Registering the global middlewares');
|
|
156
|
+
this.instance.addHook('onRequest', this.context.init());
|
|
157
|
+
this.instance.addHook('onRequest', this.getRequestLogger());
|
|
158
|
+
this.logger.info('Registered global middlewares');
|
|
126
159
|
for (const route of routes) {
|
|
127
160
|
const metadata = route.metadata;
|
|
128
161
|
(0, assert_1.default)(metadata.path, 'Route path is required');
|
|
@@ -166,17 +199,18 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends app_1.Router {
|
|
|
166
199
|
this.logger.info(`server started at ${address}`);
|
|
167
200
|
}
|
|
168
201
|
async stop() {
|
|
169
|
-
this.logger.
|
|
202
|
+
this.logger.debug('stopping server');
|
|
170
203
|
await this.instance.close();
|
|
204
|
+
this.logger.info('server stopped');
|
|
171
205
|
}
|
|
172
206
|
mockRequest(options) {
|
|
173
207
|
return options ? this.instance.inject(options) : this.instance.inject();
|
|
174
208
|
}
|
|
175
209
|
};
|
|
176
210
|
exports.FastifyRouter = FastifyRouter;
|
|
177
|
-
exports.FastifyRouter = FastifyRouter =
|
|
211
|
+
exports.FastifyRouter = FastifyRouter = __decorate([
|
|
178
212
|
(0, app_1.Injectable)(),
|
|
179
213
|
__param(0, (0, app_1.Inject)(constants_1.FASTIFY_CONFIG)),
|
|
180
214
|
__param(1, (0, app_1.Inject)(constants_1.FASTIFY_INSTANCE)),
|
|
181
|
-
__metadata("design:paramtypes", [Object, Object])
|
|
215
|
+
__metadata("design:paramtypes", [Object, Object, services_1.Context])
|
|
182
216
|
], FastifyRouter);
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ValidationError } from '@shadow-library/common';
|
|
2
|
+
import { SchemaObject } from 'ajv';
|
|
2
3
|
import { FastifyInstance } from 'fastify';
|
|
3
|
-
import { FastifySchemaValidationError, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
4
|
+
import { FastifyRouteSchemaDef, FastifySchemaValidationError, FastifyValidationResult, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
4
5
|
import { FastifyConfig, FastifyModuleOptions } from './fastify-module.interface.js';
|
|
5
6
|
export declare const notFoundHandler: () => never;
|
|
7
|
+
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject>): FastifyValidationResult;
|
|
6
8
|
export declare function formatSchemaErrors(errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar): ValidationError;
|
|
7
9
|
export declare function createFastifyInstance(config: FastifyConfig, fastifyFactory?: FastifyModuleOptions['fastifyFactory']): Promise<FastifyInstance>;
|
|
@@ -1,14 +1,41 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.notFoundHandler = void 0;
|
|
7
|
+
exports.compileValidator = compileValidator;
|
|
4
8
|
exports.formatSchemaErrors = formatSchemaErrors;
|
|
5
9
|
exports.createFastifyInstance = createFastifyInstance;
|
|
10
|
+
const assert_1 = __importDefault(require("assert"));
|
|
6
11
|
const common_1 = require("@shadow-library/common");
|
|
12
|
+
const ajv_1 = __importDefault(require("ajv"));
|
|
7
13
|
const fastify_1 = require("fastify");
|
|
8
14
|
const server_error_1 = require("../server.error.js");
|
|
15
|
+
const allowedHttpParts = ['body', 'params', 'querystring'];
|
|
16
|
+
const strictValidator = new ajv_1.default({ allErrors: true, useDefaults: true, removeAdditional: true, strict: true });
|
|
17
|
+
const lenientValidator = new ajv_1.default({ allErrors: true, coerceTypes: true, useDefaults: true, removeAdditional: true, strict: true });
|
|
9
18
|
const notFoundError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S002);
|
|
10
19
|
const notFoundHandler = () => (0, common_1.throwError)(notFoundError);
|
|
11
20
|
exports.notFoundHandler = notFoundHandler;
|
|
21
|
+
function compileValidator(routeSchema) {
|
|
22
|
+
(0, assert_1.default)(allowedHttpParts.includes(routeSchema.httpPart), `Invalid httpPart: ${routeSchema.httpPart}`);
|
|
23
|
+
if (routeSchema.httpPart !== 'querystring')
|
|
24
|
+
return strictValidator.compile(routeSchema.schema);
|
|
25
|
+
const validate = lenientValidator.compile(routeSchema.schema);
|
|
26
|
+
return (data) => {
|
|
27
|
+
validate(data);
|
|
28
|
+
for (const error of validate.errors ?? []) {
|
|
29
|
+
const path = error.instancePath.substring(1);
|
|
30
|
+
const defaultValue = routeSchema.schema.properties?.[path]?.default;
|
|
31
|
+
if (defaultValue !== undefined)
|
|
32
|
+
data[path] = defaultValue;
|
|
33
|
+
else
|
|
34
|
+
delete data[path];
|
|
35
|
+
}
|
|
36
|
+
return { value: data };
|
|
37
|
+
};
|
|
38
|
+
}
|
|
12
39
|
function formatSchemaErrors(errors, dataVar) {
|
|
13
40
|
const validationError = new common_1.ValidationError();
|
|
14
41
|
for (const error of errors) {
|
|
@@ -29,5 +56,6 @@ async function createFastifyInstance(config, fastifyFactory) {
|
|
|
29
56
|
instance.setSchemaErrorFormatter(formatSchemaErrors);
|
|
30
57
|
instance.setNotFoundHandler(exports.notFoundHandler);
|
|
31
58
|
instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
|
|
59
|
+
instance.setValidatorCompiler(compileValidator);
|
|
32
60
|
return fastifyFactory ? await fastifyFactory(instance) : instance;
|
|
33
61
|
}
|
|
@@ -3,9 +3,9 @@ type Key = string | symbol;
|
|
|
3
3
|
export declare class Context {
|
|
4
4
|
private readonly storage;
|
|
5
5
|
init(): MiddlewareHandler;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
get<T>(key: Key, throwOnMissing: true): T;
|
|
7
|
+
get<T>(key: Key, throwOnMissing?: false): T | null;
|
|
8
|
+
set<T>(key: Key, value: T): this;
|
|
9
9
|
getRequest(): Request;
|
|
10
10
|
getResponse(): Response;
|
|
11
11
|
getRID(): string;
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { AppError, ErrorType, Logger, ValidationError } from '@shadow-library/common';
|
|
2
|
+
import { NAMESPACE } from '../constants.js';
|
|
2
3
|
import { ServerError, ServerErrorCode } from '../server.error.js';
|
|
3
4
|
const unexpectedError = new ServerError(ServerErrorCode.S001);
|
|
4
5
|
const validationError = new ServerError(ServerErrorCode.S003);
|
|
5
6
|
export class DefaultErrorHandler {
|
|
6
|
-
logger = Logger.getLogger(DefaultErrorHandler
|
|
7
|
+
logger = Logger.getLogger(NAMESPACE, 'DefaultErrorHandler');
|
|
7
8
|
parseFastifyError(err) {
|
|
8
9
|
if (err.statusCode === 500)
|
|
9
10
|
return { statusCode: 500, error: unexpectedError.toObject() };
|
|
10
|
-
return {
|
|
11
|
-
statusCode: err.statusCode,
|
|
12
|
-
error: {
|
|
13
|
-
code: err.code,
|
|
14
|
-
type: ErrorType.CLIENT_ERROR,
|
|
15
|
-
message: err.message,
|
|
16
|
-
},
|
|
17
|
-
};
|
|
11
|
+
return { statusCode: err.statusCode, error: { code: err.code, type: ErrorType.CLIENT_ERROR, message: err.message } };
|
|
18
12
|
}
|
|
19
13
|
handle(err, _req, res) {
|
|
20
14
|
this.logger.warn('Handling error', err);
|
package/esm/constants.d.ts
CHANGED
package/esm/constants.js
CHANGED
|
@@ -3,6 +3,7 @@ import { type FastifyInstance } from 'fastify';
|
|
|
3
3
|
import { Chain as MockRequestChain, InjectOptions as MockRequestOptions, Response as MockResponse } from 'light-my-request';
|
|
4
4
|
import { JsonObject } from 'type-fest';
|
|
5
5
|
import { HttpRequest, HttpResponse, ServerMetadata } from '../interfaces/index.js';
|
|
6
|
+
import { Context } from '../services/index.js';
|
|
6
7
|
import { type FastifyConfig } from './fastify-module.interface.js';
|
|
7
8
|
declare module 'fastify' {
|
|
8
9
|
interface FastifyRequest {
|
|
@@ -19,13 +20,30 @@ export interface RequestContext {
|
|
|
19
20
|
query: Record<string, string>;
|
|
20
21
|
body: JsonObject;
|
|
21
22
|
}
|
|
23
|
+
export interface RequestMetadata {
|
|
24
|
+
rid?: string;
|
|
25
|
+
srid?: string;
|
|
26
|
+
method?: string;
|
|
27
|
+
url?: string;
|
|
28
|
+
status?: number;
|
|
29
|
+
reqLen?: string;
|
|
30
|
+
reqIp?: string;
|
|
31
|
+
resLen?: string;
|
|
32
|
+
timeTaken?: string;
|
|
33
|
+
body?: any;
|
|
34
|
+
query?: object;
|
|
35
|
+
service?: string;
|
|
36
|
+
[key: string]: any;
|
|
37
|
+
}
|
|
22
38
|
export declare class FastifyRouter extends Router {
|
|
23
39
|
private readonly config;
|
|
24
40
|
private readonly instance;
|
|
41
|
+
private readonly context;
|
|
25
42
|
private readonly logger;
|
|
26
|
-
constructor(config: FastifyConfig, instance: FastifyInstance);
|
|
43
|
+
constructor(config: FastifyConfig, instance: FastifyInstance, context: Context);
|
|
27
44
|
getInstance(): FastifyInstance;
|
|
28
45
|
private registerRawBody;
|
|
46
|
+
private getRequestLogger;
|
|
29
47
|
private parseControllers;
|
|
30
48
|
private getStatusCode;
|
|
31
49
|
private generateRouteHandler;
|
|
@@ -10,22 +10,24 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
11
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
12
|
};
|
|
13
|
-
var FastifyRouter_1;
|
|
14
13
|
import assert from 'assert';
|
|
15
14
|
import { Inject, Injectable, Router } from '@shadow-library/app';
|
|
16
15
|
import { InternalError, Logger, utils } from '@shadow-library/common';
|
|
17
16
|
import merge from 'deepmerge';
|
|
18
|
-
import { FASTIFY_CONFIG, FASTIFY_INSTANCE, HTTP_CONTROLLER_INPUTS, HTTP_CONTROLLER_TYPE } from '../constants.js';
|
|
17
|
+
import { FASTIFY_CONFIG, FASTIFY_INSTANCE, HTTP_CONTROLLER_INPUTS, HTTP_CONTROLLER_TYPE, NAMESPACE } from '../constants.js';
|
|
19
18
|
import { HttpMethod } from '../decorators/index.js';
|
|
19
|
+
import { Context } from '../services/index.js';
|
|
20
20
|
const httpMethods = Object.values(HttpMethod).filter(m => m !== HttpMethod.ALL);
|
|
21
|
-
let FastifyRouter =
|
|
21
|
+
let FastifyRouter = class FastifyRouter extends Router {
|
|
22
22
|
config;
|
|
23
23
|
instance;
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
context;
|
|
25
|
+
logger = Logger.getLogger(NAMESPACE, 'FastifyRouter');
|
|
26
|
+
constructor(config, instance, context) {
|
|
26
27
|
super();
|
|
27
28
|
this.config = config;
|
|
28
29
|
this.instance = instance;
|
|
30
|
+
this.context = context;
|
|
29
31
|
}
|
|
30
32
|
getInstance() {
|
|
31
33
|
return this.instance;
|
|
@@ -40,6 +42,33 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends Router {
|
|
|
40
42
|
return parser(req, body.toString(), done);
|
|
41
43
|
});
|
|
42
44
|
}
|
|
45
|
+
getRequestLogger() {
|
|
46
|
+
return (req, res, done) => {
|
|
47
|
+
const startTime = process.hrtime();
|
|
48
|
+
res.raw.on('finish', () => {
|
|
49
|
+
const isLoggingDisabled = this.context.get('DISABLE_REQUEST_LOGGING') ?? false;
|
|
50
|
+
if (isLoggingDisabled)
|
|
51
|
+
return done();
|
|
52
|
+
const metadata = {};
|
|
53
|
+
metadata.rid = this.context.getRID();
|
|
54
|
+
metadata.url = req.url;
|
|
55
|
+
metadata.method = req.method;
|
|
56
|
+
metadata.status = res.statusCode;
|
|
57
|
+
metadata.service = req.headers['x-service'];
|
|
58
|
+
metadata.reqLen = req.headers['content-length'];
|
|
59
|
+
metadata.reqIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
|
60
|
+
metadata.resLen = res.getHeader('content-length');
|
|
61
|
+
const resTime = process.hrtime(startTime);
|
|
62
|
+
metadata.timeTaken = (resTime[0] * 1e3 + resTime[1] * 1e-6).toFixed(3);
|
|
63
|
+
if (req.query)
|
|
64
|
+
metadata.query = req.query;
|
|
65
|
+
if (req.body)
|
|
66
|
+
metadata.body = req.body;
|
|
67
|
+
this.logger.http('http', metadata);
|
|
68
|
+
});
|
|
69
|
+
return done();
|
|
70
|
+
};
|
|
71
|
+
}
|
|
43
72
|
parseControllers(controllers) {
|
|
44
73
|
const parsedControllers = { middlewares: [], routes: [] };
|
|
45
74
|
for (const controller of controllers) {
|
|
@@ -117,6 +146,10 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends Router {
|
|
|
117
146
|
const hasRawBody = routes.some(r => r.metadata.rawBody);
|
|
118
147
|
if (hasRawBody)
|
|
119
148
|
this.registerRawBody();
|
|
149
|
+
this.logger.debug('Registering the global middlewares');
|
|
150
|
+
this.instance.addHook('onRequest', this.context.init());
|
|
151
|
+
this.instance.addHook('onRequest', this.getRequestLogger());
|
|
152
|
+
this.logger.info('Registered global middlewares');
|
|
120
153
|
for (const route of routes) {
|
|
121
154
|
const metadata = route.metadata;
|
|
122
155
|
assert(metadata.path, 'Route path is required');
|
|
@@ -160,17 +193,18 @@ let FastifyRouter = FastifyRouter_1 = class FastifyRouter extends Router {
|
|
|
160
193
|
this.logger.info(`server started at ${address}`);
|
|
161
194
|
}
|
|
162
195
|
async stop() {
|
|
163
|
-
this.logger.
|
|
196
|
+
this.logger.debug('stopping server');
|
|
164
197
|
await this.instance.close();
|
|
198
|
+
this.logger.info('server stopped');
|
|
165
199
|
}
|
|
166
200
|
mockRequest(options) {
|
|
167
201
|
return options ? this.instance.inject(options) : this.instance.inject();
|
|
168
202
|
}
|
|
169
203
|
};
|
|
170
|
-
FastifyRouter =
|
|
204
|
+
FastifyRouter = __decorate([
|
|
171
205
|
Injectable(),
|
|
172
206
|
__param(0, Inject(FASTIFY_CONFIG)),
|
|
173
207
|
__param(1, Inject(FASTIFY_INSTANCE)),
|
|
174
|
-
__metadata("design:paramtypes", [Object, Object])
|
|
208
|
+
__metadata("design:paramtypes", [Object, Object, Context])
|
|
175
209
|
], FastifyRouter);
|
|
176
210
|
export { FastifyRouter };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ValidationError } from '@shadow-library/common';
|
|
2
|
+
import { SchemaObject } from 'ajv';
|
|
2
3
|
import { FastifyInstance } from 'fastify';
|
|
3
|
-
import { FastifySchemaValidationError, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
4
|
+
import { FastifyRouteSchemaDef, FastifySchemaValidationError, FastifyValidationResult, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
4
5
|
import { FastifyConfig, FastifyModuleOptions } from './fastify-module.interface.js';
|
|
5
6
|
export declare const notFoundHandler: () => never;
|
|
7
|
+
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject>): FastifyValidationResult;
|
|
6
8
|
export declare function formatSchemaErrors(errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar): ValidationError;
|
|
7
9
|
export declare function createFastifyInstance(config: FastifyConfig, fastifyFactory?: FastifyModuleOptions['fastifyFactory']): Promise<FastifyInstance>;
|
|
@@ -1,8 +1,31 @@
|
|
|
1
|
+
import assert from 'assert';
|
|
1
2
|
import { ValidationError, throwError, utils } from '@shadow-library/common';
|
|
3
|
+
import Ajv from 'ajv';
|
|
2
4
|
import { fastify } from 'fastify';
|
|
3
5
|
import { ServerError, ServerErrorCode } from '../server.error.js';
|
|
6
|
+
const allowedHttpParts = ['body', 'params', 'querystring'];
|
|
7
|
+
const strictValidator = new Ajv({ allErrors: true, useDefaults: true, removeAdditional: true, strict: true });
|
|
8
|
+
const lenientValidator = new Ajv({ allErrors: true, coerceTypes: true, useDefaults: true, removeAdditional: true, strict: true });
|
|
4
9
|
const notFoundError = new ServerError(ServerErrorCode.S002);
|
|
5
10
|
export const notFoundHandler = () => throwError(notFoundError);
|
|
11
|
+
export function compileValidator(routeSchema) {
|
|
12
|
+
assert(allowedHttpParts.includes(routeSchema.httpPart), `Invalid httpPart: ${routeSchema.httpPart}`);
|
|
13
|
+
if (routeSchema.httpPart !== 'querystring')
|
|
14
|
+
return strictValidator.compile(routeSchema.schema);
|
|
15
|
+
const validate = lenientValidator.compile(routeSchema.schema);
|
|
16
|
+
return (data) => {
|
|
17
|
+
validate(data);
|
|
18
|
+
for (const error of validate.errors ?? []) {
|
|
19
|
+
const path = error.instancePath.substring(1);
|
|
20
|
+
const defaultValue = routeSchema.schema.properties?.[path]?.default;
|
|
21
|
+
if (defaultValue !== undefined)
|
|
22
|
+
data[path] = defaultValue;
|
|
23
|
+
else
|
|
24
|
+
delete data[path];
|
|
25
|
+
}
|
|
26
|
+
return { value: data };
|
|
27
|
+
};
|
|
28
|
+
}
|
|
6
29
|
export function formatSchemaErrors(errors, dataVar) {
|
|
7
30
|
const validationError = new ValidationError();
|
|
8
31
|
for (const error of errors) {
|
|
@@ -23,5 +46,6 @@ export async function createFastifyInstance(config, fastifyFactory) {
|
|
|
23
46
|
instance.setSchemaErrorFormatter(formatSchemaErrors);
|
|
24
47
|
instance.setNotFoundHandler(notFoundHandler);
|
|
25
48
|
instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
|
|
49
|
+
instance.setValidatorCompiler(compileValidator);
|
|
26
50
|
return fastifyFactory ? await fastifyFactory(instance) : instance;
|
|
27
51
|
}
|
|
@@ -3,9 +3,9 @@ type Key = string | symbol;
|
|
|
3
3
|
export declare class Context {
|
|
4
4
|
private readonly storage;
|
|
5
5
|
init(): MiddlewareHandler;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
get<T>(key: Key, throwOnMissing: true): T;
|
|
7
|
+
get<T>(key: Key, throwOnMissing?: false): T | null;
|
|
8
|
+
set<T>(key: Key, value: T): this;
|
|
9
9
|
getRequest(): Request;
|
|
10
10
|
getResponse(): Response;
|
|
11
11
|
getRID(): string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadow-library/fastify",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.5",
|
|
5
|
+
"sideEffects": false,
|
|
5
6
|
"description": "A Fastify wrapper featuring decorator-based routing, middleware and error handling",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
@@ -14,17 +15,19 @@
|
|
|
14
15
|
},
|
|
15
16
|
"homepage": "https://github.com/shadow-library/fastify#readme",
|
|
16
17
|
"dependencies": {
|
|
17
|
-
"@
|
|
18
|
+
"@fastify/middie": "^9.0.3",
|
|
19
|
+
"ajv": "^8.17.1",
|
|
18
20
|
"deepmerge": "^4.3.1",
|
|
19
|
-
"fastify": "^5.
|
|
21
|
+
"fastify": "^5.3.3",
|
|
20
22
|
"jest": "^29.7.0",
|
|
21
|
-
"type-fest": "^4.
|
|
22
|
-
"uuid": "^11.0
|
|
23
|
+
"type-fest": "^4.41.0",
|
|
24
|
+
"uuid": "^11.1.0"
|
|
23
25
|
},
|
|
24
26
|
"peerDependencies": {
|
|
25
27
|
"@fastify/view": "^10.0.0",
|
|
26
|
-
"@shadow-library/app": "^1.0.
|
|
27
|
-
"@shadow-library/
|
|
28
|
+
"@shadow-library/app": "^1.0.7",
|
|
29
|
+
"@shadow-library/class-schema": "^0.0.5",
|
|
30
|
+
"@shadow-library/common": "^1.0.13",
|
|
28
31
|
"reflect-metadata": "^0.2.2"
|
|
29
32
|
},
|
|
30
33
|
"peerDependenciesMeta": {
|