@naturalcycles/backend-lib 5.1.0 → 5.3.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.
@@ -49,25 +49,25 @@ function httpDBRequestHandler(db) {
49
49
  //
50
50
  // })
51
51
  // getByIds
52
- router.put('/getByIds', (0, __1.reqValidation)('body', getByIdsInputSchema), async (req, res) => {
53
- const { table, ids, opt } = req.body;
52
+ router.put('/getByIds', async (req, res) => {
53
+ const { table, ids, opt } = __1.validateRequest.body(req, getByIdsInputSchema);
54
54
  res.json(await db.getByIds(table, ids, opt));
55
55
  });
56
56
  // runQuery
57
- router.put('/runQuery', (0, __1.reqValidation)('body', runQueryInputSchema), async (req, res) => {
58
- const { query, opt } = req.body;
57
+ router.put('/runQuery', async (req, res) => {
58
+ const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
59
59
  const q = db_lib_1.DBQuery.fromPlainObject(query);
60
60
  res.json(await db.runQuery(q, opt));
61
61
  });
62
62
  // runQueryCount
63
- router.put('/runQueryCount', (0, __1.reqValidation)('body', runQueryInputSchema), async (req, res) => {
64
- const { query, opt } = req.body;
63
+ router.put('/runQueryCount', async (req, res) => {
64
+ const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
65
65
  const q = db_lib_1.DBQuery.fromPlainObject(query);
66
66
  res.json(await db.runQueryCount(q, opt));
67
67
  });
68
68
  // saveBatch
69
- router.put('/saveBatch', (0, __1.reqValidation)('body', saveBatchInputSchema), async (req, res) => {
70
- const { table, rows, opt } = req.body;
69
+ router.put('/saveBatch', async (req, res) => {
70
+ const { table, rows, opt } = __1.validateRequest.body(req, saveBatchInputSchema);
71
71
  await db.saveBatch(table, rows, opt);
72
72
  res.end();
73
73
  });
@@ -77,8 +77,8 @@ function httpDBRequestHandler(db) {
77
77
  // res.json(await db.deleteByIds(table, ids, opt))
78
78
  // })
79
79
  // deleteByQuery
80
- router.put('/deleteByQuery', (0, __1.reqValidation)('body', runQueryInputSchema), async (req, res) => {
81
- const { query, opt } = req.body;
80
+ router.put('/deleteByQuery', async (req, res) => {
81
+ const { query, opt } = __1.validateRequest.body(req, runQueryInputSchema);
82
82
  const q = db_lib_1.DBQuery.fromPlainObject(query);
83
83
  res.json(await db.deleteByQuery(q, opt));
84
84
  });
package/dist/index.d.ts CHANGED
@@ -20,11 +20,11 @@ export * from './server/notFoundMiddleware';
20
20
  export * from './server/okMiddleware';
21
21
  export * from './server/basicAuthMiddleware';
22
22
  export * from './server/requestTimeoutMiddleware';
23
- export * from './server/reqValidationMiddleware';
23
+ export * from './server/validation/validateRequest';
24
24
  export * from './server/simpleRequestLoggerMiddleware';
25
25
  export * from './server/serverStatusMiddleware';
26
- export * from './server/validateMiddleware';
27
- export * from './server/zodValidateMiddleware';
26
+ export * from './server/validation/validateMiddleware';
27
+ export * from './server/validation/zodValidateMiddleware';
28
28
  export * from './server/request.log.util';
29
29
  export * from './server/startServer';
30
30
  export * from './server/startServer.model';
package/dist/index.js CHANGED
@@ -25,11 +25,11 @@ tslib_1.__exportStar(require("./server/notFoundMiddleware"), exports);
25
25
  tslib_1.__exportStar(require("./server/okMiddleware"), exports);
26
26
  tslib_1.__exportStar(require("./server/basicAuthMiddleware"), exports);
27
27
  tslib_1.__exportStar(require("./server/requestTimeoutMiddleware"), exports);
28
- tslib_1.__exportStar(require("./server/reqValidationMiddleware"), exports);
28
+ tslib_1.__exportStar(require("./server/validation/validateRequest"), exports);
29
29
  tslib_1.__exportStar(require("./server/simpleRequestLoggerMiddleware"), exports);
30
30
  tslib_1.__exportStar(require("./server/serverStatusMiddleware"), exports);
31
- tslib_1.__exportStar(require("./server/validateMiddleware"), exports);
32
- tslib_1.__exportStar(require("./server/zodValidateMiddleware"), exports);
31
+ tslib_1.__exportStar(require("./server/validation/validateMiddleware"), exports);
32
+ tslib_1.__exportStar(require("./server/validation/zodValidateMiddleware"), exports);
33
33
  tslib_1.__exportStar(require("./server/request.log.util"), exports);
34
34
  tslib_1.__exportStar(require("./server/startServer"), exports);
35
35
  tslib_1.__exportStar(require("./server/startServer.model"), exports);
@@ -57,7 +57,6 @@ function respondWithError(req, res, err) {
57
57
  // Otherwise, it breaks the _isHttpErrorResponse function check, and error get formatted/detected wrongly
58
58
  httpError.data['httpStatusCode'] = httpError.data.backendResponseStatusCode;
59
59
  httpError.data.headersSent = headersSent || undefined;
60
- httpError.data.report ||= undefined; // set to undefined if false
61
60
  (0, js_lib_1._filterUndefinedValues)(httpError.data, true);
62
61
  formatError?.(httpError); // Mutates
63
62
  res.status(httpError.data.backendResponseStatusCode).json({
@@ -1,3 +1,4 @@
1
+ /// <reference types="cookie-parser" />
1
2
  /// <reference types="node" />
2
3
  import type { CommonLogFunction, Promisable } from '@naturalcycles/js-lib';
3
4
  import type { IRouter, Request, NextFunction, Response, Application } from 'express';
@@ -1,7 +1,7 @@
1
1
  import { JsonSchema, JsonSchemaBuilder } from '@naturalcycles/js-lib';
2
2
  import { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib';
3
- import { ReqValidationOptions } from './reqValidationMiddleware';
4
- import { BackendRequestHandler } from './server.model';
3
+ import { BackendRequestHandler } from '../server.model';
4
+ import { ReqValidationOptions } from './validateRequest';
5
5
  export declare function validateBody(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
6
6
  export declare function validateParams(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
7
7
  export declare function validateQuery(schema: JsonSchema | JsonSchemaBuilder | AjvSchema, opt?: ReqValidationOptions<AjvValidationError>): BackendRequestHandler;
@@ -26,7 +26,7 @@ function validateObject(prop, schema, opt = {}) {
26
26
  const ajvSchema = nodejs_lib_1.AjvSchema.create(schema, {
27
27
  objectName: `request ${prop}`,
28
28
  });
29
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report;
29
+ const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
30
30
  return (req, res, next) => {
31
31
  const error = ajvSchema.getValidationError(req[prop]);
32
32
  if (error) {
@@ -1,5 +1,5 @@
1
1
  import { AnySchema, JoiValidationError } from '@naturalcycles/nodejs-lib';
2
- import { BackendRequestHandler } from './server.model';
2
+ import { BackendRequest } from '../server.model';
3
3
  export interface ReqValidationOptions<ERR extends Error> {
4
4
  /**
5
5
  * Pass a 'dot-paths' (e.g `pw`, or `input.pw`) that needs to be redacted from the output, in case of error.
@@ -12,4 +12,11 @@ export interface ReqValidationOptions<ERR extends Error> {
12
12
  */
13
13
  report?: boolean | ((err: ERR) => boolean);
14
14
  }
15
- export declare function reqValidation(reqProperty: 'body' | 'params' | 'query', schema: AnySchema, opt?: ReqValidationOptions<JoiValidationError>): BackendRequestHandler;
15
+ declare class ValidateRequest {
16
+ body<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
17
+ query<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
18
+ params<T>(req: BackendRequest, schema: AnySchema<T>, opt?: ReqValidationOptions<JoiValidationError>): T;
19
+ private validate;
20
+ }
21
+ export declare const validateRequest: ValidateRequest;
22
+ export {};
@@ -1,40 +1,54 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.reqValidation = void 0;
3
+ exports.validateRequest = void 0;
4
4
  const js_lib_1 = require("@naturalcycles/js-lib");
5
5
  const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
6
6
  const REDACTED = 'REDACTED';
7
- function reqValidation(reqProperty, schema, opt = {}) {
8
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report;
9
- return (req, res, next) => {
7
+ /**
8
+ * Mutates error
9
+ */
10
+ function redact(redactPaths, obj, error) {
11
+ redactPaths
12
+ .map(path => (0, js_lib_1._get)(obj, path))
13
+ .filter(Boolean)
14
+ .forEach(secret => {
15
+ error.message = error.message.replace(secret, REDACTED);
16
+ });
17
+ }
18
+ class ValidateRequest {
19
+ body(req, schema, opt = {}) {
20
+ return this.validate(req, 'body', schema, opt);
21
+ }
22
+ query(req, schema, opt = {}) {
23
+ return this.validate(req, 'query', schema, opt);
24
+ }
25
+ params(req, schema, opt = {}) {
26
+ return this.validate(req, 'params', schema, opt);
27
+ }
28
+ validate(req, reqProperty, schema, opt = {}) {
10
29
  const { value, error } = (0, nodejs_lib_1.getValidationResult)(req[reqProperty], schema, `request ${reqProperty}`);
11
30
  if (error) {
12
- const report = reportPredicate(error);
31
+ let report;
32
+ if (typeof opt.report === 'boolean') {
33
+ report = opt.report;
34
+ }
35
+ else if (typeof opt.report === 'function') {
36
+ report = opt.report(error);
37
+ }
13
38
  if (opt.redactPaths) {
14
39
  redact(opt.redactPaths, req[reqProperty], error);
15
40
  error.data.joiValidationErrorItems.length = 0; // clears the array
16
41
  delete error.data.annotation;
17
42
  }
18
- return next(new js_lib_1.AppError(error.message, {
43
+ throw new js_lib_1.AppError(error.message, {
19
44
  backendResponseStatusCode: 400,
20
45
  report,
21
46
  ...error.data,
22
- }));
47
+ });
23
48
  }
24
49
  // mutate req to replace the property with the value, converted by Joi
25
50
  req[reqProperty] = value;
26
- next();
27
- };
28
- }
29
- exports.reqValidation = reqValidation;
30
- /**
31
- * Mutates error
32
- */
33
- function redact(redactPaths, obj, error) {
34
- redactPaths
35
- .map(path => (0, js_lib_1._get)(obj, path))
36
- .filter(Boolean)
37
- .forEach(secret => {
38
- error.message = error.message.replace(secret, REDACTED);
39
- });
51
+ return value;
52
+ }
40
53
  }
54
+ exports.validateRequest = new ValidateRequest();
@@ -1,6 +1,6 @@
1
1
  import { ZodSchema, ZodValidationError } from '@naturalcycles/js-lib';
2
- import { ReqValidationOptions } from './reqValidationMiddleware';
3
- import { BackendRequestHandler } from './server.model';
2
+ import { BackendRequestHandler } from '../server.model';
3
+ import { ReqValidationOptions } from './validateRequest';
4
4
  /**
5
5
  * Validates req property (body, params or query).
6
6
  * Supports Joi schema or AjvSchema (from nodejs-lib).
@@ -10,7 +10,7 @@ const REDACTED = 'REDACTED';
10
10
  * Throws http 400 on error.
11
11
  */
12
12
  function zodReqValidate(prop, schema, opt = {}) {
13
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report;
13
+ const reportPredicate = typeof opt.report === 'function' ? opt.report : () => opt.report;
14
14
  return (req, res, next) => {
15
15
  const { error } = (0, js_lib_1.zSafeValidate)(req[prop], schema);
16
16
  if (!error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
- "version": "5.1.0",
3
+ "version": "5.3.0",
4
4
  "scripts": {
5
5
  "prepare": "husky",
6
6
  "dev": "APP_ENV=dev nodemon",
@@ -36,7 +36,7 @@
36
36
  "yargs": "^17.0.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@naturalcycles/bench-lib": "^1.0.7",
39
+ "@naturalcycles/bench-lib": "^2.0.0",
40
40
  "@naturalcycles/dev-lib": "^13.0.0",
41
41
  "@sentry/node": "^7.0.0",
42
42
  "@types/ejs": "^3.0.0",
@@ -12,7 +12,7 @@ import {
12
12
  } from '@naturalcycles/db-lib/dist/validation'
13
13
  import { ObjectWithId } from '@naturalcycles/js-lib'
14
14
  import { anyObjectSchema, arraySchema, objectSchema, stringSchema } from '@naturalcycles/nodejs-lib'
15
- import { BackendRouter, getDefaultRouter, reqValidation } from '..'
15
+ import { BackendRouter, getDefaultRouter, validateRequest } from '..'
16
16
 
17
17
  export interface GetByIdsInput {
18
18
  table: string
@@ -84,28 +84,28 @@ export function httpDBRequestHandler(db: CommonDB): BackendRouter {
84
84
  // })
85
85
 
86
86
  // getByIds
87
- router.put('/getByIds', reqValidation('body', getByIdsInputSchema), async (req, res) => {
88
- const { table, ids, opt } = req.body as GetByIdsInput
87
+ router.put('/getByIds', async (req, res) => {
88
+ const { table, ids, opt } = validateRequest.body(req, getByIdsInputSchema)
89
89
  res.json(await db.getByIds(table, ids, opt))
90
90
  })
91
91
 
92
92
  // runQuery
93
- router.put('/runQuery', reqValidation('body', runQueryInputSchema), async (req, res) => {
94
- const { query, opt } = req.body as RunQueryInput
93
+ router.put('/runQuery', async (req, res) => {
94
+ const { query, opt } = validateRequest.body(req, runQueryInputSchema)
95
95
  const q = DBQuery.fromPlainObject(query)
96
96
  res.json(await db.runQuery(q, opt))
97
97
  })
98
98
 
99
99
  // runQueryCount
100
- router.put('/runQueryCount', reqValidation('body', runQueryInputSchema), async (req, res) => {
101
- const { query, opt } = req.body as RunQueryInput
100
+ router.put('/runQueryCount', async (req, res) => {
101
+ const { query, opt } = validateRequest.body(req, runQueryInputSchema)
102
102
  const q = DBQuery.fromPlainObject(query)
103
103
  res.json(await db.runQueryCount(q, opt))
104
104
  })
105
105
 
106
106
  // saveBatch
107
- router.put('/saveBatch', reqValidation('body', saveBatchInputSchema), async (req, res) => {
108
- const { table, rows, opt } = req.body as SaveBatchInput
107
+ router.put('/saveBatch', async (req, res) => {
108
+ const { table, rows, opt } = validateRequest.body(req, saveBatchInputSchema)
109
109
  await db.saveBatch(table, rows, opt)
110
110
  res.end()
111
111
  })
@@ -117,8 +117,8 @@ export function httpDBRequestHandler(db: CommonDB): BackendRouter {
117
117
  // })
118
118
 
119
119
  // deleteByQuery
120
- router.put('/deleteByQuery', reqValidation('body', runQueryInputSchema), async (req, res) => {
121
- const { query, opt } = req.body as RunQueryInput
120
+ router.put('/deleteByQuery', async (req, res) => {
121
+ const { query, opt } = validateRequest.body(req, runQueryInputSchema)
122
122
  const q = DBQuery.fromPlainObject(query)
123
123
  res.json(await db.deleteByQuery(q, opt))
124
124
  })
package/src/index.ts CHANGED
@@ -20,11 +20,11 @@ export * from './server/notFoundMiddleware'
20
20
  export * from './server/okMiddleware'
21
21
  export * from './server/basicAuthMiddleware'
22
22
  export * from './server/requestTimeoutMiddleware'
23
- export * from './server/reqValidationMiddleware'
23
+ export * from './server/validation/validateRequest'
24
24
  export * from './server/simpleRequestLoggerMiddleware'
25
25
  export * from './server/serverStatusMiddleware'
26
- export * from './server/validateMiddleware'
27
- export * from './server/zodValidateMiddleware'
26
+ export * from './server/validation/validateMiddleware'
27
+ export * from './server/validation/zodValidateMiddleware'
28
28
  export * from './server/request.log.util'
29
29
  export * from './server/startServer'
30
30
  export * from './server/startServer.model'
@@ -90,7 +90,6 @@ export function respondWithError(req: BackendRequest, res: BackendResponse, err:
90
90
  // Otherwise, it breaks the _isHttpErrorResponse function check, and error get formatted/detected wrongly
91
91
  httpError.data['httpStatusCode'] = httpError.data.backendResponseStatusCode
92
92
  httpError.data.headersSent = headersSent || undefined
93
- httpError.data.report ||= undefined // set to undefined if false
94
93
  _filterUndefinedValues(httpError.data, true)
95
94
 
96
95
  formatError?.(httpError) // Mutates
@@ -1,7 +1,7 @@
1
1
  import { JsonSchema, JsonSchemaBuilder, _get, AppError } from '@naturalcycles/js-lib'
2
2
  import { AjvSchema, AjvValidationError } from '@naturalcycles/nodejs-lib'
3
- import { ReqValidationOptions } from './reqValidationMiddleware'
4
- import { BackendRequestHandler } from './server.model'
3
+ import { BackendRequestHandler } from '../server.model'
4
+ import { ReqValidationOptions } from './validateRequest'
5
5
 
6
6
  const REDACTED = 'REDACTED'
7
7
 
@@ -41,7 +41,8 @@ function validateObject(
41
41
  objectName: `request ${prop}`,
42
42
  })
43
43
 
44
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report
44
+ const reportPredicate =
45
+ typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
45
46
 
46
47
  return (req, res, next) => {
47
48
  const error = ajvSchema.getValidationError(req[prop])
@@ -1,6 +1,6 @@
1
1
  import { _get, AppError } from '@naturalcycles/js-lib'
2
2
  import { AnySchema, getValidationResult, JoiValidationError } from '@naturalcycles/nodejs-lib'
3
- import { BackendRequestHandler } from './server.model'
3
+ import { BackendRequest } from '../server.model'
4
4
 
5
5
  const REDACTED = 'REDACTED'
6
6
 
@@ -18,17 +18,57 @@ export interface ReqValidationOptions<ERR extends Error> {
18
18
  report?: boolean | ((err: ERR) => boolean)
19
19
  }
20
20
 
21
- export function reqValidation(
22
- reqProperty: 'body' | 'params' | 'query',
23
- schema: AnySchema,
24
- opt: ReqValidationOptions<JoiValidationError> = {},
25
- ): BackendRequestHandler {
26
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report
21
+ /**
22
+ * Mutates error
23
+ */
24
+ function redact(redactPaths: string[], obj: any, error: Error): void {
25
+ redactPaths
26
+ .map(path => _get(obj, path) as string)
27
+ .filter(Boolean)
28
+ .forEach(secret => {
29
+ error.message = error.message.replace(secret, REDACTED)
30
+ })
31
+ }
32
+
33
+ class ValidateRequest {
34
+ body<T>(
35
+ req: BackendRequest,
36
+ schema: AnySchema<T>,
37
+ opt: ReqValidationOptions<JoiValidationError> = {},
38
+ ): T {
39
+ return this.validate(req, 'body', schema, opt)
40
+ }
41
+
42
+ query<T>(
43
+ req: BackendRequest,
44
+ schema: AnySchema<T>,
45
+ opt: ReqValidationOptions<JoiValidationError> = {},
46
+ ): T {
47
+ return this.validate(req, 'query', schema, opt)
48
+ }
27
49
 
28
- return (req, res, next) => {
50
+ params<T>(
51
+ req: BackendRequest,
52
+ schema: AnySchema<T>,
53
+ opt: ReqValidationOptions<JoiValidationError> = {},
54
+ ): T {
55
+ return this.validate(req, 'params', schema, opt)
56
+ }
57
+
58
+ private validate<T>(
59
+ req: BackendRequest,
60
+ reqProperty: 'body' | 'params' | 'query',
61
+ schema: AnySchema<T>,
62
+ opt: ReqValidationOptions<JoiValidationError> = {},
63
+ ): T {
29
64
  const { value, error } = getValidationResult(req[reqProperty], schema, `request ${reqProperty}`)
30
65
  if (error) {
31
- const report = reportPredicate(error)
66
+ let report: boolean | undefined
67
+ if (typeof opt.report === 'boolean') {
68
+ report = opt.report
69
+ } else if (typeof opt.report === 'function') {
70
+ report = opt.report(error)
71
+ }
32
72
 
33
73
  if (opt.redactPaths) {
34
74
  redact(opt.redactPaths, req[reqProperty], error)
@@ -36,29 +76,17 @@ export function reqValidation(
36
76
  delete error.data.annotation
37
77
  }
38
78
 
39
- return next(
40
- new AppError(error.message, {
41
- backendResponseStatusCode: 400,
42
- report,
43
- ...error.data,
44
- }),
45
- )
79
+ throw new AppError(error.message, {
80
+ backendResponseStatusCode: 400,
81
+ report,
82
+ ...error.data,
83
+ })
46
84
  }
47
85
 
48
86
  // mutate req to replace the property with the value, converted by Joi
49
87
  req[reqProperty] = value
50
- next()
88
+ return value
51
89
  }
52
90
  }
53
91
 
54
- /**
55
- * Mutates error
56
- */
57
- function redact(redactPaths: string[], obj: any, error: Error): void {
58
- redactPaths
59
- .map(path => _get(obj, path) as string)
60
- .filter(Boolean)
61
- .forEach(secret => {
62
- error.message = error.message.replace(secret, REDACTED)
63
- })
64
- }
92
+ export const validateRequest = new ValidateRequest()
@@ -1,6 +1,6 @@
1
1
  import { _get, AppError, ZodSchema, ZodValidationError, zSafeValidate } from '@naturalcycles/js-lib'
2
- import { ReqValidationOptions } from './reqValidationMiddleware'
3
- import { BackendRequestHandler } from './server.model'
2
+ import { BackendRequestHandler } from '../server.model'
3
+ import { ReqValidationOptions } from './validateRequest'
4
4
 
5
5
  const REDACTED = 'REDACTED'
6
6
 
@@ -15,7 +15,8 @@ export function zodReqValidate(
15
15
  schema: ZodSchema,
16
16
  opt: ReqValidationOptions<ZodValidationError<any>> = {},
17
17
  ): BackendRequestHandler {
18
- const reportPredicate = typeof opt.report === 'function' ? opt.report : () => !!opt.report
18
+ const reportPredicate =
19
+ typeof opt.report === 'function' ? opt.report : () => opt.report as boolean | undefined
19
20
 
20
21
  return (req, res, next) => {
21
22
  const { error } = zSafeValidate(req[prop], schema)