nestjs-backend-common 0.0.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.
Files changed (32) hide show
  1. package/.github/README.md +9 -0
  2. package/.github/workflows/publish.yml +46 -0
  3. package/.prettierignore +8 -0
  4. package/.prettierrc +27 -0
  5. package/dist/src/correlation-id/correlation-id.constant.js +6 -0
  6. package/dist/src/correlation-id/correlation-id.constant.js.map +1 -0
  7. package/dist/src/correlation-id/correlation-id.interceptor.js +58 -0
  8. package/dist/src/correlation-id/correlation-id.interceptor.js.map +1 -0
  9. package/dist/src/correlation-id/correlation-id.module.js +37 -0
  10. package/dist/src/correlation-id/correlation-id.module.js.map +1 -0
  11. package/dist/src/correlation-id/correlation-id.service.js +28 -0
  12. package/dist/src/correlation-id/correlation-id.service.js.map +1 -0
  13. package/dist/src/correlation-id/correlation-id.type.js +3 -0
  14. package/dist/src/correlation-id/correlation-id.type.js.map +1 -0
  15. package/dist/src/correlation-id/index.js +21 -0
  16. package/dist/src/correlation-id/index.js.map +1 -0
  17. package/dist/src/index.js +19 -0
  18. package/dist/src/index.js.map +1 -0
  19. package/dist/src/types/index.js +3 -0
  20. package/dist/src/types/index.js.map +1 -0
  21. package/eslint.config.mjs +99 -0
  22. package/package.json +50 -0
  23. package/src/correlation-id/correlation-id.constant.ts +2 -0
  24. package/src/correlation-id/correlation-id.interceptor.ts +71 -0
  25. package/src/correlation-id/correlation-id.module.ts +26 -0
  26. package/src/correlation-id/correlation-id.service.ts +18 -0
  27. package/src/correlation-id/correlation-id.type.ts +7 -0
  28. package/src/correlation-id/index.ts +4 -0
  29. package/src/index.ts +2 -0
  30. package/src/types/index.ts +3 -0
  31. package/tsconfig.build.json +10 -0
  32. package/tsconfig.json +18 -0
