@naturalcycles/backend-lib 3.3.0 → 4.1.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.
@@ -7,7 +7,7 @@ const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
7
7
  const time_lib_1 = require("@naturalcycles/time-lib");
8
8
  const yaml = require("js-yaml");
9
9
  const APP_YAML_DEFAULT = () => ({
10
- runtime: 'nodejs14',
10
+ runtime: 'nodejs16',
11
11
  service: 'default',
12
12
  inbound_services: ['warmup'],
13
13
  instance_class: 'F1',
@@ -47,7 +47,8 @@ const DEFAULT_FILES = [
47
47
  'tsconfig.dist.json',
48
48
  '.gcloudignore',
49
49
  'app.yaml',
50
- 'patches', // to allow patch-package
50
+ 'patches',
51
+ 'resources',
51
52
  ];
52
53
  const defaultFilesDir = `${paths_cnst_1.srcDir}/deploy/files-default`;
53
54
  async function deployPrepare(opt = {}) {
@@ -38,8 +38,22 @@ function createDefaultApp(cfg) {
38
38
  app.use((0, simpleRequestLoggerMiddleware_1.simpleRequestLoggerMiddleware)());
39
39
  }
40
40
  // app.use(safeJsonMiddleware()) // optional
41
- app.use(express.json({ limit: '1mb' }));
42
- app.use(express.urlencoded({ limit: '1mb', extended: true }));
41
+ // accepts application/json
42
+ app.use(express.json({
43
+ limit: '1mb',
44
+ ...cfg.bodyParserJsonOptions,
45
+ }));
46
+ app.use(express.urlencoded({
47
+ limit: '1mb',
48
+ extended: true,
49
+ ...cfg.bodyParserUrlEncodedOptions,
50
+ }));
51
+ // accepts application/octet-stream
52
+ app.use(express.raw({
53
+ // inflate: true, // default is `true`
54
+ limit: '1mb',
55
+ ...cfg.bodyParserRawOptions,
56
+ }));
43
57
  app.use(cookieParser());
44
58
  if (!isTest) {
45
59
  // leaks, load lazily
@@ -52,6 +66,7 @@ function createDefaultApp(cfg) {
52
66
  credentials: true,
53
67
  // methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', // default
54
68
  maxAge: 86400,
69
+ ...cfg.corsOptions,
55
70
  }));
56
71
  // app.use(clearBodyParserTimeout()) // removed by default
57
72
  // Static is now disabled by default due to performance
@@ -1,3 +1,5 @@
1
+ import { Options, OptionsJson, OptionsUrlencoded } from 'body-parser';
2
+ import { CorsOptions } from 'cors';
1
3
  import { SentrySharedService } from '../sentry/sentry.shared.service';
2
4
  import { BackendRequestHandler } from './server.model';
3
5
  /**
@@ -23,4 +25,8 @@ export interface DefaultAppCfg {
23
25
  resources?: BackendRequestHandlerCfg[];
24
26
  postHandlers?: BackendRequestHandlerCfg[];
25
27
  sentryService?: SentrySharedService;
28
+ bodyParserJsonOptions?: OptionsJson;
29
+ bodyParserUrlEncodedOptions?: OptionsUrlencoded;
30
+ bodyParserRawOptions?: Options;
31
+ corsOptions?: CorsOptions;
26
32
  }
@@ -2,6 +2,11 @@ import { SentrySharedService } from '../sentry/sentry.shared.service';
2
2
  import { BackendErrorRequestHandler, BackendRequest, BackendResponse } from './server.model';
3
3
  export interface GenericErrorMiddlewareCfg {
4
4
  sentryService?: SentrySharedService;
5
+ /**
6
+ * Defaults to false.
7
+ * So, by default, it will report ALL errors, not only 5xx.
8
+ */
9
+ reportOnly5xx?: boolean;
5
10
  }
6
11
  /**
7
12
  * Generic error handler.
@@ -7,6 +7,7 @@ const { APP_ENV } = process.env;
7
7
  const includeErrorStack = APP_ENV !== 'prod' && APP_ENV !== 'test';
8
8
  // Hacky way to store the sentryService, so it's available to `respondWithError` function
9
9
  let sentryService;
10
+ let reportOnly5xx = false;
10
11
  /**
11
12
  * Generic error handler.
12
13
  * Returns HTTP code based on err.data.httpStatusCode (default to 500).
@@ -14,6 +15,7 @@ let sentryService;
14
15
  */
15
16
  function genericErrorMiddleware(cfg = {}) {
16
17
  sentryService || (sentryService = cfg.sentryService);
18
+ reportOnly5xx = cfg.reportOnly5xx || false;
17
19
  return (err, req, res, _next) => {
18
20
  // if (res.headersSent) {
19
21
  // Here we don't even log this error
@@ -30,9 +32,6 @@ function genericErrorMiddleware(cfg = {}) {
30
32
  };
31
33
  }
32
34
  exports.genericErrorMiddleware = genericErrorMiddleware;
33
- // export interface ResponseWithError extends Response {
34
- // __err?: any
35
- // }
36
35
  function respondWithError(req, res, err) {
37
36
  var _a, _b;
38
37
  const { headersSent } = res;
@@ -73,5 +72,8 @@ function shouldReportToSentry(err) {
73
72
  if (e.data.report === false)
74
73
  return false;
75
74
  // Report if http 5xx, otherwise not
76
- return !e.data.httpStatusCode || e.data.httpStatusCode >= 500;
75
+ // If no httpCode - report
76
+ // if httpCode >= 500 - report
77
+ // Otherwise - report, unless !reportOnly5xx is set
78
+ return !reportOnly5xx || !e.data.httpStatusCode || e.data.httpStatusCode >= 500;
77
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
- "version": "3.3.0",
3
+ "version": "4.1.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install && patch-package",
6
6
  "serve": "APP_ENV=dev nodemon",
@@ -44,7 +44,7 @@
44
44
  "@sentry/node": "^6.5.1",
45
45
  "@types/ejs": "^3.0.0",
46
46
  "@types/js-yaml": "^4.0.0",
47
- "@types/node": "^16.4.1",
47
+ "@types/node": "^17.0.0",
48
48
  "esbuild-register": "^3.1.2",
49
49
  "fastify": "^3.20.1",
50
50
  "jest": "^27.0.1",
@@ -84,7 +84,7 @@
84
84
  "url": "https://github.com/NaturalCycles/backend-lib"
85
85
  },
86
86
  "engines": {
87
- "node": ">=14.15.0"
87
+ "node": ">=16.13.0"
88
88
  },
89
89
  "type": "commonjs",
90
90
  "description": "Standard library for making Express.js / AppEngine based backend services",
@@ -8,7 +8,7 @@ import { BackendCfg } from './backend.cfg.util'
8
8
  import { AppYaml, DeployInfo } from './deploy.model'
9
9
 
10
10
  const APP_YAML_DEFAULT = (): AppYaml => ({
11
- runtime: 'nodejs14',
11
+ runtime: 'nodejs16',
12
12
  service: 'default',
13
13
  inbound_services: ['warmup'],
14
14
  instance_class: 'F1',
@@ -60,6 +60,7 @@ const DEFAULT_FILES = [
60
60
  '.gcloudignore',
61
61
  'app.yaml',
62
62
  'patches', // to allow patch-package
63
+ 'resources',
63
64
  ]
64
65
 
65
66
  const defaultFilesDir = `${srcDir}/deploy/files-default`
@@ -1,3 +1,5 @@
1
+ import { Options, OptionsJson, OptionsUrlencoded } from 'body-parser'
2
+ import { CorsOptions } from 'cors'
1
3
  import { SentrySharedService } from '../sentry/sentry.shared.service'
2
4
  import { BackendRequestHandler } from './server.model'
3
5
 
@@ -25,5 +27,12 @@ export interface DefaultAppCfg {
25
27
  handlers?: BackendRequestHandlerCfg[]
26
28
  resources?: BackendRequestHandlerCfg[]
27
29
  postHandlers?: BackendRequestHandlerCfg[]
30
+
28
31
  sentryService?: SentrySharedService
32
+
33
+ bodyParserJsonOptions?: OptionsJson
34
+ bodyParserUrlEncodedOptions?: OptionsUrlencoded
35
+ bodyParserRawOptions?: Options
36
+
37
+ corsOptions?: CorsOptions
29
38
  }
@@ -51,9 +51,34 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
51
51
  }
52
52
 
53
53
  // app.use(safeJsonMiddleware()) // optional
54
- app.use(express.json({ limit: '1mb' }))
55
- app.use(express.urlencoded({ limit: '1mb', extended: true }))
54
+
55
+ // accepts application/json
56
+ app.use(
57
+ express.json({
58
+ limit: '1mb',
59
+ ...cfg.bodyParserJsonOptions,
60
+ }),
61
+ )
62
+
63
+ app.use(
64
+ express.urlencoded({
65
+ limit: '1mb',
66
+ extended: true,
67
+ ...cfg.bodyParserUrlEncodedOptions,
68
+ }),
69
+ )
70
+
71
+ // accepts application/octet-stream
72
+ app.use(
73
+ express.raw({
74
+ // inflate: true, // default is `true`
75
+ limit: '1mb',
76
+ ...cfg.bodyParserRawOptions,
77
+ }),
78
+ )
79
+
56
80
  app.use(cookieParser())
81
+
57
82
  if (!isTest) {
58
83
  // leaks, load lazily
59
84
  app.use(
@@ -69,6 +94,7 @@ export function createDefaultApp(cfg: DefaultAppCfg): BackendApplication {
69
94
  credentials: true,
70
95
  // methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', // default
71
96
  maxAge: 86400,
97
+ ...cfg.corsOptions,
72
98
  }),
73
99
  )
74
100
 
@@ -12,6 +12,12 @@ import { BackendErrorRequestHandler, BackendRequest, BackendResponse } from './s
12
12
 
13
13
  export interface GenericErrorMiddlewareCfg {
14
14
  sentryService?: SentrySharedService
15
+
16
+ /**
17
+ * Defaults to false.
18
+ * So, by default, it will report ALL errors, not only 5xx.
19
+ */
20
+ reportOnly5xx?: boolean
15
21
  }
16
22
 
17
23
  const { APP_ENV } = process.env
@@ -19,6 +25,7 @@ const includeErrorStack = APP_ENV !== 'prod' && APP_ENV !== 'test'
19
25
 
20
26
  // Hacky way to store the sentryService, so it's available to `respondWithError` function
21
27
  let sentryService: SentrySharedService | undefined
28
+ let reportOnly5xx = false
22
29
 
23
30
  /**
24
31
  * Generic error handler.
@@ -29,6 +36,7 @@ export function genericErrorMiddleware(
29
36
  cfg: GenericErrorMiddlewareCfg = {},
30
37
  ): BackendErrorRequestHandler {
31
38
  sentryService ||= cfg.sentryService
39
+ reportOnly5xx = cfg.reportOnly5xx || false
32
40
 
33
41
  return (err, req, res, _next) => {
34
42
  // if (res.headersSent) {
@@ -47,9 +55,6 @@ export function genericErrorMiddleware(
47
55
  }
48
56
  }
49
57
 
50
- // export interface ResponseWithError extends Response {
51
- // __err?: any
52
- // }
53
58
  export function respondWithError(req: BackendRequest, res: BackendResponse, err: any): void {
54
59
  const { headersSent } = res
55
60
 
@@ -95,5 +100,8 @@ function shouldReportToSentry(err: Error): boolean {
95
100
  if (e.data.report === false) return false
96
101
 
97
102
  // Report if http 5xx, otherwise not
98
- return !e.data.httpStatusCode || e.data.httpStatusCode >= 500
103
+ // If no httpCode - report
104
+ // if httpCode >= 500 - report
105
+ // Otherwise - report, unless !reportOnly5xx is set
106
+ return !reportOnly5xx || !e.data.httpStatusCode || e.data.httpStatusCode >= 500
99
107
  }