@lenne.tech/nest-server 11.7.3 → 11.9.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 (96) hide show
  1. package/dist/config.env.js +3 -0
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/interfaces/server-options.interface.d.ts +35 -0
  4. package/dist/core/modules/auth/guards/roles.guard.js +4 -3
  5. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  6. package/dist/core/modules/auth/services/core-auth.service.js +5 -4
  7. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  8. package/dist/core/modules/error-code/core-error-code.controller.d.ts +7 -0
  9. package/dist/core/modules/error-code/core-error-code.controller.js +45 -0
  10. package/dist/core/modules/error-code/core-error-code.controller.js.map +1 -0
  11. package/dist/core/modules/error-code/core-error-code.service.d.ts +16 -0
  12. package/dist/core/modules/error-code/core-error-code.service.js +65 -0
  13. package/dist/core/modules/error-code/core-error-code.service.js.map +1 -0
  14. package/dist/core/modules/error-code/error-code.module.d.ts +7 -0
  15. package/dist/core/modules/error-code/error-code.module.js +64 -0
  16. package/dist/core/modules/error-code/error-code.module.js.map +1 -0
  17. package/dist/core/modules/error-code/error-codes.d.ts +219 -0
  18. package/dist/core/modules/error-code/error-codes.js +204 -0
  19. package/dist/core/modules/error-code/error-codes.js.map +1 -0
  20. package/dist/core/modules/error-code/index.d.ts +5 -0
  21. package/dist/core/modules/error-code/index.js +22 -0
  22. package/dist/core/modules/error-code/index.js.map +1 -0
  23. package/dist/core/modules/error-code/interfaces/error-code.interfaces.d.ts +12 -0
  24. package/dist/core/modules/error-code/interfaces/error-code.interfaces.js +3 -0
  25. package/dist/core/modules/error-code/interfaces/error-code.interfaces.js.map +1 -0
  26. package/dist/core/modules/file/core-file.controller.d.ts +1 -0
  27. package/dist/core/modules/file/core-file.controller.js +22 -0
  28. package/dist/core/modules/file/core-file.controller.js.map +1 -1
  29. package/dist/core/modules/tus/core-tus.controller.d.ts +9 -0
  30. package/dist/core/modules/tus/core-tus.controller.js +85 -0
  31. package/dist/core/modules/tus/core-tus.controller.js.map +1 -0
  32. package/dist/core/modules/tus/core-tus.service.d.ts +30 -0
  33. package/dist/core/modules/tus/core-tus.service.js +284 -0
  34. package/dist/core/modules/tus/core-tus.service.js.map +1 -0
  35. package/dist/core/modules/tus/index.d.ts +4 -0
  36. package/dist/core/modules/tus/index.js +21 -0
  37. package/dist/core/modules/tus/index.js.map +1 -0
  38. package/dist/core/modules/tus/interfaces/tus-config.interface.d.ts +10 -0
  39. package/dist/core/modules/tus/interfaces/tus-config.interface.js +59 -0
  40. package/dist/core/modules/tus/interfaces/tus-config.interface.js.map +1 -0
  41. package/dist/core/modules/tus/tus.module.d.ts +21 -0
  42. package/dist/core/modules/tus/tus.module.js +99 -0
  43. package/dist/core/modules/tus/tus.module.js.map +1 -0
  44. package/dist/core.module.js +8 -0
  45. package/dist/core.module.js.map +1 -1
  46. package/dist/index.d.ts +2 -0
  47. package/dist/index.js +2 -0
  48. package/dist/index.js.map +1 -1
  49. package/dist/server/modules/error-code/error-code.controller.d.ts +8 -0
  50. package/dist/server/modules/error-code/error-code.controller.js +55 -0
  51. package/dist/server/modules/error-code/error-code.controller.js.map +1 -0
  52. package/dist/server/modules/error-code/error-code.service.d.ts +4 -0
  53. package/dist/server/modules/error-code/error-code.service.js +27 -0
  54. package/dist/server/modules/error-code/error-code.service.js.map +1 -0
  55. package/dist/server/modules/error-code/error-codes.d.ts +45 -0
  56. package/dist/server/modules/error-code/error-codes.js +24 -0
  57. package/dist/server/modules/error-code/error-codes.js.map +1 -0
  58. package/dist/server/modules/error-code/index.d.ts +3 -0
  59. package/dist/server/modules/error-code/index.js +20 -0
  60. package/dist/server/modules/error-code/index.js.map +1 -0
  61. package/dist/server/modules/file/file.controller.d.ts +5 -7
  62. package/dist/server/modules/file/file.controller.js +3 -31
  63. package/dist/server/modules/file/file.controller.js.map +1 -1
  64. package/dist/server/server.module.js +10 -1
  65. package/dist/server/server.module.js.map +1 -1
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/package.json +5 -2
  68. package/src/config.env.ts +4 -0
  69. package/src/core/common/interfaces/server-options.interface.ts +243 -0
  70. package/src/core/modules/auth/guards/roles.guard.ts +5 -4
  71. package/src/core/modules/auth/services/core-auth.service.ts +5 -4
  72. package/src/core/modules/error-code/INTEGRATION-CHECKLIST.md +288 -0
  73. package/src/core/modules/error-code/core-error-code.controller.ts +54 -0
  74. package/src/core/modules/error-code/core-error-code.service.ts +135 -0
  75. package/src/core/modules/error-code/error-code.module.ts +119 -0
  76. package/src/core/modules/error-code/error-codes.ts +405 -0
  77. package/src/core/modules/error-code/index.ts +14 -0
  78. package/src/core/modules/error-code/interfaces/error-code.interfaces.ts +99 -0
  79. package/src/core/modules/file/README.md +165 -0
  80. package/src/core/modules/file/core-file.controller.ts +27 -1
  81. package/src/core/modules/tus/INTEGRATION-CHECKLIST.md +176 -0
  82. package/src/core/modules/tus/README.md +439 -0
  83. package/src/core/modules/tus/core-tus.controller.ts +88 -0
  84. package/src/core/modules/tus/core-tus.service.ts +424 -0
  85. package/src/core/modules/tus/index.ts +5 -0
  86. package/src/core/modules/tus/interfaces/tus-config.interface.ts +107 -0
  87. package/src/core/modules/tus/tus.module.ts +187 -0
  88. package/src/core.module.ts +16 -0
  89. package/src/index.ts +12 -0
  90. package/src/server/modules/error-code/README.md +131 -0
  91. package/src/server/modules/error-code/error-code.controller.ts +91 -0
  92. package/src/server/modules/error-code/error-code.service.ts +42 -0
  93. package/src/server/modules/error-code/error-codes.ts +65 -0
  94. package/src/server/modules/error-code/index.ts +8 -0
  95. package/src/server/modules/file/file.controller.ts +14 -34
  96. package/src/server/server.module.ts +15 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.7.3",
