@flusys/nestjs-shared 0.1.0-beta.1 → 0.1.0-beta.3

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 (136) hide show
  1. package/README.md +1399 -0
  2. package/cjs/classes/api-controller.class.js +473 -0
  3. package/cjs/classes/api-service.class.js +493 -0
  4. package/cjs/classes/hybrid-cache.class.js +96 -0
  5. package/cjs/classes/index.js +23 -0
  6. package/cjs/classes/request-scoped-api.service.js +110 -0
  7. package/cjs/classes/winston-logger-adapter.class.js +121 -0
  8. package/cjs/classes/winston.logger.class.js +251 -0
  9. package/cjs/constants/index.js +62 -0
  10. package/cjs/decorators/api-response.decorator.js +53 -0
  11. package/cjs/decorators/current-user.decorator.js +16 -0
  12. package/cjs/decorators/index.js +21 -0
  13. package/cjs/decorators/public.decorator.js +13 -0
  14. package/cjs/decorators/require-permission.decorator.js +32 -0
  15. package/cjs/dtos/delete.dto.js +82 -0
  16. package/cjs/dtos/filter-and-pagination.dto.js +174 -0
  17. package/cjs/dtos/identity-response.dto.js +112 -0
  18. package/cjs/dtos/index.js +22 -0
  19. package/cjs/dtos/pagination.dto.js +71 -0
  20. package/cjs/dtos/response-payload.dto.js +458 -0
  21. package/cjs/entities/identity.js +94 -0
  22. package/cjs/entities/index.js +19 -0
  23. package/cjs/entities/user-root.js +189 -0
  24. package/cjs/exceptions/index.js +18 -0
  25. package/cjs/exceptions/permission.exception.js +52 -0
  26. package/cjs/guards/index.js +19 -0
  27. package/cjs/guards/jwt-auth.guard.js +72 -0
  28. package/cjs/guards/permission.guard.js +266 -0
  29. package/cjs/index.js +28 -130
  30. package/cjs/interceptors/delete-empty-id-from-body.interceptor.js +40 -0
  31. package/cjs/interceptors/idempotency.interceptor.js +96 -0
  32. package/cjs/interceptors/index.js +25 -0
  33. package/cjs/interceptors/query-performance.interceptor.js +66 -0
  34. package/cjs/interceptors/response-meta.interceptor.js +47 -0
  35. package/cjs/interceptors/set-create-by-on-body.interceptor.js +40 -0
  36. package/cjs/interceptors/set-delete-by-on-body.interceptor.js +40 -0
  37. package/cjs/interceptors/set-update-by-on-body.interceptor.js +40 -0
  38. package/cjs/interceptors/slug.interceptor.js +54 -0
  39. package/cjs/interfaces/api.interface.js +4 -0
  40. package/cjs/interfaces/base-query.interface.js +6 -0
  41. package/cjs/interfaces/identity.interface.js +4 -0
  42. package/cjs/interfaces/index.js +23 -0
  43. package/cjs/interfaces/logged-user-info.interface.js +7 -0
  44. package/cjs/interfaces/logger.interface.js +7 -0
  45. package/cjs/interfaces/permission.interface.js +13 -0
  46. package/cjs/middlewares/index.js +18 -0
  47. package/cjs/middlewares/logger.middleware.js +250 -0
  48. package/cjs/modules/cache/cache.module.js +39 -0
  49. package/cjs/modules/datasource/datasource.module.js +120 -0
  50. package/cjs/modules/datasource/index.js +19 -0
  51. package/cjs/modules/datasource/multi-tenant-datasource.service.js +345 -0
  52. package/cjs/modules/index.js +21 -0
  53. package/cjs/modules/utils/utils.module.js +32 -0
  54. package/cjs/modules/utils/utils.service.js +255 -0
  55. package/cjs/utils/error-handler.util.js +67 -0
  56. package/cjs/utils/index.js +18 -0
  57. package/fesm/classes/api-controller.class.js +510 -0
  58. package/fesm/classes/api-service.class.js +487 -0
  59. package/fesm/classes/hybrid-cache.class.js +81 -0
  60. package/fesm/classes/index.js +6 -0
  61. package/fesm/classes/request-scoped-api.service.js +100 -0
  62. package/fesm/classes/winston-logger-adapter.class.js +124 -0
  63. package/fesm/classes/winston.logger.class.js +208 -0
  64. package/fesm/constants/index.js +29 -0
  65. package/fesm/decorators/api-response.decorator.js +60 -0
  66. package/fesm/decorators/current-user.decorator.js +18 -0
  67. package/fesm/decorators/index.js +4 -0
  68. package/fesm/decorators/public.decorator.js +20 -0
  69. package/fesm/decorators/require-permission.decorator.js +75 -0
  70. package/fesm/dtos/delete.dto.js +72 -0
  71. package/fesm/dtos/filter-and-pagination.dto.js +169 -0
  72. package/fesm/dtos/identity-response.dto.js +102 -0
  73. package/fesm/dtos/index.js +5 -0
  74. package/fesm/dtos/pagination.dto.js +61 -0
  75. package/fesm/dtos/response-payload.dto.js +436 -0
  76. package/fesm/entities/identity.js +84 -0
  77. package/fesm/entities/index.js +2 -0
  78. package/fesm/entities/user-root.js +179 -0
  79. package/fesm/exceptions/index.js +1 -0
  80. package/fesm/exceptions/permission.exception.js +37 -0
  81. package/fesm/guards/index.js +2 -0
  82. package/fesm/guards/jwt-auth.guard.js +62 -0
  83. package/fesm/guards/permission.guard.js +256 -0
  84. package/fesm/index.js +12 -131
  85. package/fesm/interceptors/delete-empty-id-from-body.interceptor.js +30 -0
  86. package/fesm/interceptors/idempotency.interceptor.js +86 -0
  87. package/fesm/interceptors/index.js +8 -0
  88. package/fesm/interceptors/query-performance.interceptor.js +56 -0
  89. package/fesm/interceptors/response-meta.interceptor.js +37 -0
  90. package/fesm/interceptors/set-create-by-on-body.interceptor.js +30 -0
  91. package/fesm/interceptors/set-delete-by-on-body.interceptor.js +30 -0
  92. package/fesm/interceptors/set-update-by-on-body.interceptor.js +30 -0
  93. package/fesm/interceptors/slug.interceptor.js +44 -0
  94. package/fesm/interfaces/api.interface.js +1 -0
  95. package/fesm/interfaces/base-query.interface.js +3 -0
  96. package/fesm/interfaces/identity.interface.js +1 -0
  97. package/fesm/interfaces/index.js +6 -0
  98. package/fesm/interfaces/logged-user-info.interface.js +4 -0
  99. package/fesm/interfaces/logger.interface.js +4 -0
  100. package/fesm/interfaces/permission.interface.js +15 -0
  101. package/fesm/middlewares/index.js +1 -0
  102. package/fesm/middlewares/logger.middleware.js +215 -0
  103. package/fesm/modules/cache/cache.module.js +29 -0
  104. package/fesm/modules/datasource/datasource.module.js +110 -0
  105. package/fesm/modules/datasource/index.js +2 -0
  106. package/fesm/modules/datasource/multi-tenant-datasource.service.js +335 -0
  107. package/fesm/modules/index.js +4 -0
  108. package/fesm/modules/utils/utils.module.js +22 -0
  109. package/fesm/modules/utils/utils.service.js +245 -0
  110. package/fesm/utils/error-handler.util.js +59 -0
  111. package/fesm/utils/index.js +1 -0
  112. package/package.json +32 -27
  113. package/cjs/classes-index.js +0 -131
  114. package/cjs/constants-index.js +0 -1
  115. package/cjs/decorators-index.js +0 -1
  116. package/cjs/dtos-index.js +0 -1
  117. package/cjs/entities-index.js +0 -1
  118. package/cjs/exceptions-index.js +0 -1
  119. package/cjs/guards-index.js +0 -2
  120. package/cjs/interceptors-index.js +0 -106
  121. package/cjs/interfaces-index.js +0 -1
  122. package/cjs/middlewares-index.js +0 -2
  123. package/cjs/modules-index.js +0 -114
  124. package/cjs/utils-index.js +0 -1
  125. package/fesm/classes-index.js +0 -131
  126. package/fesm/constants-index.js +0 -1
  127. package/fesm/decorators-index.js +0 -1
  128. package/fesm/dtos-index.js +0 -1
  129. package/fesm/entities-index.js +0 -1
  130. package/fesm/exceptions-index.js +0 -1
  131. package/fesm/guards-index.js +0 -2
  132. package/fesm/interceptors-index.js +0 -106
  133. package/fesm/interfaces-index.js +0 -0
  134. package/fesm/middlewares-index.js +0 -2
  135. package/fesm/modules-index.js +0 -114
  136. package/fesm/utils-index.js +0 -1
