@flusys/nestjs-shared 1.1.0-beta → 1.1.0

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