3
+ "version": "11.9.0",
4
4
  "description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
5
5
  "keywords": [
6
6
  "node",
@@ -21,7 +21,7 @@
21
21
  "build:pack": "npm pack && echo 'use file:/ROOT_PATH_TO_TGZ_FILE to integrate the package'",
22
22
  "build:dev": "npm run build && yalc push --private",
23
23
  "docs": "npm run docs:ci && open http://127.0.0.1:8080/ && open ./public/index.html && compodoc -p tsconfig.json -s ",
24
- "docs:bootstrap": "node extras/update-spectaql-version.mjs && npx -y spectaql ./spectaql.yml",
24
+ "docs:bootstrap": "node extras/update-spectaql-version.mjs && node scripts/run-spectaql.mjs",
25
25
  "docs:ci": "ts-node ./scripts/init-server.ts && npm run docs:bootstrap && compodoc -p tsconfig.json",
26
26
  "format": "prettier --write 'src/**/*.ts'",
27
27
  "format:staged": "pretty-quick --staged",
@@ -93,6 +93,8 @@
93
93
  "@nestjs/swagger": "11.2.3",
94
94
  "@nestjs/terminus": "11.0.0",
95
95
  "@nestjs/websockets": "11.1.9",
96
+ "@tus/file-store": "2.0.0",
97
+ "@tus/server": "2.3.0",
96
98
  "apollo-server-core": "3.13.0",
97
99
  "bcrypt": "6.0.0",
98
100
  "better-auth": "1.4.8-beta.4",
@@ -167,6 +169,7 @@
167
169
  "ts-morph": "27.0.2",
168
170
  "ts-node": "10.9.2",
169
171
  "tsconfig-paths": "4.2.0",
172
+ "tus-js-client": "4.3.1",
170
173
  "typescript": "5.9.3",
171
174
  "unplugin-swc": "1.5.9",
172
175
  "vite": "7.3.0",
package/src/config.env.ts CHANGED
@@ -262,6 +262,10 @@ const config: { [env: string]: IServerOptions } = {
262
262
  verificationLink: 'http://localhost:4200/user/verification',
263
263
  },
264
264
  env: 'local',
265
+ // Disable auto-registration to allow Server ErrorCodeModule with SRV_* codes
266
+ errorCode: {
267
+ autoRegister: false,
268
+ },
265
269
  execAfterInit: 'npm run docs:bootstrap',
266
270
  filter: {
267
271
  maxLimit: null,
@@ -589,6 +589,70 @@ export interface IBetterAuthUserField {
589
589
  type: BetterAuthFieldType;
590
590
  }
591
591
 
592
+ /**
593
+ * Interface for Error Code module configuration
594
+ *
595
+ * Controls how the ErrorCodeModule is registered and configured.
596
+ *
597
+ * @since 11.9.0
598
+ */
599
+ export interface IErrorCode {
600
+ /**
601
+ * Additional error registry to merge with core LTNS_* errors
602
+ *
603
+ * Use this to add project-specific error codes with a custom prefix.
604
+ *
605
+ * @example
606
+ * ```typescript
607
+ * const ProjectErrors = {
608
+ * ORDER_NOT_FOUND: {
609
+ * code: 'PROJ_0001',
610
+ * message: 'Order not found',
611
+ * translations: { de: 'Bestellung nicht gefunden.', en: 'Order not found.' }
612
+ * }
613
+ * } as const satisfies IErrorRegistry;
614
+ *
615
+ * errorCode: {
616
+ * additionalErrorRegistry: ProjectErrors,
617
+ * }
618
+ * ```
619
+ */
620
+ additionalErrorRegistry?: Record<
621
+ string,
622
+ {
623
+ code: string;
624
+ message: string;
625
+ translations: { [locale: string]: string; de: string; en: string };
626
+ }
627
+ >;
628
+
629
+ /**
630
+ * Automatically register the ErrorCodeModule in CoreModule
631
+ *
632
+ * Set to `false` to disable auto-registration and provide your own
633
+ * ErrorCodeModule with custom controller and/or service.
634
+ *
635
+ * @default true
636
+ *
637
+ * @example
638
+ * ```typescript
639
+ * // In config.env.ts - disable auto-registration
640
+ * errorCode: {
641
+ * autoRegister: false,
642
+ * }
643
+ *
644
+ * // In server.module.ts - import your custom module
645
+ * @Module({
646
+ * imports: [
647
+ * CoreModule.forRoot(...),
648
+ * ErrorCodeModule.forRoot(), // Your custom module
649
+ * ],
650
+ * })
651
+ * ```
652
+ */
653
+ autoRegister?: boolean;
654
+ }
655
+
592
656
  /**
593
657
  * Interface for JWT configuration (main and refresh)
594
658
  */
@@ -766,6 +830,31 @@ export interface IServerOptions {
766
830
  */
767
831
  env?: string;
768
832
 
833
+ /**
834
+ * Configuration for the error code module
835
+ *
836
+ * Controls how error codes and translations are handled.
837
+ *
838
+ * @since 11.9.0
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * // Default: auto-register with core errors only
843
+ * errorCode: undefined
844
+ *
845
+ * // Add project-specific error codes
846
+ * errorCode: {
847
+ * additionalErrorRegistry: ProjectErrors,
848
+ * }
849
+ *
850
+ * // Disable auto-registration to provide your own module
851
+ * errorCode: {
852
+ * autoRegister: false,
853
+ * }
854
+ * ```
855
+ */
856
+ errorCode?: IErrorCode;
857
+
769
858
  /**
770
859
  * Exec a command after server is initialized
771
860
  * e.g. 'npm run docs:bootstrap'
@@ -1132,4 +1221,158 @@ export interface IServerOptions {
1132
1221
  */
1133
1222
  path?: string;
1134
1223
  };
1224
+
1225
+ /**
1226
+ * TUS resumable upload configuration.
1227
+ *
1228
+ * Follows the "Enabled by Default" pattern - tus is automatically enabled
1229
+ * without any configuration. Set `tus: false` to explicitly disable.
1230
+ *
1231
+ * Accepts:
1232
+ * - `true` or `undefined`: Enable with defaults (enabled by default)
1233
+ * - `false`: Disable TUS uploads
1234
+ * - `{ ... }`: Enable with custom configuration
1235
+ *
1236
+ * @example
1237
+ * ```typescript
1238
+ * // Default: TUS enabled with all defaults (no config needed)
1239
+ *
1240
+ * // Disable TUS
1241
+ * tus: false,
1242
+ *
1243
+ * // Custom configuration
1244
+ * tus: {
1245
+ * maxSize: 100 * 1024 * 1024, // 100 MB
1246
+ * path: '/uploads',
1247
+ * },
1248
+ * ```
1249
+ *
1250
+ * @since 11.8.0
1251
+ */
1252
+ tus?: boolean | ITusConfig;
1253
+ }
1254
+
1255
+ /**
1256
+ * TUS Upload Configuration Interface
1257
+ *
1258
+ * Follows the "Enabled by Default" pattern - tus is automatically enabled
1259
+ * without any configuration. Set `tus: false` to explicitly disable.
1260
+ */
1261
+ export interface ITusConfig {
1262
+ /**
1263
+ * Additional allowed HTTP headers for TUS requests (beyond @tus/server defaults).
1264
+ *
1265
+ * Note: @tus/server already includes all TUS protocol headers:
1266
+ * Authorization, Content-Type, Location, Tus-Extension, Tus-Max-Size,
1267
+ * Tus-Resumable, Tus-Version, Upload-Concat, Upload-Defer-Length,
1268
+ * Upload-Length, Upload-Metadata, Upload-Offset, X-HTTP-Method-Override,
1269
+ * X-Requested-With, X-Forwarded-Host, X-Forwarded-Proto, Forwarded
1270
+ *
1271
+ * Use this only for project-specific custom headers.
1272
+ *
1273
+ * @default [] (no additional headers needed)
1274
+ */
1275
+ allowedHeaders?: string[];
1276
+
1277
+ /**
1278
+ * Allowed MIME types for uploads.
1279
+ * If undefined, all types are allowed.
1280
+ * @default undefined (all types allowed)
1281
+ */
1282
+ allowedTypes?: string[];
1283
+
1284
+ /**
1285
+ * Checksum extension configuration.
1286
+ * Enables data integrity verification.
1287
+ * @default true
1288
+ */
1289
+ checksum?: boolean;
1290
+
1291
+ /**
1292
+ * Concatenation extension configuration.
1293
+ * Allows parallel uploads that are merged.
1294
+ * @default true
1295
+ */
1296
+ concatenation?: boolean;
1297
+
1298
+ /**
1299
+ * Creation extension configuration.
1300
+ * Allows creating new uploads via POST.
1301
+ * @default true
1302
+ */
1303
+ creation?: boolean | ITusCreationConfig;
1304
+
1305
+ /**
1306
+ * Creation With Upload extension configuration.
1307
+ * Allows sending data in the initial POST request.
1308
+ * @default true
1309
+ */
1310
+ creationWithUpload?: boolean;
1311
+
1312
+ /**
1313
+ * Whether tus uploads are enabled.
1314
+ * @default true (enabled by default)
1315
+ */
1316
+ enabled?: boolean;
1317
+
1318
+ /**
1319
+ * Expiration extension configuration.
1320
+ * Automatically cleans up incomplete uploads.
1321
+ * @default { expiresIn: '24h' }
1322
+ */
1323
+ expiration?: boolean | ITusExpirationConfig;
1324
+
1325
+ /**
1326
+ * Maximum upload size in bytes
1327
+ * @default 50 * 1024 * 1024 * 1024 (50 GB)
1328
+ */
1329
+ maxSize?: number;
1330
+
1331
+ /**
1332
+ * Base path for tus endpoints
1333
+ * @default '/tus'
1334
+ */
1335
+ path?: string;
1336
+
1337
+ /**
1338
+ * Termination extension configuration.
1339
+ * Allows deleting uploads via DELETE.
1340
+ * @default true
1341
+ */
1342
+ termination?: boolean;
1343
+
1344
+ /**
1345
+ * Directory for temporary upload chunks.
1346
+ * @default 'uploads/tus'
1347
+ */
1348
+ uploadDir?: string;
1349
+ }
1350
+
1351
+ /**
1352
+ * TUS Creation extension configuration
1353
+ */
1354
+ export interface ITusCreationConfig {
1355
+ /**
1356
+ * Whether creation is enabled
1357
+ * @default true
1358
+ */
1359
+ enabled?: boolean;
1360
+ }
1361
+
1362
+ /**
1363
+ * TUS Expiration extension configuration
1364
+ */
1365
+ export interface ITusExpirationConfig {
1366
+ /**
1367
+ * Whether expiration is enabled
1368
+ * @default true
1369
+ */
1370
+ enabled?: boolean;
1371
+
1372
+ /**
1373
+ * Time until incomplete uploads expire
1374
+ * Supports formats: '24h', '1d', '12h', etc.
1375
+ * @default '24h'
1376
+ */
1377
+ expiresIn?: string;
1135
1378
  }
@@ -1,4 +1,4 @@
1
- import { ExecutionContext, Injectable, Logger, Optional, UnauthorizedException } from '@nestjs/common';
1
+ import { ExecutionContext, ForbiddenException, Injectable, Logger, Optional, UnauthorizedException } from '@nestjs/common';
2
2
  import { ModuleRef, Reflector } from '@nestjs/core';
3
3
  import { GqlExecutionContext } from '@nestjs/graphql';
4
4
  import { getConnectionToken } from '@nestjs/mongoose';
@@ -7,6 +7,7 @@ import { firstValueFrom, isObservable } from 'rxjs';
7
7
 
8
8
  import { RoleEnum } from '../../../common/enums/role.enum';
9
9
  import { BetterAuthService } from '../../better-auth/better-auth.service';
10
+ import { ErrorCode } from '../../error-code';
10
11
  import { AuthGuardStrategy } from '../auth-guard-strategy.enum';
11
12
  import { ExpiredTokenException } from '../exceptions/expired-token.exception';
12
13
  import { InvalidTokenException } from '../exceptions/invalid-token.exception';
@@ -331,7 +332,7 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
331
332
 
332
333
  // Check if locked
333
334
  if (roles && roles.includes(RoleEnum.S_NO_ONE)) {
334
- throw new UnauthorizedException('No access');
335
+ throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
335
336
  }
336
337
 
337
338
  // Check roles
@@ -354,11 +355,11 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
354
355
  if (info?.name === 'TokenExpiredError') {
355
356
  throw new ExpiredTokenException();
356
357
  }
357
- throw new UnauthorizedException('Unauthorized');
358
+ throw new UnauthorizedException(ErrorCode.UNAUTHORIZED);
358
359
  }