@@ -0,0 +1,9 @@
1
+ # NestJS Backend Common
2
+
3
+ All the utility functions and common modules I usually use in my NestJS applications will be published and maintained here.
4
+
5
+ ## [Increase Version](https://docs.npmjs.com/cli/v8/commands/npm-version)
6
+
7
+ ```bash
8
+ npm version patch --no-git-tag-version
9
+ ```
@@ -0,0 +1,46 @@
1
+ name: Publish The Library
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ # https://stackoverflow.com/a/72183279/8784518
10
+ id-token: write
11
+
12
+ jobs:
13
+ build:
14
+ runs-on: ubuntu-24.04
15
+ steps:
16
+ - name: Checkout
17
+ uses: actions/checkout@v4
18
+
19
+ - name: Check the current published version
20
+ run: |
21
+ LAST_PUBLISHED_VERSION=$(npm view nestjs-backend-common version 2> /dev/null || echo "")
22
+ PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g')
23
+ if [[ $LAST_PUBLISHED_VERSION == $PACKAGE_VERSION ]]; then
24
+ echo "Version is published already, if you need to publish a new version please increase the version in package.json"
25
+ fi
26
+
27
+ - uses: pnpm/action-setup@v4
28
+ name: Install pnpm
29
+ with:
30
+ version: 10
31
+ run_install: false
32
+
33
+ - name: Install Node.js
34
+ uses: actions/setup-node@v4
35
+ with:
36
+ node-version: 22
37
+ cache: "pnpm"
38
+ registry-url: "https://registry.npmjs.org/"
39
+
40
+ - name: Install Dependencies
41
+ run: pnpm install --frozen-lockfile
42
+
43
+ - name: Publish Package on npm 📦
44
+ run: pnpm publish --provenance --access public --no-git-checks
45
+ env:
46
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,8 @@
1
+ # Add files here to ignore them from prettier formatting
2
+
3
+ /dist
4
+ /coverage
5
+ /.nx/cache
6
+ /.nx/workspace-data
7
+ /node_modules
8
+ pnpm-lock.yaml
package/.prettierrc ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "arrowParens": "always",
3
+ "useTabs": false,
4
+ "bracketSpacing": true,
5
+ "endOfLine": "lf",
6
+ "printWidth": 70,
7
+ "quoteProps": "consistent",
8
+ "tabWidth": 2,
9
+ "trailingComma": "all",
10
+ "semi": true,
11
+ "singleQuote": true,
12
+ "overrides": [
13
+ {
14
+ "files": ["*.yml", "*.yaml"],
15
+ "options": {
16
+ "singleQuote": false,
17
+ "tabWidth": 2
18
+ }
19
+ },
20
+ {
21
+ "files": "*.md",
22
+ "options": {
23
+ "tabWidth": 2
24
+ }
25
+ }
26
+ ]
27
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CORRELATION_ID_CLS_KEY = exports.CORRELATION_ID_HEADER_NAME = void 0;
4
+ exports.CORRELATION_ID_HEADER_NAME = 'correlation-id';
5
+ exports.CORRELATION_ID_CLS_KEY = 'CORRELATION_ID_CLS_KEY';
6
+ //# sourceMappingURL=correlation-id.constant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-id.constant.js","sourceRoot":"","sources":["../../../src/correlation-id/correlation-id.constant.ts"],"names":[],"mappings":";;;AAAa,QAAA,0BAA0B,GAAG,gBAAgB,CAAC;AAC9C,QAAA,sBAAsB,GAAG,wBAAwB,CAAC"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var CorrelationIdInterceptor_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CorrelationIdInterceptor = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const graphql_1 = require("@nestjs/graphql");
13
+ const crypto_1 = require("crypto");
14
+ const correlation_id_constant_1 = require("./correlation-id.constant");
15
+ let CorrelationIdInterceptor = CorrelationIdInterceptor_1 = class CorrelationIdInterceptor {
16
+ clsService;
17
+ logger = new common_1.Logger(CorrelationIdInterceptor_1.name);
18
+ constructor(clsService) {
19
+ this.clsService = clsService;
20
+ }
21
+ intercept(executionContext, next) {
22
+ let correlationId;
23
+ switch (executionContext.getType()) {
24
+ case 'http': {
25
+ const request = executionContext
26
+ .switchToHttp()
27
+ .getRequest();
28
+ const correlationIdValue = Array.isArray(request.headers[correlation_id_constant_1.CORRELATION_ID_HEADER_NAME])
29
+ ? request.headers[correlation_id_constant_1.CORRELATION_ID_HEADER_NAME][0]
30
+ : request.headers[correlation_id_constant_1.CORRELATION_ID_HEADER_NAME];
31
+ correlationId = correlationIdValue ?? (0, crypto_1.randomUUID)();
32
+ break;
33
+ }
34
+ case 'graphql': {
35
+ const ctx = graphql_1.GqlExecutionContext.create(executionContext);
36
+ const req = ctx.getContext().req;
37
+ correlationId = req.headers[correlation_id_constant_1.CORRELATION_ID_HEADER_NAME];
38
+ break;
39
+ }
40
+ case 'rpc': {
41
+ const { correlationId: correlationIdValue } = executionContext
42
+ .switchToRpc()
43
+ .getContext();
44
+ correlationId = correlationIdValue ?? (0, crypto_1.randomUUID)();
45
+ break;
46
+ }
47
+ default:
48
+ throw new Error('Unimplemented request type');
49
+ }
50
+ this.clsService.set(correlation_id_constant_1.CORRELATION_ID_CLS_KEY, correlationId);
51
+ return next.handle();
52
+ }
53
+ };
54
+ exports.CorrelationIdInterceptor = CorrelationIdInterceptor;
55
+ exports.CorrelationIdInterceptor = CorrelationIdInterceptor = CorrelationIdInterceptor_1 = __decorate([
56
+ (0, common_1.Injectable)()
57
+ ], CorrelationIdInterceptor);
58
+ //# sourceMappingURL=correlation-id.interceptor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-id.interceptor.js","sourceRoot":"","sources":["../../../src/correlation-id/correlation-id.interceptor.ts"],"names":[],"mappings":";;;;;;;;;;AAEA,2CAMwB;AACxB,6CAAsD;AACtD,mCAAoC;AAIpC,uEAGmC;AAG5B,IAAM,wBAAwB,gCAA9B,MAAM,wBAAwB;IAGN;IAFrB,MAAM,GAAG,IAAI,eAAM,CAAC,0BAAwB,CAAC,IAAI,CAAC,CAAC;IAE3D,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD,SAAS,CAAC,gBAAkC,EAAE,IAAiB;QAC7D,IAAI,aAAqB,CAAC;QAE1B,QAAQ,gBAAgB,CAAC,OAAO,EAA0B,EAAE,CAAC;YAC3D,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,OAAO,GAAY,gBAAgB;qBACtC,YAAY,EAAE;qBACd,UAAU,EAAE,CAAC;gBAChB,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,CACtC,OAAO,CAAC,OAAO,CAAC,oDAA0B,CAAC,CAC5C;oBACC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oDAA0B,CAAC,CAAC,CAAC,CAAC;oBAChD,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,oDAA0B,CAAC,CAAC;gBAEhD,aAAa,GAAG,kBAAkB,IAAI,IAAA,mBAAU,GAAE,CAAC;gBAEnD,MAAM;YACR,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,MAAM,GAAG,GAAG,6BAAmB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBACzD,MAAM,GAAG,GAAY,GAAG,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC;gBAE1C,aAAa,GAAG,GAAG,CAAC,OAAO,CACzB,oDAA0B,CACjB,CAAC;gBAEZ,MAAM;YACR,CAAC;YACD,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,gBAAgB;qBAC3D,WAAW,EAAE;qBACb,UAAU,EAA8B,CAAC;gBAE5C,aAAa,GAAG,kBAAkB,IAAI,IAAA,mBAAU,GAAE,CAAC;gBAEnD,MAAM;YACR,CAAC;YACD;gBACE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAClD,CAAC;QAED,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,gDAAsB,EAAE,aAAa,CAAC,CAAC;QAE3D,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;IACvB,CAAC;CACF,CAAA;AAlDY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;GACA,wBAAwB,CAkDpC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var CorrelationIdModule_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CorrelationIdModule = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const core_1 = require("@nestjs/core");
13
+ const nestjs_cls_1 = require("nestjs-cls");
14
+ const correlation_id_interceptor_1 = require("./correlation-id.interceptor");
15
+ const correlation_id_service_1 = require("./correlation-id.service");
16
+ let CorrelationIdModule = CorrelationIdModule_1 = class CorrelationIdModule {
17
+ forRoot(options) {
18
+ return {
19
+ global: options?.isGlobal ?? false,
20
+ module: CorrelationIdModule_1,
21
+ imports: [nestjs_cls_1.ClsModule],
22
+ providers: [
23
+ {
24
+ provide: core_1.APP_INTERCEPTOR,
25
+ useClass: correlation_id_interceptor_1.CorrelationIdInterceptor,
26
+ },
27
+ correlation_id_service_1.CorrelationIdService,
28
+ ],
29
+ exports: [correlation_id_service_1.CorrelationIdService],
30
+ };
31
+ }
32
+ };
33
+ exports.CorrelationIdModule = CorrelationIdModule;
34
+ exports.CorrelationIdModule = CorrelationIdModule = CorrelationIdModule_1 = __decorate([
35
+ (0, common_1.Module)({})
36
+ ], CorrelationIdModule);
37
+ //# sourceMappingURL=correlation-id.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-id.module.js","sourceRoot":"","sources":["../../../src/correlation-id/correlation-id.module.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAuD;AACvD,uCAA+C;AAC/C,2CAAuC;AAEvC,6EAAwE;AACxE,qEAAgE;AAIzD,IAAM,mBAAmB,2BAAzB,MAAM,mBAAmB;IAC9B,OAAO,CAAC,OAAoC;QAC1C,OAAO;YACL,MAAM,EAAE,OAAO,EAAE,QAAQ,IAAI,KAAK;YAClC,MAAM,EAAE,qBAAmB;YAC3B,OAAO,EAAE,CAAC,sBAAS,CAAC;YACpB,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,sBAAe;oBACxB,QAAQ,EAAE,qDAAwB;iBACnC;gBACD,6CAAoB;aACrB;YACD,OAAO,EAAE,CAAC,6CAAoB,CAAC;SAChC,CAAC;IACJ,CAAC;CACF,CAAA;AAhBY,kDAAmB;8BAAnB,mBAAmB;IAD/B,IAAA,eAAM,EAAC,EAAE,CAAC;GACE,mBAAmB,CAgB/B"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var CorrelationIdService_1;
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.CorrelationIdService = void 0;
11
+ const common_1 = require("@nestjs/common");
12
+ const crypto_1 = require("crypto");
13
+ const correlation_id_constant_1 = require("./correlation-id.constant");
14
+ let CorrelationIdService = CorrelationIdService_1 = class CorrelationIdService {
15
+ clsService;
16
+ logger = new common_1.Logger(CorrelationIdService_1.name);
17
+ constructor(clsService) {
18
+ this.clsService = clsService;
19
+ }
20
+ get correlationId() {
21
+ return (this.clsService.get(correlation_id_constant_1.CORRELATION_ID_CLS_KEY) ?? (0, crypto_1.randomUUID)());
22
+ }
23
+ };
24
+ exports.CorrelationIdService = CorrelationIdService;
25
+ exports.CorrelationIdService = CorrelationIdService = CorrelationIdService_1 = __decorate([
26
+ (0, common_1.Injectable)()
27
+ ], CorrelationIdService);
28
+ //# sourceMappingURL=correlation-id.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-id.service.js","sourceRoot":"","sources":["../../../src/correlation-id/correlation-id.service.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAoD;AACpD,mCAAoC;AAGpC,uEAAmE;AAG5D,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAGF;IAFZ,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEhE,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAEvD,IAAI,aAAa;QACf,OAAO,CACL,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,gDAAsB,CAAC,IAAI,IAAA,mBAAU,GAAE,CAC5D,CAAC;IACJ,CAAC;CACF,CAAA;AAVY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;GACA,oBAAoB,CAUhC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=correlation-id.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"correlation-id.type.js","sourceRoot":"","sources":["../../../src/correlation-id/correlation-id.type.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./correlation-id.constant"), exports);
18
+ __exportStar(require("./correlation-id.module"), exports);
19
+ __exportStar(require("./correlation-id.service"), exports);
20
+ __exportStar(require("./correlation-id.type"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/correlation-id/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4DAA0C;AAC1C,0DAAwC;AACxC,2DAAyC;AACzC,wDAAsC"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./correlation-id"), exports);
18
+ __exportStar(require("./types"), exports);
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,0CAAwB"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,99 @@
1
+ // @ts-check
2
+
3
+ import eslint from '@eslint/js';
4
+ import perfectionist from 'eslint-plugin-perfectionist';
5
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
6
+ import globals from 'globals';
7
+ import { dirname } from 'path';
8
+ import tseslint from 'typescript-eslint';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+
14
+ export default tseslint.config(
15
+ {
16
+ ignores: ['eslint.config.mjs', '**/dist'],
17
+ },
18
+ eslint.configs.recommended,
19
+ ...tseslint.configs.recommendedTypeChecked,
20
+ eslintPluginPrettierRecommended,
21
+ {
22
+ languageOptions: {
23
+ globals: {
24
+ ...globals.node,
25
+ ...globals.jest,
26
+ },
27
+ ecmaVersion: 5,
28
+ sourceType: 'module',
29
+ parserOptions: {
30
+ projectService: true,
31
+ tsconfigRootDir: __dirname,
32
+ },
33
+ },
34
+ },
35
+ {
36
+ rules: {
37
+ '@typescript-eslint/no-explicit-any': 'error',
38
+ '@typescript-eslint/no-floating-promises': 'warn',
39
+ '@typescript-eslint/no-unsafe-argument': 'warn',
40
+ '@typescript-eslint/no-unsafe-assignment': 'off',
41
+ '@typescript-eslint/no-unsafe-member-access': 'off',
42
+ '@typescript-eslint/no-unsafe-call': 'off',
43
+ },
44
+ },
45
+ {
46
+ files: ['**/*.config.ts', '**/*.spec.ts', '**/*.e2e-spec.ts'],
47
+ rules: {
48
+ '@typescript-eslint/naming-convention': [
49
+ 'error',
50
+ {
51
+ selector: 'typeParameter',
52
+ format: ['PascalCase'],
53
+
54
+ custom: {
55
+ regex: '^T[A-Z][a-zA-Z]+$',
56
+ match: true,
57
+ },
58
+ },
59
+ ],
60
+ '@typescript-eslint/no-unused-vars': [
61
+ 'error',
62
+ {
63
+ argsIgnorePattern: '^_',
64
+ varsIgnorePattern: '^_',
65
+ },
66
+ ],
67
+ '@typescript-eslint/no-namespace': 'off',
68
+ '@typescript-eslint/no-empty-interface': 'off',
69
+ '@typescript-eslint/no-explicit-any': 'off',
70
+ '@typescript-eslint/no-unsafe-argument': 'off',
71
+ '@typescript-eslint/no-empty-object-type': 'off',
72
+ },
73
+ },
74
+ {
75
+ plugins: {
76
+ perfectionist,
77
+ },
78
+ rules: {
79
+ '@typescript-eslint/no-unused-vars': [
80
+ 'warn',
81
+ {
82
+ argsIgnorePattern: '^_[^_].*$|^_$',
83
+ varsIgnorePattern: '^_[^_].*$|^_$',
84
+ },
85
+ ],
86
+ 'perfectionist/sort-named-imports': 'error',
87
+ 'perfectionist/sort-exports': 'error',
88
+ 'perfectionist/sort-imports': 'error',
89
+ 'perfectionist/sort-named-exports': 'error',
90
+ 'perfectionist/sort-enums': 'error',
91
+ },
92
+ },
93
+ {
94
+ ignores: ['*decorator.ts', '*.spec.ts'],
95
+ rules: {
96
+ '@typescript-eslint/no-explicit-any': 'off',
97
+ },
98
+ },
99
+ );
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "nestjs-backend-common",
3
+ "version": "0.0.0",
4
+ "main": "dist/index",
5
+ "types": "dist/index",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+ssh://git@github.com/kasir-barati/nestjs-backend-common.git"
9
+ },
10
+ "keywords": [
11
+ "npm",
12
+ "common",
13
+ "nestjs",
14
+ "nodejs",
15
+ "javascript",
16
+ "typescript"
17
+ ],
18
+ "author": "kasir-barati",
19
+ "license": "MIT",
20
+ "bugs": {
21
+ "url": "https://github.com/kasir-barati/nestjs-backend-common/issues"
22
+ },
23
+ "homepage": "https://github.com/kasir-barati/nestjs-backend-common#readme",
24
+ "description": "All the utility functions and common modules I usually use in my NestJS applications will be published and maintained here.",
25
+ "dependencies": {
26
+ "@nestjs/common": "^11.0.20",
27
+ "@nestjs/core": "^11.0.20",
28
+ "@nestjs/graphql": "^13.1.0",
29
+ "nestjs-cls": "^5.4.3"
30
+ },
31
+ "devDependencies": {
32
+ "@eslint/js": "^9.25.0",
33
+ "@types/express": "^5.0.1",
34
+ "@types/node": "^22.14.1",
35
+ "eslint-config-prettier": "^10.1.2",
36
+ "eslint-plugin-perfectionist": "^4.11.0",
37
+ "eslint-plugin-prettier": "^5.2.6",
38
+ "globals": "^16.0.0",
39
+ "prettier": "^3.5.3",
40
+ "typescript": "^5.8.3",
41
+ "typescript-eslint": "^8.30.1"
42
+ },
43
+ "scripts": {
44
+ "test": "jest",
45
+ "build": "rm -rf dist && tsc",
46
+ "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
47
+ "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
48
+ "prepublish": "npm run build"
49
+ }
50
+ }
@@ -0,0 +1,2 @@
1
+ export const CORRELATION_ID_HEADER_NAME = 'correlation-id';
2
+ export const CORRELATION_ID_CLS_KEY = 'CORRELATION_ID_CLS_KEY';
@@ -0,0 +1,71 @@
1
+ import type { Request } from 'express';
2
+
3
+ import {
4
+ CallHandler,
5
+ ExecutionContext,
6
+ Injectable,
7
+ Logger,
8
+ NestInterceptor,
9
+ } from '@nestjs/common';
10
+ import { GqlExecutionContext } from '@nestjs/graphql';
11
+ import { randomUUID } from 'crypto';
12
+ import { ClsService } from 'nestjs-cls';
13
+
14
+ import { CommonExecutionContext } from '../types';
15
+ import {
16
+ CORRELATION_ID_CLS_KEY,
17
+ CORRELATION_ID_HEADER_NAME,
18
+ } from './correlation-id.constant';
19
+
20
+ @Injectable()
21
+ export class CorrelationIdInterceptor implements NestInterceptor {
22
+ private logger = new Logger(CorrelationIdInterceptor.name);
23
+
24
+ constructor(private readonly clsService: ClsService) {}
25
+
26
+ intercept(executionContext: ExecutionContext, next: CallHandler) {
27
+ let correlationId: string;
28
+
29
+ switch (executionContext.getType<CommonExecutionContext>()) {
30
+ case 'http': {
31
+ const request: Request = executionContext
32
+ .switchToHttp()
33
+ .getRequest();
34
+ const correlationIdValue = Array.isArray(
35
+ request.headers[CORRELATION_ID_HEADER_NAME],
36
+ )
37
+ ? request.headers[CORRELATION_ID_HEADER_NAME][0]
38
+ : request.headers[CORRELATION_ID_HEADER_NAME];
39
+
40
+ correlationId = correlationIdValue ?? randomUUID();
41
+
42
+ break;
43
+ }
44
+ case 'graphql': {
45
+ const ctx = GqlExecutionContext.create(executionContext);
46
+ const req: Request = ctx.getContext().req;
47
+
48
+ correlationId = req.headers[
49
+ CORRELATION_ID_HEADER_NAME
50
+ ] as string;
51
+
52
+ break;
53
+ }
54
+ case 'rpc': {
55
+ const { correlationId: correlationIdValue } = executionContext
56
+ .switchToRpc()
57
+ .getContext<{ correlationId?: string }>();
58
+
59
+ correlationId = correlationIdValue ?? randomUUID();
60
+
61
+ break;
62
+ }
63
+ default:
64
+ throw new Error('Unimplemented request type');
65
+ }
66
+
67
+ this.clsService.set(CORRELATION_ID_CLS_KEY, correlationId);
68
+
69
+ return next.handle();
70
+ }
71
+ }
@@ -0,0 +1,26 @@
1
+ import { DynamicModule, Module } from '@nestjs/common';
2
+ import { APP_INTERCEPTOR } from '@nestjs/core';
3
+ import { ClsModule } from 'nestjs-cls';
4
+
5
+ import { CorrelationIdInterceptor } from './correlation-id.interceptor';
6
+ import { CorrelationIdService } from './correlation-id.service';
7
+ import { CorrelationIdModuleOptions } from './correlation-id.type';
8
+
9
+ @Module({})
10
+ export class CorrelationIdModule {
11
+ forRoot(options?: CorrelationIdModuleOptions): DynamicModule {
12
+ return {
13
+ global: options?.isGlobal ?? false,
14
+ module: CorrelationIdModule,
15
+ imports: [ClsModule],
16
+ providers: [
17
+ {
18
+ provide: APP_INTERCEPTOR,
19
+ useClass: CorrelationIdInterceptor,
20
+ },
21
+ CorrelationIdService,
22
+ ],
23
+ exports: [CorrelationIdService],
24
+ };
25
+ }
26
+ }
@@ -0,0 +1,18 @@
1
+ import { Injectable, Logger } from '@nestjs/common';
2
+ import { randomUUID } from 'crypto';
3
+ import { ClsService } from 'nestjs-cls';
4
+
5
+ import { CORRELATION_ID_CLS_KEY } from './correlation-id.constant';
6
+
7
+ @Injectable()
8
+ export class CorrelationIdService {
9
+ private readonly logger = new Logger(CorrelationIdService.name);
10
+
11
+ constructor(private readonly clsService: ClsService) {}
12
+
13
+ get correlationId(): string {
14
+ return (
15
+ this.clsService.get(CORRELATION_ID_CLS_KEY) ?? randomUUID()
16
+ );
17
+ }
18
+ }
@@ -0,0 +1,7 @@
1
+ export interface CorrelationIdModuleOptions {
2
+ /**
3
+ * @default false
4
+ * @description Register the module globally
5
+ */
6
+ isGlobal?: boolean;
7
+ }
@@ -0,0 +1,4 @@
1
+ export * from './correlation-id.constant';
2
+ export * from './correlation-id.module';
3
+ export * from './correlation-id.service';
4
+ export * from './correlation-id.type';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './correlation-id';
2
+ export * from './types';
@@ -0,0 +1,3 @@
1
+ import { ContextType } from '@nestjs/common';
2
+
3
+ export type CommonExecutionContext = 'graphql' | ContextType;
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "exclude": [
4
+ "node_modules",
5
+ "test",
6
+ "dist",
7
+ "**/*spec.ts",
8
+ "eslint.config.mjs"
9
+ ]
10
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2024",
4
+ "experimentalDecorators": true,
5
+ "module": "NodeNext",
6
+ "rootDir": "./",
7
+ "outDir": "./dist",
8
+ "moduleResolution": "nodenext",
9
+ "sourceMap": true,
10
+ "removeComments": false,
11
+ "newLine": "crlf",
12
+ "esModuleInterop": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "strict": true,
15
+ "strictPropertyInitialization": false,
16
+ "skipLibCheck": true
17
+ }
18
+ }