package/README.md ADDED
@@ -0,0 +1,1399 @@
1
+ # Shared Package Guide
2
+
3
+ > **Package:** `@flusys/nestjs-shared`
4
+ > **Type:** Shared NestJS utilities, classes, decorators, guards, and modules
5
+
6
+ This comprehensive guide covers the shared package - the shared NestJS infrastructure layer.
7
+
8
+ ## Table of Contents
9
+
10
+ - [Overview](#overview)
11
+ - [Installation](#installation)
12
+ - [Package Architecture](#package-architecture)
13
+ - [ApiService - Generic CRUD Service](#apiservice---generic-crud-service)
14
+ - [ApiController - Generic CRUD Controller](#apicontroller---generic-crud-controller)
15
+ - [Decorators](#decorators)
16
+ - [Guards](#guards)
17
+ - [Middleware](#middleware)
18
+ - [Interceptors](#interceptors)
19
+ - [Caching System](#caching-system)
20
+ - [Multi-Tenant DataSource](#multi-tenant-datasource)
21
+ - [DTOs](#dtos)
22
+ - [Base Entities](#base-entities)
23
+ - [Error Handling](#error-handling)
24
+ - [API Reference](#api-reference)
25
+
26
+ ---
27
+
28
+ ## Overview
29
+
30
+ `@flusys/nestjs-shared` provides shared utilities for building scalable NestJS applications:
31
+
32
+ - **Generic CRUD** - Standardized API controller and service patterns
33
+ - **Permission System** - Role and permission-based access control
34
+ - **Caching** - In-memory + Redis hybrid caching
35
+ - **Request Correlation** - AsyncLocalStorage-based request tracking (NEW)
36
+ - **Middleware** - Logging, correlation, and performance monitoring (NEW)
37
+ - **Interceptors** - Response metadata, idempotency, auto field setting
38
+ - **Multi-Tenancy** - Dynamic database connection management
39
+ - **Error Handling** - Centralized error handling utilities
40
+
41
+ ### Package Hierarchy
42
+
43
+ ```
44
+ @flusys/nestjs-core ← Pure TypeScript (foundation)
45
+
46
+ @flusys/nestjs-shared ← Shared NestJS utilities (THIS PACKAGE)
47
+
48
+ @flusys/nestjs-auth ← Uses common classes
49
+
50
+ @flusys/nestjs-iam ← Uses common patterns
51
+
52
+ @flusys/nestjs-storage ← Uses common patterns
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ npm install @flusys/nestjs-shared @flusys/nestjs-core
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Package Architecture
66
+
67
+ ```
68
+ nestjs-shared/
69
+ ├── src/
70
+ │ ├── classes/ # Base classes
71
+ │ │ ├── api-service.class.ts # Generic CRUD service
72
+ │ │ ├── api-controller.class.ts # Generic CRUD controller factory
73
+ │ │ ├── request-scoped-api.service.ts # REQUEST-scoped service base
74
+ │ │ ├── hybrid-cache.class.ts # Two-tier caching
75
+ │ │ ├── winston.logger.class.ts # Winston logger config
76
+ │ │ ├── winston-logger-adapter.class.ts # Logger adapters
77
+ │ │ └── index.ts
78
+ │ │
79
+ │ ├── constants/ # Injection tokens & constants
80
+ │ │ └── index.ts
81
+ │ │
82
+ │ ├── decorators/ # Custom decorators
83
+ │ │ ├── api-response.decorator.ts # Swagger response decorator
84
+ │ │ ├── current-user.decorator.ts
85
+ │ │ ├── public.decorator.ts
86
+ │ │ ├── require-permission.decorator.ts
87
+ │ │ └── index.ts
88
+ │ │
89
+ │ ├── dtos/ # Shared DTOs
90
+ │ │ ├── delete.dto.ts
91
+ │ │ ├── filter-and-pagination.dto.ts
92
+ │ │ ├── identity-response.dto.ts
93
+ │ │ ├── pagination.dto.ts
94
+ │ │ ├── response-payload.dto.ts
95
+ │ │ └── index.ts
96
+ │ │
97
+ │ ├── entities/ # Base entities
98
+ │ │ ├── identity.ts
99
+ │ │ ├── user-root.ts
100
+ │ │ └── index.ts
101
+ │ │
102
+ │ ├── exceptions/ # Custom exceptions
103
+ │ │ ├── permission.exception.ts
104
+ │ │ └── index.ts
105
+ │ │
106
+ │ ├── guards/ # Authentication & authorization
107
+ │ │ ├── jwt-auth.guard.ts # JWT token validation
108
+ │ │ ├── permission.guard.ts # Permission checks
109
+ │ │ └── index.ts
110
+ │ │
111
+ │ ├── interceptors/ # Request/response interceptors
112
+ │ │ ├── delete-empty-id-from-body.interceptor.ts
113
+ │ │ ├── idempotency.interceptor.ts
114
+ │ │ ├── query-performance.interceptor.ts
115
+ │ │ ├── response-meta.interceptor.ts
116
+ │ │ ├── set-create-by-on-body.interceptor.ts
117
+ │ │ ├── set-delete-by-on-body.interceptor.ts
118
+ │ │ ├── set-update-by-on-body.interceptor.ts
119
+ │ │ ├── slug.interceptor.ts
120
+ │ │ └── index.ts
121
+ │ │
122
+ │ ├── interfaces/ # TypeScript interfaces
123
+ │ │ ├── api.interface.ts
124
+ │ │ ├── base-query.interface.ts
125
+ │ │ ├── identity.interface.ts
126
+ │ │ ├── logged-user-info.interface.ts
127
+ │ │ ├── logger.interface.ts
128
+ │ │ ├── permission.interface.ts
129
+ │ │ └── index.ts
130
+ │ │
131
+ │ ├── middlewares/ # Middleware
132
+ │ │ ├── logger.middleware.ts # Request logging & correlation
133
+ │ │ └── index.ts
134
+ │ │
135
+ │ ├── modules/ # NestJS modules
136
+ │ │ ├── cache/cache.module.ts
137
+ │ │ ├── datasource/
138
+ │ │ │ ├── datasource.module.ts
139
+ │ │ │ ├── multi-tenant-datasource.service.ts
140
+ │ │ │ └── index.ts
141
+ │ │ ├── utils/
142
+ │ │ │ ├── utils.module.ts
143
+ │ │ │ └── utils.service.ts
144
+ │ │ └── index.ts
145
+ │ │
146
+ │ └── utils/ # Utility functions
147
+ │ ├── error-handler.util.ts
148
+ │ └── index.ts
149
+ ```
150
+
151
+ ---
152
+
153
+ ## ApiService - Generic CRUD Service
154
+
155
+ The `ApiService` base class provides standardized CRUD operations with caching, pagination, and transaction support.
156
+
157
+ ### Basic Usage
158
+
159
+ ```typescript
160
+ import { ApiService, HybridCache } from '@flusys/nestjs-shared/classes';
161
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
162
+ import { Injectable, Inject } from '@nestjs/common';
163
+ import { InjectRepository } from '@nestjs/typeorm';
164
+ import { Repository } from 'typeorm';
165
+ import { User } from './user.entity';
166
+ import { CreateUserDto, UpdateUserDto } from './user.dto';
167
+ import { IUser } from './user.interface';
168
+
169
+ @Injectable()
170
+ export class UserService extends ApiService<
171
+ CreateUserDto, // Create DTO
172
+ UpdateUserDto, // Update DTO
173
+ IUser, // Interface
174
+ User, // Entity
175
+ Repository<User> // Repository type
176
+ > {
177
+ constructor(
178
+ @InjectRepository(User)
179
+ protected override repository: Repository<User>,
180
+ @Inject('CACHE_INSTANCE')
181
+ protected override cacheManager: HybridCache,
182
+ protected override utilsService: UtilsService,
183
+ ) {
184
+ super(
185
+ 'user', // Entity name (for query building)
186
+ repository,
187
+ cacheManager,
188
+ utilsService,
189
+ 'UserService', // Service name (for logging)
190
+ true, // Enable caching
191
+ );
192
+ }
193
+
194
+ // Override to customize DTO to entity conversion
195
+ override async convertSingleDtoToEntity(
196
+ dto: CreateUserDto | UpdateUserDto,
197
+ user: ILoggedUserInfo,
198
+ ): Promise<User> {
199
+ const entity = new User();
200
+ Object.assign(entity, dto);
201
+ return entity;
202
+ }
203
+
204
+ // Override to customize query selection
205
+ override async getSelectQuery(
206
+ query: SelectQueryBuilder<User>,
207
+ user: ILoggedUserInfo,
208
+ select?: string[],
209
+ ) {
210
+ if (!select?.length) {
211
+ select = ['id', 'name', 'email', 'createdAt'];
212
+ }
213
+ const selectFields = select.map(f => `${this.entityName}.${f}`);
214
+ query.select(selectFields);
215
+ return { query, isRaw: false };
216
+ }
217
+ }
218
+ ```
219
+
220
+ ### ApiService Methods
221
+
222
+ | Method | Description |
223
+ |--------|-------------|
224
+ | `insert(dto, user)` | Create single entity |
225
+ | `insertMany(dtos, user)` | Create multiple entities |
226
+ | `getById(id, user, select?)` | Get entity by ID |
227
+ | `findById(id, user, select?)` | Find entity (returns null if not found) |
228
+ | `getAll(dto, user)` | Get paginated list |
229
+ | `update(dto, user)` | Update single entity |
230
+ | `updateMany(dtos, user)` | Update multiple entities |
231
+ | `delete(dto, user)` | Soft/permanent delete |
232
+ | `restore(dto, user)` | Restore soft-deleted |
233
+
234
+ ### Customization Hooks
235
+
236
+ ```typescript
237
+ @Injectable()
238
+ export class UserService extends ApiService<...> {
239
+ // Convert DTO to entity
240
+ override async convertSingleDtoToEntity(dto, user): Promise<User> { }
241
+
242
+ // Customize SELECT query
243
+ override async getSelectQuery(query, user, select?): Promise<{ query, isRaw }> { }
244
+
245
+ // Add WHERE filters
246
+ override async getFilterQuery(query, filter, user): Promise<{ query, isRaw }> { }
247
+
248
+ // Add extra query conditions (e.g., company filtering)
249
+ override async getExtraManipulateQuery(query, dto, user): Promise<{ query, isRaw }> { }
250
+
251
+ // Before insert hook
252
+ override async beforeInsertOperation(entity, user, queryRunner): Promise<void> { }
253
+
254
+ // After insert hook
255
+ override async afterInsertOperation(entity, user, queryRunner): Promise<void> { }
256
+
257
+ // Before update hook
258
+ override async beforeUpdateOperation(oldEntity, newEntity, user, queryRunner): Promise<void> { }
259
+
260
+ // After update hook
261
+ override async afterUpdateOperation(entity, user, queryRunner): Promise<void> { }
262
+
263
+ // Before delete hook
264
+ override async beforeDeleteOperation(dto, user, queryRunner): Promise<void> { }
265
+
266
+ // After delete hook
267
+ override async afterDeleteOperation(dto, user, queryRunner): Promise<void> { }
268
+ }
269
+ ```
270
+
271
+ ### Company/Branch Filtering Example
272
+
273
+ ```typescript
274
+ override async getExtraManipulateQuery(
275
+ query: SelectQueryBuilder<User>,
276
+ dto: FilterAndPaginationDto,
277
+ user: ILoggedUserInfo,
278
+ ) {
279
+ // Filter by user's company
280
+ if (user.companyId) {
281
+ query.andWhere(`${this.entityName}.companyId = :companyId`, {
282
+ companyId: user.companyId,
283
+ });
284
+ }
285
+
286
+ // Filter by user's branch
287
+ if (user.branchId) {
288
+ query.andWhere(`${this.entityName}.branchId = :branchId`, {
289
+ branchId: user.branchId,
290
+ });
291
+ }
292
+
293
+ return { query, isRaw: false };
294
+ }
295
+ ```
296
+
297
+ ---
298
+
299
+ ## ApiController - Generic CRUD Controller
300
+
301
+ The `createApiController` factory creates standardized REST API controllers.
302
+
303
+ ### Basic Usage
304
+
305
+ ```typescript
306
+ import { createApiController } from '@flusys/nestjs-shared/classes';
307
+ import { Controller } from '@nestjs/common';
308
+ import { ApiTags } from '@nestjs/swagger';
309
+ import { CreateUserDto, UpdateUserDto, UserResponseDto } from './user.dto';
310
+ import { IUser } from './user.interface';
311
+ import { UserService } from './user.service';
312
+
313
+ @ApiTags('Users')
314
+ @Controller('users')
315
+ export class UserController extends createApiController<
316
+ CreateUserDto,
317
+ UpdateUserDto,
318
+ UserResponseDto,
319
+ IUser,
320
+ UserService
321
+ >(CreateUserDto, UpdateUserDto, UserResponseDto) {
322
+ constructor(protected service: UserService) {
323
+ super(service);
324
+ }
325
+ }
326
+ ```
327
+
328
+ ### Generated Endpoints
329
+
330
+ | Endpoint | Method | Description |
331
+ |----------|--------|-------------|
332
+ | `/insert` | POST | Create entity |
333
+ | `/insert-many` | POST | Create multiple entities |
334
+ | `/get/:id` | POST | Get entity by ID |
335
+ | `/get-all` | POST | Get paginated list |
336
+ | `/update` | POST | Update entity |
337
+ | `/update-many` | POST | Update multiple entities |
338
+ | `/delete` | POST | Delete entity |
339
+
340
+ ### Security Configuration
341
+
342
+ ```typescript
343
+ // Global security (all endpoints)
344
+ @Controller('users')
345
+ export class UserController extends createApiController(
346
+ CreateUserDto,
347
+ UpdateUserDto,
348
+ UserResponseDto,
349
+ {
350
+ security: 'jwt', // All endpoints require JWT
351
+ },
352
+ ) {}
353
+
354
+ // Per-endpoint security
355
+ @Controller('users')
356
+ export class UserController extends createApiController(
357
+ CreateUserDto,
358
+ UpdateUserDto,
359
+ UserResponseDto,
360
+ {
361
+ security: {
362
+ getAll: 'public', // No auth required
363
+ getById: 'jwt', // JWT required
364
+ insert: { level: 'permission', permissions: ['users.create'] },
365
+ update: { level: 'permission', permissions: ['users.update'] },
366
+ delete: { level: 'permission', permissions: ['users.delete'] },
367
+ },
368
+ },
369
+ ) {}
370
+
371
+ // Permission combinations
372
+ {
373
+ security: {
374
+ // Require ANY of these permissions
375
+ insert: {
376
+ level: 'permission',
377
+ permissions: ['users.create', 'users.admin'],
378
+ require: 'any', // Default
379
+ },
380
+
381
+ // Require ALL permissions
382
+ delete: {
383
+ level: 'permission',
384
+ permissions: ['users.delete', 'users.admin'],
385
+ require: 'all',
386
+ },
387
+ },
388
+ }
389
+ ```
390
+
391
+ See [API-CONTROLLER-SECURITY.md](./API-CONTROLLER-SECURITY.md) for detailed security configuration.
392
+
393
+ ---
394
+
395
+ ## Decorators
396
+
397
+ ### @CurrentUser
398
+
399
+ Extract the logged-in user from request:
400
+
401
+ ```typescript
402
+ import { CurrentUser } from '@flusys/nestjs-shared/decorators';
403
+ import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
404
+
405
+ @Controller('profile')
406
+ export class ProfileController {
407
+ @Get()
408
+ getProfile(@CurrentUser() user: ILoggedUserInfo) {
409
+ return { userId: user.id, companyId: user.companyId };
410
+ }
411
+ }
412
+ ```
413
+
414
+ ### @Public
415
+
416
+ Mark route as public (skip authentication):
417
+
418
+ ```typescript
419
+ import { Public } from '@flusys/nestjs-shared/decorators';
420
+
421
+ @Controller('auth')
422
+ export class AuthController {
423
+ @Public()
424
+ @Post('login')
425
+ login() {
426
+ // No JWT required
427
+ }
428
+ }
429
+ ```
430
+
431
+ ### @RequirePermission
432
+
433
+ Require specific permission:
434
+
435
+ ```typescript
436
+ import { RequirePermission } from '@flusys/nestjs-shared/decorators';
437
+
438
+ @Controller('admin')
439
+ export class AdminController {
440
+ @RequirePermission('admin.dashboard')
441
+ @Get('dashboard')
442
+ getDashboard() {
443
+ // Requires 'admin.dashboard' permission
444
+ }
445
+ }
446
+ ```
447
+
448
+ ### @RequireAnyPermission
449
+
450
+ Require any of the listed permissions:
451
+
452
+ ```typescript
453
+ import { RequireAnyPermission } from '@flusys/nestjs-shared/decorators';
454
+
455
+ @Controller('reports')
456
+ export class ReportsController {
457
+ @RequireAnyPermission('reports.view', 'reports.admin')
458
+ @Get()
459
+ getReports() {
460
+ // Requires 'reports.view' OR 'reports.admin'
461
+ }
462
+ }
463
+ ```
464
+
465
+ ### @RequireAllPermissions
466
+
467
+ Require all listed permissions:
468
+
469
+ ```typescript
470
+ import { RequireAllPermissions } from '@flusys/nestjs-shared/decorators';
471
+
472
+ @Controller('sensitive')
473
+ export class SensitiveController {
474
+ @RequireAllPermissions('admin.access', 'security.clearance')
475
+ @Get()
476
+ getSensitiveData() {
477
+ // Requires BOTH permissions
478
+ }
479
+ }
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Guards
485
+
486
+ The shared package provides two guards for authentication and authorization:
487
+
488
+ ### JwtAuthGuard
489
+
490
+ **Location:** `@flusys/nestjs-shared/guards/jwt-auth.guard.ts`
491
+
492
+ The `JwtAuthGuard` validates JWT tokens for protected routes. It extends Passport's `AuthGuard('jwt')` and respects the `@Public()` decorator.
493
+
494
+ **Why in shared module:**
495
+ - All feature modules (auth, iam, storage) need JWT authentication
496
+ - Keeps feature modules independent (no cross-dependencies)
497
+ - JwtStrategy registration remains in auth module
498
+ - This guard just validates tokens using the registered strategy
499
+
500
+ **Usage:**
501
+
502
+ ```typescript
503
+ import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
504
+ import { UseGuards } from '@nestjs/common';
505
+
506
+ // Protect entire controller
507
+ @Controller('users')
508
+ @UseGuards(JwtAuthGuard)
509
+ export class UserController {
510
+ // All routes protected
511
+
512
+ @Post('login')
513
+ @Public() // Skip JWT check for this route
514
+ async login() { }
515
+ }
516
+
517
+ // Or apply globally
518
+ @Module({
519
+ providers: [
520
+ {
521
+ provide: APP_GUARD,
522
+ useClass: JwtAuthGuard,
523
+ },
524
+ ],
525
+ })
526
+ export class AppModule {}
527
+ ```
528
+
529
+ **Features:**
530
+ - Validates JWT tokens from `Authorization: Bearer <token>` header
531
+ - Respects `@Public()` decorator to skip authentication
532
+ - Throws `UnauthorizedException` for invalid/expired tokens
533
+ - Works with JwtStrategy registered in auth module
534
+
535
+ **Note:** The JwtStrategy (which registers the JWT validation logic with Passport) is still in the auth module at `@flusys/nestjs-auth/strategies/jwt.strategy.ts`. This guard uses that registered strategy.
536
+
537
+ ### PermissionGuard
538
+
539
+ **Location:** `@flusys/nestjs-shared/guards/permission.guard.ts`
540
+
541
+ The `PermissionGuard` handles permission-based access control.
542
+
543
+ ```typescript
544
+ import { Module } from '@nestjs/common';
545
+ import { APP_GUARD } from '@nestjs/core';
546
+ import { PermissionGuard } from '@flusys/nestjs-shared/guards';
547
+
548
+ @Module({
549
+ providers: [
550
+ {
551
+ provide: APP_GUARD,
552
+ useClass: PermissionGuard,
553
+ },
554
+ ],
555
+ })
556
+ export class AppModule {}
557
+ ```
558
+
559
+ ### Permission Cache Key Format
560
+
561
+ ```typescript
562
+ // Without company feature
563
+ `permissions:user:{userId}`
564
+
565
+ // With company feature (includes branchId for DIRECT permissions)
566
+ `permissions:company:{companyId}:branch:{branchId}:user:{userId}`
567
+ ```
568
+
569
+ ### Permission Guard Configuration
570
+
571
+ ```typescript
572
+ // Configure via PermissionModule options
573
+ PermissionModule.forRoot({
574
+ // Cache TTL in seconds
575
+ cacheTtl: 3600,
576
+
577
+ // Permission check mode
578
+ mode: 'RBAC' | 'DIRECT' | 'FULL',
579
+
580
+ // Custom permission resolver
581
+ resolver: CustomPermissionResolver,
582
+ });
583
+ ```
584
+
585
+ ---
586
+
587
+ ## Middleware
588
+
589
+ ### LoggerMiddleware (NEW - 2026-01-12, Enhanced - 2026-01-14)
590
+
591
+ **Location:** `@flusys/nestjs-shared/middlewares/logger.middleware.ts`
592
+
593
+ Combined middleware that handles:
594
+ 1. **Request Correlation** - Tracks requests across services using AsyncLocalStorage
595
+ 2. **HTTP Request/Response Logging** - Structured logging with Winston
596
+ 3. **Detailed Request/Response Information** - Complete visibility into all requests
597
+
598
+ **Key Features:**
599
+ - **Request ID Generation** - Automatic UUID generation or uses `x-request-id` header
600
+ - **Tenant ID Tracking** - Extracts `x-tenant-id` from headers for multi-tenant apps
601
+ - **AsyncLocalStorage Context** - Thread-safe request context accessible anywhere
602
+ - **Security** - Automatically redacts sensitive headers (authorization, cookie, x-api-key)
603
+ - **Performance Monitoring** - Logs slow requests (>3s) with dedicated warning logs
604
+ - **Body Truncation** - Limits log size to 1000 characters
605
+ - **Debug Mode** - Conditionally logs headers and body based on LOG_LEVEL=debug
606
+ - **Complete Request Details** - URL, path, query params, content type, user agent, client IP
607
+ - **Complete Response Details** - Status code, message, content type, content length, user/company context
608
+ - **Multiple Response Hooks** - Captures responses via `res.send()`, `res.json()`, and `res.end()`
609
+ - **Error Handling** - Logs response errors with stack traces
610
+ - **User Context** - Automatically includes userId and companyId in response logs if available
611
+
612
+ **Usage:**
613
+
614
+ ```typescript
615
+ import { LoggerMiddleware } from '@flusys/nestjs-shared/middlewares';
616
+ import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
617
+
618
+ @Module({
619
+ // ...
620
+ })
621
+ export class AppModule implements NestModule {
622
+ configure(consumer: MiddlewareConsumer) {
623
+ // Apply to all routes
624
+ consumer.apply(LoggerMiddleware).forRoutes('*');
625
+ }
626
+ }
627
+ ```
628
+
629
+ **Accessing Request Context:**
630
+
631
+ ```typescript
632
+ import {
633
+ getRequestId,
634
+ getTenantId,
635
+ getUserId,
636
+ getCompanyId,
637
+ setUserId,
638
+ setCompanyId,
639
+ } from '@flusys/nestjs-shared/middlewares';
640
+
641
+ @Injectable()
642
+ export class MyService {
643
+ async doSomething() {
644
+ const requestId = getRequestId(); // Get current request ID
645
+ const tenantId = getTenantId(); // Get tenant ID from header
646
+
647
+ // Set user context after authentication
648
+ setUserId('user-123');
649
+ setCompanyId('company-456');
650
+
651
+ // Use in logs
652
+ this.logger.info('Processing request', {
653
+ requestId,
654
+ tenantId,
655
+ userId: getUserId(),
656
+ companyId: getCompanyId(),
657
+ });
658
+ }
659
+ }
660
+ ```
661
+
662
+ **Request Context Interface:**
663
+
664
+ ```typescript
665
+ interface IRequestContext {
666
+ requestId: string; // UUID or from x-request-id header
667
+ tenantId?: string; // From x-tenant-id header
668
+ userId?: string; // Set after authentication
669
+ companyId?: string; // Set after authentication
670
+ startTime: number; // Request start timestamp
671
+ }
672
+ ```
673
+
674
+ **Configuration:**
675
+
676
+ ```typescript
677
+ // Environment-based configuration
678
+ const IS_DEBUG = envConfig.getLogConfig().level === 'debug';
679
+ const TENANT_ID_HEADER = 'x-tenant-id';
680
+ const EXCLUDED_PATHS = ['/health', '/metrics', '/favicon.ico'];
681
+ const EXCLUDED_HEADERS = ['authorization', 'cookie', 'x-api-key'];
682
+ const MAX_BODY_LOG_SIZE = 1000;
683
+ ```
684
+
685
+ **Console Log Format (Development):**
686
+
687
+ The development console logger displays HTTP requests in human-readable format:
688
+
689
+ ```
690
+ 2026-01-17 22:41:23 [INFO ] [HTTP] [POST /auth/login] [200] (45ms) [uuid] Incoming request
691
+ 2026-01-17 22:41:23 [INFO ] [HTTP] [POST /auth/login] [200] (45ms) [uuid] (user:user-123) Response [200]
692
+ 2026-01-17 22:41:25 [WARN ] [HTTP] [GET /api/users] [404] (12ms) [uuid] Response [404]
693
+ 2026-01-17 22:41:30 [ERROR ] [HTTP] [POST /api/orders] [500] (234ms) [uuid] (user:user-123) Response [500]
694
+ ```
695
+
696
+ **Format Structure:**
697
+ - `[timestamp]` - Request timestamp
698
+ - `[level]` - Log level (INFO, WARN, ERROR)
699
+ - `[context]` - Always "HTTP" for HTTP requests
700
+ - `[METHOD /path]` - HTTP method and endpoint path
701
+ - `[statusCode]` - HTTP response status (only in response logs)
702
+ - `(duration)` - Request duration (only in response logs)
703
+ - `[uuid]` - Request correlation ID
704
+ - `(user:userId)` - User ID if authenticated (only in response logs)
705
+
706
+ **File Log Format (Production):**
707
+
708
+ ```json
709
+ // Incoming request (Enhanced with full details)
710
+ {
711
+ "level": "info",
712
+ "message": "Incoming request",
713
+ "context": "HTTP",
714
+ "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
715
+ "tenantId": "tenant-123",
716
+ "method": "POST",
717
+ "url": "/api/users/insert?sort=name",
718
+ "path": "/api/users/insert",
719
+ "query": { "sort": "name" },
720
+ "ip": "192.168.1.100",
721
+ "userAgent": "Mozilla/5.0...",
722
+ "contentType": "application/json",
723
+ "contentLength": "342",
724
+ "headers": { ... }, // Only in debug mode
725
+ "body": { ... }, // Only in debug mode, truncated to 1000 chars
726
+ "params": { ... } // Only in debug mode (route params)
727
+ }
728
+
729
+ // Response (Enhanced with full details)
730
+ {
731
+ "level": "info",
732
+ "message": "Response [200]",
733
+ "context": "HTTP",
734
+ "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
735
+ "tenantId": "tenant-123",
736
+ "method": "POST",
737
+ "url": "/api/users/insert?sort=name",
738
+ "path": "/api/users/insert",
739
+ "statusCode": 200,
740
+ "statusMessage": "OK",
741
+ "duration": "125ms",
742
+ "durationMs": 125,
743
+ "contentType": "application/json; charset=utf-8",
744
+ "contentLength": "156",
745
+ "userId": "user-456", // Automatically added if available
746
+ "companyId": "company-789" // Automatically added if available
747
+ }
748
+
749
+ // Error response (includes response body automatically)
750
+ {
751
+ "level": "warn",
752
+ "message": "Response [400]",
753
+ "context": "HTTP",
754
+ "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
755
+ "tenantId": "tenant-123",
756
+ "method": "POST",
757
+ "url": "/api/users/insert",
758
+ "path": "/api/users/insert",
759
+ "statusCode": 400,
760
+ "statusMessage": "Bad Request",
761
+ "duration": "45ms",
762
+ "durationMs": 45,
763
+ "userId": "user-456",
764
+ "companyId": "company-789",
765
+ "responseBody": {
766
+ "statusCode": 400,
767
+ "message": "Validation failed",
768
+ "errors": ["Email is required"]
769
+ }
770
+ }
771
+
772
+ // Slow request warning (Enhanced with more context)
773
+ {
774
+ "level": "warn",
775
+ "message": "Slow request detected",
776
+ "context": "HTTP",
777
+ "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
778
+ "tenantId": "tenant-123",
779
+ "userId": "user-456",
780
+ "companyId": "company-789",
781
+ "method": "POST",
782
+ "url": "/api/reports/generate",
783
+ "path": "/api/reports/generate",
784
+ "duration": "3245ms",
785
+ "durationMs": 3245,
786
+ "threshold": "3000ms"
787
+ }
788
+
789
+ // Response error
790
+ {
791
+ "level": "error",
792
+ "message": "Response error",
793
+ "context": "HTTP",
794
+ "requestId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
795
+ "tenantId": "tenant-123",
796
+ "method": "POST",
797
+ "url": "/api/users/insert",
798
+ "path": "/api/users/insert",
799
+ "error": "Socket hang up",
800
+ "stack": "Error: Socket hang up\n at ..."
801
+ }
802
+ ```
803
+
804
+ **Benefits:**
805
+
806
+ - **Request Tracing** - Correlate logs across multiple services using requestId
807
+ - **Multi-Tenant Support** - Automatic tenant isolation in logs
808
+ - **Security** - Sensitive data automatically redacted
809
+ - **Performance Monitoring** - Identify slow endpoints
810
+ - **Debugging** - Conditional verbose logging
811
+
812
+ ---
813
+
814
+ ## Interceptors
815
+
816
+ ### ResponseMetaInterceptor
817
+
818
+ Adds request metadata to responses:
819
+
820
+ ```typescript
821
+ import { ResponseMetaInterceptor } from '@flusys/nestjs-shared/interceptors';
822
+
823
+ @UseInterceptors(ResponseMetaInterceptor)
824
+ @Controller('users')
825
+ export class UserController {}
826
+
827
+ // Response:
828
+ {
829
+ "data": [...],
830
+ "meta": {
831
+ "timestamp": "2024-01-01T00:00:00.000Z",
832
+ "requestId": "abc-123",
833
+ "responseTime": 45
834
+ }
835
+ }
836
+ ```
837
+
838
+ ### IdempotencyInterceptor
839
+
840
+ Prevent duplicate requests:
841
+
842
+ ```typescript
843
+ import { IdempotencyInterceptor } from '@flusys/nestjs-shared/interceptors';
844
+
845
+ @UseInterceptors(IdempotencyInterceptor)
846
+ @Controller('payments')
847
+ export class PaymentController {
848
+ @Post()
849
+ // Clients send X-Idempotency-Key header
850
+ // Duplicate requests return cached response
851
+ processPayment() {}
852
+ }
853
+ ```
854
+
855
+ ### SetCreatedByOnBody / SetUpdateByOnBody
856
+
857
+ Auto-set user IDs on request body:
858
+
859
+ ```typescript
860
+ import { SetCreatedByOnBody, SetUpdateByOnBody } from '@flusys/nestjs-shared/interceptors';
861
+
862
+ @Controller('posts')
863
+ export class PostController {
864
+ @UseInterceptors(SetCreatedByOnBody)
865
+ @Post()
866
+ create(@Body() dto: CreatePostDto) {
867
+ // dto.createdById is automatically set to current user ID
868
+ }
869
+
870
+ @UseInterceptors(SetUpdateByOnBody)
871
+ @Put()
872
+ update(@Body() dto: UpdatePostDto) {
873
+ // dto.updatedById is automatically set to current user ID
874
+ }
875
+ }
876
+ ```
877
+
878
+ ### Slug Interceptor
879
+
880
+ Auto-generate slugs from name field:
881
+
882
+ ```typescript
883
+ import { Slug } from '@flusys/nestjs-shared/interceptors';
884
+
885
+ @Controller('products')
886
+ export class ProductController {
887
+ @UseInterceptors(Slug)
888
+ @Post()
889
+ create(@Body() dto: CreateProductDto) {
890
+ // dto.slug is auto-generated from dto.name
891
+ // "My Product" -> "my-product"
892
+ }
893
+ }
894
+ ```
895
+
896
+ ---
897
+
898
+ ## Caching System
899
+
900
+ ### HybridCache
901
+
902
+ Two-tier caching with in-memory (fast) and Redis (distributed):
903
+
904
+ ```typescript
905
+ import { HybridCache } from '@flusys/nestjs-shared/classes';
906
+
907
+ @Injectable()
908
+ export class MyService {
909
+ constructor(
910
+ @Inject('CACHE_INSTANCE')
911
+ private cache: HybridCache,
912
+ ) {}
913
+
914
+ async getData(key: string) {
915
+ // Check cache first
916
+ const cached = await this.cache.get(key);
917
+ if (cached) return cached;
918
+
919
+ // Fetch from database
920
+ const data = await this.fetchFromDb();
921
+
922
+ // Store in cache (TTL in seconds)
923
+ await this.cache.set(key, data, 3600);
924
+
925
+ return data;
926
+ }
927
+
928
+ async invalidate(key: string) {
929
+ await this.cache.del(key);
930
+ }
931
+
932
+ async invalidatePattern(pattern: string) {
933
+ // Delete all keys matching pattern
934
+ await this.cache.delByPattern('users:*');
935
+ }
936
+ }
937
+ ```
938
+
939
+ ### CacheModule Setup
940
+
941
+ ```typescript
942
+ import { CacheModule } from '@flusys/nestjs-shared/modules';
943
+
944
+ @Module({
945
+ imports: [
946
+ CacheModule.forRoot({
947
+ // In-memory cache config
948
+ memory: {
949
+ max: 500, // Max items
950
+ ttl: 3600, // Default TTL (seconds)
951
+ },
952
+
953
+ // Redis config (optional)
954
+ redis: {
955
+ url: 'redis://localhost:6379',
956
+ ttl: 3600,
957
+ },
958
+ }),
959
+ ],
960
+ })
961
+ export class AppModule {}
962
+ ```
963
+
964
+ ### Cache Methods
965
+
966
+ | Method | Description |
967
+ |--------|-------------|
968
+ | `get(key)` | Get cached value |
969
+ | `set(key, value, ttl?)` | Set value with optional TTL |
970
+ | `del(key)` | Delete single key |
971
+ | `delByPattern(pattern)` | Delete keys matching pattern |
972
+ | `reset()` | Clear all cache |
973
+ | `has(key)` | Check if key exists |
974
+
975
+ ---
976
+
977
+ ## Multi-Tenant DataSource
978
+
979
+ Dynamic database connection management for multi-tenant applications.
980
+
981
+ ### Setup
982
+
983
+ ```typescript
984
+ import { DataSourceModule } from '@flusys/nestjs-shared/modules';
985
+
986
+ @Module({
987
+ imports: [
988
+ DataSourceModule.forRoot({
989
+ // Default database config (used as template)
990
+ defaultDatabaseConfig: {
991
+ type: 'mysql',
992
+ host: 'localhost',
993
+ port: 3306,
994
+ username: 'root',
995
+ password: 'password',
996
+ },
997
+
998
+ // Tenant configurations
999
+ tenants: [
1000
+ { id: 'tenant1', database: 'tenant1_db', isActive: true },
1001
+ { id: 'tenant2', database: 'tenant2_db', isActive: true },
1002
+ ],
1003
+ }),
1004
+ ],
1005
+ })
1006
+ export class AppModule {}
1007
+ ```
1008
+
1009
+ ### Usage with Tenant Header
1010
+
1011
+ ```typescript
1012
+ // Client sends X-Tenant-ID header
1013
+ // DataSource automatically switches to tenant's database
1014
+
1015
+ @Injectable()
1016
+ export class TenantService {
1017
+ constructor(
1018
+ @Inject('DATASOURCE_PROVIDER')
1019
+ private dataSourceProvider: MultiTenantDataSourceService,
1020
+ ) {}
1021
+
1022
+ async getConnection(tenantId: string) {
1023
+ return this.dataSourceProvider.getConnection(tenantId);
1024
+ }
1025
+ }
1026
+ ```
1027
+
1028
+ ### Tenant Resolution
1029
+
1030
+ ```typescript
1031
+ // Default: Resolved from X-Tenant-ID header
1032
+ @Controller('users')
1033
+ export class UserController {
1034
+ // Automatically uses tenant-specific database
1035
+ }
1036
+
1037
+ // Manual tenant specification
1038
+ @Injectable()
1039
+ export class CrossTenantService {
1040
+ async copyData(fromTenant: string, toTenant: string) {
1041
+ const fromConn = await this.dataSourceProvider.getConnection(fromTenant);
1042
+ const toConn = await this.dataSourceProvider.getConnection(toTenant);
1043
+ // ...
1044
+ }
1045
+ }
1046
+ ```
1047
+
1048
+ ---
1049
+
1050
+ ## DTOs
1051
+
1052
+ ### FilterAndPaginationDto
1053
+
1054
+ Standard filtering and pagination:
1055
+
1056
+ ```typescript
1057
+ import { FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
1058
+
1059
+ // Request body
1060
+ {
1061
+ "filter": {
1062
+ "status": "active",
1063
+ "category": "electronics"
1064
+ },
1065
+ "pagination": {
1066
+ "page": 1,
1067
+ "limit": 10
1068
+ },
1069
+ "sort": {
1070
+ "field": "createdAt",
1071
+ "order": "DESC"
1072
+ },
1073
+ "search": {
1074
+ "fields": ["name", "description"],
1075
+ "value": "laptop"
1076
+ },
1077
+ "select": ["id", "name", "price"]
1078
+ }
1079
+ ```
1080
+
1081
+ ### DeleteDto
1082
+
1083
+ Soft or permanent delete:
1084
+
1085
+ ```typescript
1086
+ import { DeleteDto } from '@flusys/nestjs-shared/dtos';
1087
+
1088
+ // Soft delete (sets deletedAt)
1089
+ {
1090
+ "id": "user-123",
1091
+ "type": "soft"
1092
+ }
1093
+
1094
+ // Permanent delete
1095
+ {
1096
+ "id": "user-123",
1097
+ "type": "permanent"
1098
+ }
1099
+
1100
+ // Multiple IDs
1101
+ {
1102
+ "id": ["user-123", "user-456"],
1103
+ "type": "soft"
1104
+ }
1105
+ ```
1106
+
1107
+ ### ResponsePayloadDto
1108
+
1109
+ Standardized response format:
1110
+
1111
+ ```typescript
1112
+ import { ResponsePayloadDto } from '@flusys/nestjs-shared/dtos';
1113
+
1114
+ // Success response
1115
+ {
1116
+ "success": true,
1117
+ "data": { ... },
1118
+ "message": "Operation successful"
1119
+ }
1120
+
1121
+ // Paginated response
1122
+ {
1123
+ "success": true,
1124
+ "data": [...],
1125
+ "pagination": {
1126
+ "total": 100,
1127
+ "page": 1,
1128
+ "limit": 10,
1129
+ "totalPages": 10
1130
+ }
1131
+ }
1132
+
1133
+ // Error response
1134
+ {
1135
+ "success": false,
1136
+ "error": {
1137
+ "code": "VALIDATION_ERROR",
1138
+ "message": "Invalid input"
1139
+ }
1140
+ }
1141
+ ```
1142
+
1143
+ ---
1144
+
1145
+ ## Base Entities
1146
+
1147
+ ### Identity Entity
1148
+
1149
+ Base entity with UUID and timestamps:
1150
+
1151
+ ```typescript
1152
+ import { Identity } from '@flusys/nestjs-shared/entities';
1153
+
1154
+ @Entity()
1155
+ export class Product extends Identity {
1156
+ // Inherited: id, createdAt, updatedAt, deletedAt
1157
+
1158
+ @Column()
1159
+ name: string;
1160
+
1161
+ @Column()
1162
+ price: number;
1163
+ }
1164
+ ```
1165
+
1166
+ ### UserRoot Entity
1167
+
1168
+ Base user entity:
1169
+
1170
+ ```typescript
1171
+ import { UserRoot } from '@flusys/nestjs-shared/entities';
1172
+
1173
+ @Entity()
1174
+ export class User extends UserRoot {
1175
+ // Inherited: id, name, email, password, phone, isActive, createdAt, updatedAt, deletedAt
1176
+
1177
+ @Column({ nullable: true })
1178
+ customField: string;
1179
+ }
1180
+ ```
1181
+
1182
+ ---
1183
+
1184
+ ## Error Handling
1185
+
1186
+ ### ErrorHandler
1187
+
1188
+ Centralized error handling utility:
1189
+
1190
+ ```typescript
1191
+ import { ErrorHandler } from '@flusys/nestjs-shared/utils';
1192
+
1193
+ @Injectable()
1194
+ export class UserService {
1195
+ async createUser(dto: CreateUserDto) {
1196
+ try {
1197
+ return await this.repository.save(dto);
1198
+ } catch (error) {
1199
+ // Handles TypeORM errors, validation errors, etc.
1200
+ throw ErrorHandler.handle(error, 'Failed to create user');
1201
+ }
1202
+ }
1203
+ }
1204
+ ```
1205
+
1206
+ ### Error Types
1207
+
1208
+ ```typescript
1209
+ // Automatically converted to appropriate HTTP exception:
1210
+
1211
+ // TypeORM unique constraint → ConflictException (409)
1212
+ // TypeORM foreign key → BadRequestException (400)
1213
+ // Validation error → BadRequestException (400)
1214
+ // Not found → NotFoundException (404)
1215
+ // Unknown → InternalServerErrorException (500)
1216
+ ```
1217
+
1218
+ ---
1219
+
1220
+ ## API Reference
1221
+
1222
+ ### Main Exports
1223
+
1224
+ ```typescript
1225
+ // Classes
1226
+ import {
1227
+ ApiService,
1228
+ createApiController,
1229
+ HybridCache,
1230
+ } from '@flusys/nestjs-shared/classes';
1231
+
1232
+ // Decorators
1233
+ import {
1234
+ CurrentUser,
1235
+ Public,
1236
+ RequirePermission,
1237
+ RequireAnyPermission,
1238
+ RequireAllPermissions,
1239
+ } from '@flusys/nestjs-shared/decorators';
1240
+
1241
+ // Guards
1242
+ import {
1243
+ PermissionGuard,
1244
+ } from '@flusys/nestjs-shared/guards';
1245
+
1246
+ // Interceptors
1247
+ import {
1248
+ ResponseMetaInterceptor,
1249
+ IdempotencyInterceptor,
1250
+ SetCreatedByOnBody,
1251
+ SetUpdateByOnBody,
1252
+ Slug,
1253
+ } from '@flusys/nestjs-shared/interceptors';
1254
+
1255
+ // Modules
1256
+ import {
1257
+ CacheModule,
1258
+ DataSourceModule,
1259
+ UtilsModule,
1260
+ } from '@flusys/nestjs-shared/modules';
1261
+
1262
+ // DTOs
1263
+ import {
1264
+ FilterAndPaginationDto,
1265
+ DeleteDto,
1266
+ ResponsePayloadDto,
1267
+ } from '@flusys/nestjs-shared/dtos';
1268
+
1269
+ // Entities
1270
+ import {
1271
+ Identity,
1272
+ UserRoot,
1273
+ } from '@flusys/nestjs-shared/entities';
1274
+
1275
+ // Interfaces
1276
+ import {
1277
+ ILoggedUserInfo,
1278
+ IPermissionConfig,
1279
+ } from '@flusys/nestjs-shared/interfaces';
1280
+
1281
+ // Utilities
1282
+ import {
1283
+ ErrorHandler,
1284
+ } from '@flusys/nestjs-shared/utils';
1285
+ ```
1286
+
1287
+ ---
1288
+
1289
+ ## Best Practices
1290
+
1291
+ ### 1. Use Generic Service Pattern
1292
+
1293
+ ```typescript
1294
+ // ✅ Extend ApiService for consistent CRUD
1295
+ @Injectable()
1296
+ export class ProductService extends ApiService<...> {
1297
+ // Override hooks for customization
1298
+ }
1299
+
1300
+ // ❌ Don't create custom CRUD from scratch
1301
+ @Injectable()
1302
+ export class ProductService {
1303
+ async getAll() { /* custom implementation */ }
1304
+ async getById() { /* custom implementation */ }
1305
+ // Inconsistent, error-prone
1306
+ }
1307
+ ```
1308
+
1309
+ ### 2. Use Decorators Consistently
1310
+
1311
+ ```typescript
1312
+ // ✅ Use built-in decorators
1313
+ @CurrentUser() user: ILoggedUserInfo
1314
+ @Public()
1315
+ @RequirePermission('users.create')
1316
+
1317
+ // ❌ Don't access request directly
1318
+ @Req() req: Request
1319
+ const user = req.user; // Not type-safe
1320
+ ```
1321
+
1322
+ ### 3. Configure Security at Controller Level
1323
+
1324
+ ```typescript
1325
+ // ✅ Configure security in createApiController
1326
+ export class UserController extends createApiController(..., {
1327
+ security: { ... },
1328
+ }) {}
1329
+
1330
+ // ❌ Don't add @UseGuards to each endpoint
1331
+ @UseGuards(JwtGuard)
1332
+ @Post('create')
1333
+ create() {}
1334
+ ```
1335
+
1336
+ ### 4. Use Cache for Repeated Queries
1337
+
1338
+ ```typescript
1339
+ // ✅ Cache expensive operations
1340
+ async getPermissions(userId: string) {
1341
+ const cacheKey = `permissions:${userId}`;
1342
+ const cached = await this.cache.get(cacheKey);
1343
+ if (cached) return cached;
1344
+
1345
+ const permissions = await this.fetchPermissions(userId);
1346
+ await this.cache.set(cacheKey, permissions, 3600);
1347
+ return permissions;
1348
+ }
1349
+ ```
1350
+
1351
+ ---
1352
+
1353
+ ## Dependencies
1354
+
1355
+ - `@nestjs/common`
1356
+ - `@nestjs/core`
1357
+ - `@nestjs/typeorm`
1358
+ - `@nestjs/swagger`
1359
+ - `typeorm`
1360
+ - `class-validator`
1361
+ - `class-transformer`
1362
+ - `@flusys/nestjs-core`
1363
+
1364
+ ---
1365
+
1366
+ ## Related Documentation
1367
+
1368
+ - [API Controller Security Guide](./API-CONTROLLER-SECURITY.md)
1369
+ - [Core Package Guide](./CORE-GUIDE.md)
1370
+ - [Auth Package Guide](./AUTH-GUIDE.md)
1371
+
1372
+ ---
1373
+
1374
+ ## Summary
1375
+
1376
+ The `@flusys/nestjs-shared` package provides:
1377
+
1378
+ ✅ **Generic CRUD** - Consistent API patterns
1379
+ ✅ **Permission System** - Flexible access control
1380
+ ✅ **Caching** - High-performance data access
1381
+ ✅ **Multi-Tenancy** - Database isolation
1382
+ ✅ **Request Correlation** - AsyncLocalStorage-based tracking (NEW)
1383
+ ✅ **Middleware** - Logging and performance monitoring (NEW)
1384
+ ✅ **Interceptors** - Request/response processing
1385
+ ✅ **Error Handling** - Centralized error management
1386
+
1387
+ This is the shared infrastructure layer used by all other Flusys packages.
1388
+
1389
+ ---
1390
+
1391
+ **Last Updated:** 2026-02-07
1392
+ **Recent Improvements:**
1393
+ - 2026-02-07: Documentation cleanup - removed outdated JWT configuration section, updated package architecture to match actual structure
1394
+ - 2026-01-14: Enhanced `LoggerMiddleware` with complete request/response details (URL, path, query, headers, user context)
1395
+ - 2026-01-14: Added multiple response hooks (send, json, end) for reliable response capture
1396
+ - 2026-01-14: Improved error logging with stack traces
1397
+ - 2026-01-12: Added `LoggerMiddleware` for request correlation and structured logging
1398
+ - 2026-01-12: Added AsyncLocalStorage-based request context (requestId, tenantId, userId, companyId)
1399
+ - 2026-01-13: Removed JWT config/constants (moved to auth-specific packages)