359
360
 
360
361
  // Requester is not authorized
361
- throw new UnauthorizedException('Missing role');
362
+ throw new ForbiddenException(ErrorCode.ACCESS_DENIED);
362
363
  }
363
364
 
364
365
  // Everything is ok
@@ -17,6 +17,7 @@ import { ServiceOptions } from '../../../common/interfaces/service-options.inter
17
17
  import { ConfigService } from '../../../common/services/config.service';
18
18
  import { BetterAuthUserMapper } from '../../better-auth/better-auth-user.mapper';
19
19
  import { BetterAuthService } from '../../better-auth/better-auth.service';
20
+ import { ErrorCode } from '../../error-code';
20
21
  import { CoreAuthModel } from '../core-auth.model';
21
22
  import { CoreAuthSignInInput } from '../inputs/core-auth-sign-in.input';
22
23
  import { CoreAuthSignUpInput } from '../inputs/core-auth-sign-up.input';
@@ -83,13 +84,13 @@ export class CoreAuthService {
83
84
  // Check authentication
84
85
  const user = serviceOptions.currentUser;
85
86
  if (!user || !tokenOrRefreshToken) {
86
- throw new UnauthorizedException('Invalid token');
87
+ throw new UnauthorizedException(ErrorCode.INVALID_TOKEN);
87
88
  }
88
89
 
89
90
  // Check authorization
90
91
  const deviceId = this.decodeJwt(tokenOrRefreshToken)?.deviceId;
91
92
  if (!deviceId || !user.refreshTokens[deviceId]) {
92
- throw new UnauthorizedException('Invalid token');
93
+ throw new UnauthorizedException(ErrorCode.INVALID_TOKEN);
93
94
  }
94
95
 
95
96
  // Logout from all devices
@@ -374,7 +375,7 @@ export class CoreAuthService {
374
375
  if (currentRefreshToken) {
375
376
  deviceId = this.decodeJwt(currentRefreshToken)?.deviceId;
376
377
  if (!deviceId || !user.refreshTokens?.[deviceId]) {
377
- throw new UnauthorizedException('Invalid token');
378
+ throw new UnauthorizedException(ErrorCode.INVALID_TOKEN);
378
379
  }
379
380
  if (!this.configService.getFastButReadOnly('jwt.refresh.renewal')) {
380
381
  // Return currentToken
@@ -398,7 +399,7 @@ export class CoreAuthService {
398
399
  // Set new token
399
400
  const payload = this.decodeJwt(newRefreshToken);
400
401
  if (!payload) {
401
- throw new UnauthorizedException('Invalid token');
402
+ throw new UnauthorizedException(ErrorCode.INVALID_TOKEN);
402
403
  }
403
404
  if (!deviceId) {
404
405
  deviceId = payload.deviceId;
@@ -0,0 +1,288 @@
1
+ # ErrorCode Integration Checklist
2
+
3
+ **For integrating custom error codes into projects using `@lenne.tech/nest-server`.**
4
+
5
+ > **Estimated time:** 5-10 minutes
6
+
7
+ ---
8
+
9
+ ## Choose Your Scenario
10
+
11
+ | Scenario | Use When | Configuration | Complexity |
12
+ |----------|----------|---------------|------------|
13
+ | **A. additionalErrorRegistry** | Simple error code addition | Config in `config.env.ts` | Minimal |
14
+ | **B. Custom Service** | Need custom locales or logic | Service inheritance | Low |
15
+ | **C. Custom Controller** | Need custom controller/routes | Service + Controller | Medium |
16
+
17
+ **Recommendation:** Start with **Scenario A**. Only use B or C if you need customization beyond adding error codes.
18
+
19
+ ---
20
+
21
+ ## Reference Implementation
22
+
23
+ All files are available as reference in the package:
24
+
25
+ **Local (in your node_modules):**
26
+ ```
27
+ node_modules/@lenne.tech/nest-server/src/server/modules/error-code/
28
+ ```
29
+
30
+ **GitHub:**
31
+ https://github.com/lenneTech/nest-server/tree/develop/src/server/modules/error-code
32
+
33
+ ---
34
+
35
+ ## Scenario A: additionalErrorRegistry (Simplest)
36
+
37
+ Use this when you just want to add project-specific error codes.
38
+
39
+ ### 1. Define Error Codes
40
+
41
+ **Create:** `src/server/common/errors/project-errors.ts`
42
+
43
+ ```typescript
44
+ import { IErrorRegistry, mergeErrorCodes } from '@lenne.tech/nest-server';
45
+
46
+ /**
47
+ * Project-specific error codes
48
+ *
49
+ * Format: PREFIX_XXXX (e.g., PROJ_0001, APP_0001)
50
+ * Use a unique prefix to avoid collisions with LTNS_* core errors.
51
+ */
52
+ export const ProjectErrors = {
53
+ ORDER_NOT_FOUND: {
54
+ code: 'PROJ_0001',
55
+ message: 'Order not found',
56
+ translations: {
57
+ de: 'Bestellung mit ID {orderId} wurde nicht gefunden.',
58
+ en: 'Order with ID {orderId} was not found.',
59
+ },
60
+ },
61
+ PAYMENT_FAILED: {
62
+ code: 'PROJ_0002',
63
+ message: 'Payment processing failed',
64
+ translations: {
65
+ de: 'Die Zahlung konnte nicht verarbeitet werden: {reason}',
66
+ en: 'Payment processing failed: {reason}',
67
+ },
68
+ },
69
+ } as const satisfies IErrorRegistry;
70
+
71
+ // Merged error codes for type-safe factory functions
72
+ export const ErrorCode = mergeErrorCodes(ProjectErrors);
73
+ ```
74
+
75
+ ### 2. Add to config.env.ts
76
+
77
+ ```typescript
78
+ import { ProjectErrors } from './server/common/errors/project-errors';
79
+
80
+ const config = {
81
+ // ... other config ...
82
+ errorCode: {
83
+ additionalErrorRegistry: ProjectErrors,
84
+ },
85
+ };
86
+ ```
87
+
88
+ **Done!** Your project errors are now available via `/api/i18n/errors/:locale`.
89
+
90
+ ---
91
+
92
+ ## Scenario B: Custom Service (For Custom Locales)
93
+
94
+ Use this when you need:
95
+ - Additional locales (e.g., French, Spanish)
96
+ - Custom logic in the service
97
+
98
+ ### 1. Define Error Codes
99
+
100
+ Same as Scenario A, Step 1.
101
+
102
+ ### 2. Create Custom Service
103
+
104
+ **Create:** `src/server/modules/error-code/error-code.service.ts`
105
+ **Copy from:** `node_modules/@lenne.tech/nest-server/src/server/modules/error-code/error-code.service.ts`
106
+
107
+ **Optional customization - add locales:**
108
+ ```typescript
109
+ @Injectable()
110
+ export class ErrorCodeService extends CoreErrorCodeService {
111
+ // Override to add more locales
112
+ protected override supportedLocales = ['de', 'en', 'fr', 'es'] as const;
113
+
114
+ constructor() {
115
+ super();
116
+ this.registerErrorRegistry(ProjectErrors);
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### 3. Disable Auto-Registration
122
+
123
+ **Update:** `src/config.env.ts`
124
+
125
+ ```typescript
126
+ const config = {
127
+ // ... other config ...
128
+ errorCode: {
129
+ autoRegister: false, // Required! Prevents CoreModule from registering its own
130
+ },
131
+ };
132
+ ```
133
+
134
+ **WHY is `autoRegister: false` required?**
135
+ NestJS @Global() modules use "first wins" for provider registration. Without this, CoreModule's ErrorCodeModule loads first and your custom service is ignored.
136
+
137
+ ### 4. Register in ServerModule
138
+
139
+ **Update:** `src/server/server.module.ts`
140
+
141
+ ```typescript
142
+ import { ErrorCodeModule as CoreErrorCodeModule } from '@lenne.tech/nest-server';
143
+ import { ErrorCodeService } from './modules/error-code/error-code.service';
144
+
145
+ @Module({
146
+ imports: [
147
+ CoreModule.forRoot(...),
148
+ // Register with custom service
149
+ CoreErrorCodeModule.forRoot({
150
+ service: ErrorCodeService,
151
+ }),
152
+ // ... other modules
153
+ ],
154
+ })
155
+ export class ServerModule {}
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Scenario C: Custom Controller (For Custom Routes)
161
+
162
+ Use this when you need:
163
+ - Custom controller endpoints (e.g., `/codes` listing)
164
+ - Different route paths
165
+ - Additional REST endpoints
166
+
167
+ **No custom module needed!** Use Core `ErrorCodeModule.forRoot()` with your custom controller and service.
168
+
169
+ ### 1. Create Files
170
+
171
+ **Copy from:** `node_modules/@lenne.tech/nest-server/src/server/modules/error-code/`
172
+
173
+ Files needed:
174
+ - `error-codes.ts` - Your error definitions
175
+ - `error-code.service.ts` - Service extending CoreErrorCodeService
176
+ - `error-code.controller.ts` - Controller (**standalone**, not extending)
177
+ - `index.ts` - Exports
178
+
179
+ **No `error-code.module.ts` needed!**
180
+
181
+ ### 2. Disable Auto-Registration
182
+
183
+ Same as Scenario B, Step 3.
184
+
185
+ ### 3. Register via Core ErrorCodeModule
186
+
187
+ **Update:** `src/server/server.module.ts`
188
+
189
+ ```typescript
190
+ import { ErrorCodeModule } from '@lenne.tech/nest-server';
191
+ import { ErrorCodeController } from './modules/error-code/error-code.controller';
192
+ import { ErrorCodeService } from './modules/error-code/error-code.service';
193
+
194
+ @Module({
195
+ imports: [
196
+ CoreModule.forRoot(...),
197
+ // Use Core ErrorCodeModule with custom service and controller
198
+ ErrorCodeModule.forRoot({
199
+ controller: ErrorCodeController,
200
+ service: ErrorCodeService,
201
+ }),
202
+ // ... other modules
203
+ ],
204
+ })
205
+ export class ServerModule {}
206
+ ```
207
+
208
+ **WHY standalone controller instead of extending?**
209
+ NestJS registers routes from parent classes first, regardless of method order in child classes. This causes `:locale` to intercept `/codes`. A standalone controller ensures correct route order: static routes (`/codes`) first, then parameterized routes (`:locale`).
210
+
211
+ ---
212
+
213
+ ## Verification Checklist
214
+
215
+ After integration, verify:
216
+
217
+ - [ ] `npm run build` succeeds without errors
218
+ - [ ] `npm test` passes
219
+ - [ ] `GET /api/i18n/errors/de` returns your project error codes
220
+ - [ ] `GET /api/i18n/errors/en` returns English translations
221
+ - [ ] Error codes follow format `PREFIX_XXXX` (e.g., `PROJ_0001`)
222
+ - [ ] Translations include placeholders where needed (`{param}`)
223
+
224
+ ### For Scenario C only:
225
+ - [ ] `GET /api/i18n/errors/codes` returns all error codes (if implemented)
226
+
227
+ ---
228
+
229
+ ## Common Mistakes
230
+
231
+ | Mistake | Symptom | Fix |
232
+ |---------|---------|-----|
233
+ | Forgot `autoRegister: false` | Project errors not appearing | Add `errorCode: { autoRegister: false }` to config |
234
+ | Wrong error code format | Validation errors | Use `PREFIX_XXXX` format (4 digits) |
235
+ | Missing translations | Runtime errors | Ensure all locales have translations |
236
+ | Controller extends CoreErrorCodeController | `/codes` returns 404 | Use standalone controller |
237
+ | Duplicate error codes | Unpredictable behavior | Ensure unique codes across all registries |
238
+ | Forgot to import module | No error translations | Import ErrorCodeModule in ServerModule |
239
+
240
+ ---
241
+
242
+ ## Using Error Codes in Code
243
+
244
+ ```typescript
245
+ import { ErrorCode, Errors } from '@lenne.tech/nest-server';
246
+
247
+ // Type-safe error code access
248
+ const code = ErrorCode.userNotFound; // Returns '#LTNS_0001: User not found'
249
+
250
+ // Factory functions with parameters
251
+ throw new BadRequestException(Errors.userNotFound({ email: 'test@example.com' }));
252
+ // Throws: '#LTNS_0001: User with email test@example.com was not found.'
253
+
254
+ // Project-specific errors (after registration)
255
+ import { ErrorCode as ProjectErrorCode } from './common/errors/project-errors';
256
+
257
+ const orderCode = ProjectErrorCode.ORDER_NOT_FOUND; // '#PROJ_0001: Order not found'
258
+ ```
259
+
260
+ ---
261
+
262
+ ## API Reference
263
+
264
+ ### REST Endpoints
265
+
266
+ | Endpoint | Method | Description |
267
+ |----------|--------|-------------|
268
+ | `/api/i18n/errors/:locale` | GET | Get translations for locale (de, en, ...) |
269
+ | `/api/i18n/errors/codes` | GET | Get all error codes (Scenario C only) |
270
+
271
+ ### Response Format (Nuxt i18n compatible)
272
+
273
+ ```json
274
+ {
275
+ "errors": {
276
+ "LTNS_0001": "Benutzer mit E-Mail {email} wurde nicht gefunden.",
277
+ "PROJ_0001": "Bestellung mit ID {orderId} wurde nicht gefunden."
278
+ }
279
+ }
280
+ ```
281
+
282
+ ---
283
+
284
+ ## Detailed Documentation
285
+
286
+ For complete API reference and advanced topics:
287
+ - **Core Error Codes:** `node_modules/@lenne.tech/nest-server/src/core/modules/error-code/error-codes.ts`
288
+ - **Interfaces:** `node_modules/@lenne.tech/nest-server/src/core/modules/error-code/interfaces/error-code.interfaces.ts`