@malamute/ai-rules 1.0.0 → 1.3.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 (145) hide show
  1. package/README.md +272 -121
  2. package/bin/cli.js +5 -2
  3. package/configs/_shared/CLAUDE.md +52 -149
  4. package/configs/_shared/rules/conventions/documentation.md +324 -0
  5. package/configs/_shared/rules/conventions/git.md +265 -0
  6. package/configs/_shared/rules/conventions/npm.md +80 -0
  7. package/configs/_shared/{.claude/rules → rules/conventions}/performance.md +1 -1
  8. package/configs/_shared/rules/conventions/principles.md +334 -0
  9. package/configs/_shared/rules/devops/ci-cd.md +262 -0
  10. package/configs/_shared/rules/devops/docker.md +275 -0
  11. package/configs/_shared/rules/devops/nx.md +194 -0
  12. package/configs/_shared/rules/domain/backend/api-design.md +203 -0
  13. package/configs/_shared/rules/lang/csharp/async.md +220 -0
  14. package/configs/_shared/rules/lang/csharp/csharp.md +314 -0
  15. package/configs/_shared/rules/lang/csharp/linq.md +210 -0
  16. package/configs/_shared/rules/lang/python/async.md +337 -0
  17. package/configs/_shared/rules/lang/python/celery.md +476 -0
  18. package/configs/_shared/rules/lang/python/config.md +339 -0
  19. package/configs/{python/.claude/rules → _shared/rules/lang/python}/database/sqlalchemy.md +6 -1
  20. package/configs/_shared/rules/lang/python/deployment.md +523 -0
  21. package/configs/_shared/rules/lang/python/error-handling.md +330 -0
  22. package/configs/_shared/rules/lang/python/migrations.md +421 -0
  23. package/configs/_shared/rules/lang/python/python.md +172 -0
  24. package/configs/_shared/rules/lang/python/repository.md +383 -0
  25. package/configs/{python/.claude/rules → _shared/rules/lang/python}/testing.md +2 -69
  26. package/configs/_shared/rules/lang/typescript/async.md +447 -0
  27. package/configs/_shared/rules/lang/typescript/generics.md +356 -0
  28. package/configs/_shared/rules/lang/typescript/typescript.md +212 -0
  29. package/configs/_shared/rules/quality/error-handling.md +48 -0
  30. package/configs/_shared/rules/quality/logging.md +45 -0
  31. package/configs/_shared/rules/quality/observability.md +240 -0
  32. package/configs/_shared/rules/quality/testing-patterns.md +65 -0
  33. package/configs/_shared/rules/security/secrets-management.md +222 -0
  34. package/configs/_shared/skills/analysis/explore/SKILL.md +257 -0
  35. package/configs/_shared/skills/analysis/security-audit/SKILL.md +184 -0
  36. package/configs/_shared/skills/dev/api-endpoint/SKILL.md +126 -0
  37. package/configs/_shared/{.claude/commands/generate-tests.md → skills/dev/generate-tests/SKILL.md} +6 -0
  38. package/configs/_shared/{.claude/commands/fix-issue.md → skills/git/fix-issue/SKILL.md} +6 -0
  39. package/configs/_shared/{.claude/commands/review-pr.md → skills/git/review-pr/SKILL.md} +6 -0
  40. package/configs/_shared/skills/infra/deploy/SKILL.md +139 -0
  41. package/configs/_shared/skills/infra/docker/SKILL.md +95 -0
  42. package/configs/_shared/skills/infra/migration/SKILL.md +158 -0
  43. package/configs/_shared/skills/nx/nx-affected/SKILL.md +72 -0
  44. package/configs/_shared/skills/nx/nx-lib/SKILL.md +375 -0
  45. package/configs/angular/CLAUDE.md +24 -216
  46. package/configs/angular/{.claude/rules → rules/core}/components.md +69 -15
  47. package/configs/angular/rules/core/resource.md +285 -0
  48. package/configs/angular/rules/core/signals.md +323 -0
  49. package/configs/angular/rules/http.md +338 -0
  50. package/configs/angular/rules/routing.md +291 -0
  51. package/configs/angular/rules/ssr.md +312 -0
  52. package/configs/angular/rules/state/signal-store.md +408 -0
  53. package/configs/angular/{.claude/rules → rules/state}/state.md +2 -2
  54. package/configs/angular/{.claude/rules → rules}/testing.md +7 -7
  55. package/configs/angular/rules/ui/aria.md +422 -0
  56. package/configs/angular/rules/ui/forms.md +424 -0
  57. package/configs/angular/rules/ui/pipes-directives.md +335 -0
  58. package/configs/angular/{.claude/settings.json → settings.json} +3 -0
  59. package/configs/dotnet/CLAUDE.md +53 -286
  60. package/configs/dotnet/rules/background-services.md +552 -0
  61. package/configs/dotnet/rules/configuration.md +426 -0
  62. package/configs/dotnet/rules/ddd.md +447 -0
  63. package/configs/dotnet/rules/dependency-injection.md +343 -0
  64. package/configs/dotnet/rules/mediatr.md +320 -0
  65. package/configs/dotnet/rules/middleware.md +489 -0
  66. package/configs/dotnet/rules/result-pattern.md +363 -0
  67. package/configs/dotnet/rules/validation.md +388 -0
  68. package/configs/dotnet/settings.json +29 -0
  69. package/configs/fastapi/CLAUDE.md +144 -0
  70. package/configs/fastapi/rules/background-tasks.md +254 -0
  71. package/configs/fastapi/rules/dependencies.md +170 -0
  72. package/configs/{python/.claude → fastapi}/rules/fastapi.md +61 -1
  73. package/configs/fastapi/rules/lifespan.md +274 -0
  74. package/configs/fastapi/rules/middleware.md +229 -0
  75. package/configs/fastapi/rules/pydantic.md +433 -0
  76. package/configs/fastapi/rules/responses.md +251 -0
  77. package/configs/fastapi/rules/routers.md +202 -0
  78. package/configs/fastapi/rules/security.md +222 -0
  79. package/configs/fastapi/rules/testing.md +251 -0
  80. package/configs/fastapi/rules/websockets.md +298 -0
  81. package/configs/fastapi/settings.json +35 -0
  82. package/configs/flask/CLAUDE.md +166 -0
  83. package/configs/flask/rules/blueprints.md +208 -0
  84. package/configs/flask/rules/cli.md +285 -0
  85. package/configs/flask/rules/configuration.md +281 -0
  86. package/configs/flask/rules/context.md +238 -0
  87. package/configs/flask/rules/error-handlers.md +278 -0
  88. package/configs/flask/rules/extensions.md +278 -0
  89. package/configs/flask/rules/flask.md +171 -0
  90. package/configs/flask/rules/marshmallow.md +206 -0
  91. package/configs/flask/rules/security.md +267 -0
  92. package/configs/flask/rules/testing.md +284 -0
  93. package/configs/flask/settings.json +35 -0
  94. package/configs/nestjs/CLAUDE.md +57 -215
  95. package/configs/nestjs/rules/common-patterns.md +300 -0
  96. package/configs/nestjs/rules/filters.md +376 -0
  97. package/configs/nestjs/rules/interceptors.md +317 -0
  98. package/configs/nestjs/rules/middleware.md +321 -0
  99. package/configs/nestjs/{.claude/rules → rules}/modules.md +26 -0
  100. package/configs/nestjs/rules/pipes.md +351 -0
  101. package/configs/nestjs/rules/websockets.md +451 -0
  102. package/configs/nestjs/settings.json +31 -0
  103. package/configs/nextjs/CLAUDE.md +69 -331
  104. package/configs/nextjs/rules/api-routes.md +358 -0
  105. package/configs/nextjs/rules/authentication.md +355 -0
  106. package/configs/nextjs/{.claude/rules → rules}/components.md +52 -0
  107. package/configs/nextjs/rules/data-fetching.md +249 -0
  108. package/configs/nextjs/rules/database.md +400 -0
  109. package/configs/nextjs/rules/middleware.md +303 -0
  110. package/configs/nextjs/rules/routing.md +324 -0
  111. package/configs/nextjs/rules/seo.md +350 -0
  112. package/configs/nextjs/rules/server-actions.md +353 -0
  113. package/configs/nextjs/{.claude/rules → rules}/state/zustand.md +6 -6
  114. package/configs/nextjs/{.claude/settings.json → settings.json} +7 -0
  115. package/package.json +24 -9
  116. package/src/cli.js +218 -0
  117. package/src/config.js +63 -0
  118. package/src/index.js +4 -0
  119. package/src/installer.js +414 -0
  120. package/src/merge.js +109 -0
  121. package/src/tech-config.json +45 -0
  122. package/src/utils.js +88 -0
  123. package/configs/dotnet/.claude/settings.json +0 -9
  124. package/configs/nestjs/.claude/settings.json +0 -15
  125. package/configs/python/.claude/rules/flask.md +0 -332
  126. package/configs/python/.claude/settings.json +0 -18
  127. package/configs/python/CLAUDE.md +0 -273
  128. package/src/install.js +0 -315
  129. /package/configs/_shared/{.claude/rules → rules/domain/frontend}/accessibility.md +0 -0
  130. /package/configs/_shared/{.claude/rules → rules/security}/security.md +0 -0
  131. /package/configs/_shared/{.claude/skills → skills/dev}/debug/SKILL.md +0 -0
  132. /package/configs/_shared/{.claude/skills → skills/dev}/learning/SKILL.md +0 -0
  133. /package/configs/_shared/{.claude/skills → skills/dev}/spec/SKILL.md +0 -0
  134. /package/configs/_shared/{.claude/skills → skills/git}/review/SKILL.md +0 -0
  135. /package/configs/dotnet/{.claude/rules → rules}/api.md +0 -0
  136. /package/configs/dotnet/{.claude/rules → rules}/architecture.md +0 -0
  137. /package/configs/dotnet/{.claude/rules → rules}/database/efcore.md +0 -0
  138. /package/configs/dotnet/{.claude/rules → rules}/testing.md +0 -0
  139. /package/configs/nestjs/{.claude/rules → rules}/auth.md +0 -0
  140. /package/configs/nestjs/{.claude/rules → rules}/database/prisma.md +0 -0
  141. /package/configs/nestjs/{.claude/rules → rules}/database/typeorm.md +0 -0
  142. /package/configs/nestjs/{.claude/rules → rules}/testing.md +0 -0
  143. /package/configs/nestjs/{.claude/rules → rules}/validation.md +0 -0
  144. /package/configs/nextjs/{.claude/rules → rules}/state/redux-toolkit.md +0 -0
  145. /package/configs/nextjs/{.claude/rules → rules}/testing.md +0 -0
@@ -4,260 +4,102 @@
4
4
 
5
5
  ## Stack
6
6
 
7
- - NestJS 10+ (latest stable)
7
+ - NestJS 11+
8
8
  - TypeScript strict mode
9
9
  - Node.js 20+
10
- - Jest for testing
11
- - Supertest for E2E
10
+ - Vitest + Supertest
11
+ - Prisma or TypeORM
12
12
 
13
- ## Architecture
14
-
15
- ### Modular Monolith (Recommended)
13
+ ## Architecture - Modular Monolith
16
14
 
17
15
  ```
18
16
  src/
19
17
  ├── modules/
20
- │ ├── users/
21
- │ │ ├── users.module.ts
22
- │ │ ├── users.controller.ts
23
- │ │ ├── users.service.ts
18
+ │ ├── [feature]/
19
+ │ │ ├── [feature].module.ts
20
+ │ │ ├── [feature].controller.ts
21
+ │ │ ├── [feature].service.ts
22
+ │ │ ├── [feature].repository.ts
24
23
  │ │ ├── dto/
25
- │ │ │ ├── create-user.dto.ts
26
- │ │ └── update-user.dto.ts
27
- ├── entities/
28
- │ │ └── user.entity.ts
29
- │ │ └── users.repository.ts
30
- │ │
31
- │ ├── auth/
32
- │ │ ├── auth.module.ts
33
- │ │ ├── auth.controller.ts
34
- │ │ ├── auth.service.ts
35
- │ │ ├── strategies/
36
- │ │ │ ├── jwt.strategy.ts
37
- │ │ │ └── local.strategy.ts
38
- │ │ └── guards/
39
- │ │ └── jwt-auth.guard.ts
40
- │ │
41
- │ └── [feature]/
42
- │ └── ...
43
-
24
+ │ │ └── entities/
25
+ │ └── auth/
26
+ ├── strategies/
27
+ └── guards/
44
28
  ├── common/
45
29
  │ ├── decorators/
46
30
  │ ├── filters/
47
31
  │ ├── guards/
48
32
  │ ├── interceptors/
49
33
  │ └── pipes/
50
-
51
34
  ├── config/
52
- │ ├── config.module.ts
53
- │ └── database.config.ts
54
-
55
35
  ├── app.module.ts
56
36
  └── main.ts
57
37
  ```
58
38
 
59
- ### Module Principles
60
-
61
- - **Single Responsibility**: Each module owns one domain
62
- - **Clear Boundaries**: Modules communicate via exported services
63
- - **In-Memory Communication**: Direct method calls, not HTTP between modules
64
- - **Barrel Exports**: Use `index.ts` for clean imports
39
+ ## Request Lifecycle
65
40
 
66
- ## Code Style
41
+ ```
42
+ Request → Middleware → Guard → Interceptor (pre) → Pipe → Controller → Interceptor (post) → Response
43
+ ```
67
44
 
68
- ### Controllers
45
+ ## Core Principles
69
46
 
70
- - Handle HTTP concerns only (request/response)
71
- - Delegate business logic to services
72
- - Use DTOs for all inputs
73
- - Return consistent response shapes
47
+ ### Module Design
74
48
 
75
- ```typescript
76
- @Controller('users')
77
- export class UsersController {
78
- constructor(private readonly usersService: UsersService) {}
49
+ - **Single Responsibility**: One module = one domain
50
+ - **Clear Boundaries**: Communicate via exported services only
51
+ - **Barrel Exports**: Use `index.ts` for clean imports
79
52
 
80
- @Post()
81
- @HttpCode(HttpStatus.CREATED)
82
- create(@Body() createUserDto: CreateUserDto): Promise<User> {
83
- return this.usersService.create(createUserDto);
84
- }
53
+ ### Layer Responsibilities
85
54
 
86
- @Get(':id')
87
- findOne(@Param('id', ParseUUIDPipe) id: string): Promise<User> {
88
- return this.usersService.findOne(id);
89
- }
90
- }
91
- ```
55
+ | Layer | Responsibility |
56
+ |-------|---------------|
57
+ | Controller | HTTP only, delegates to service |
58
+ | Service | All business logic |
59
+ | Repository | Data access only |
60
+ | DTO | Validation with class-validator |
92
61
 
93
- ### Services
62
+ ### Exception Handling
94
63
 
95
- - Contain all business logic
96
- - Inject repositories/other services
97
- - Throw appropriate NestJS exceptions
64
+ Use built-in NestJS exceptions:
65
+ - `NotFoundException` - 404
66
+ - `BadRequestException` - 400
67
+ - `UnauthorizedException` - 401
68
+ - `ForbiddenException` - 403
69
+ - `ConflictException` - 409
98
70
 
99
- ```typescript
100
- @Injectable()
101
- export class UsersService {
102
- constructor(private readonly usersRepository: UsersRepository) {}
71
+ ### Validation (Global)
103
72
 
104
- async findOne(id: string): Promise<User> {
105
- const user = await this.usersRepository.findById(id);
106
- if (!user) {
107
- throw new NotFoundException(`User with ID ${id} not found`);
108
- }
109
- return user;
110
- }
111
- }
112
- ```
73
+ Enable global `ValidationPipe` in main.ts with:
74
+ - `whitelist: true` - Strip non-whitelisted properties
75
+ - `forbidNonWhitelisted: true` - Throw on extra properties
76
+ - `transform: true` - Auto-transform to DTO types
113
77
 
114
78
  ### DTOs
115
79
 
116
80
  - Always use class-validator decorators
117
- - Use class-transformer for transformation
118
- - Extend with PartialType/PickType/OmitType for variants
81
+ - `PartialType`, `PickType`, `OmitType` for variants
82
+ - Combine with Swagger decorators
119
83
 
120
- ```typescript
121
- export class CreateUserDto {
122
- @IsEmail()
123
- @IsNotEmpty()
124
- email: string;
84
+ ## Authentication
125
85
 
126
- @IsString()
127
- @MinLength(8)
128
- password: string;
129
-
130
- @IsString()
131
- @IsOptional()
132
- name?: string;
133
- }
134
-
135
- export class UpdateUserDto extends PartialType(CreateUserDto) {}
136
- ```
86
+ - Passport.js + JWT strategy
87
+ - Global `JwtAuthGuard` with `@Public()` decorator for exceptions
88
+ - `@CurrentUser()` decorator for accessing user
137
89
 
138
90
  ## Commands
139
91
 
140
92
  ```bash
141
- # Development
142
- npm run start:dev
143
-
144
- # Build
145
- npm run build
146
-
147
- # Tests
148
- npm run test # Unit tests
149
- npm run test:watch # Watch mode
150
- npm run test:cov # Coverage
151
- npm run test:e2e # E2E tests
152
-
153
- # Linting
154
- npm run lint
155
- npm run format
156
- ```
157
-
158
- ## Global Setup (main.ts)
159
-
160
- ```typescript
161
- async function bootstrap() {
162
- const app = await NestFactory.create(AppModule);
163
-
164
- // Global validation pipe
165
- app.useGlobalPipes(
166
- new ValidationPipe({
167
- whitelist: true,
168
- forbidNonWhitelisted: true,
169
- transform: true,
170
- transformOptions: {
171
- enableImplicitConversion: true,
172
- },
173
- }),
174
- );
175
-
176
- // Global prefix
177
- app.setGlobalPrefix('api/v1');
178
-
179
- // CORS
180
- app.enableCors();
181
-
182
- // Swagger (dev only)
183
- if (process.env.NODE_ENV !== 'production') {
184
- const config = new DocumentBuilder()
185
- .setTitle('API')
186
- .setVersion('1.0')
187
- .addBearerAuth()
188
- .build();
189
- const document = SwaggerModule.createDocument(app, config);
190
- SwaggerModule.setup('docs', app, document);
191
- }
192
-
193
- await app.listen(process.env.PORT ?? 3000);
194
- }
195
- ```
196
-
197
- ## Common Patterns
198
-
199
- ### Custom Decorator
200
-
201
- ```typescript
202
- // current-user.decorator.ts
203
- export const CurrentUser = createParamDecorator(
204
- (data: keyof User | undefined, ctx: ExecutionContext) => {
205
- const request = ctx.switchToHttp().getRequest();
206
- const user = request.user;
207
- return data ? user?.[data] : user;
208
- },
209
- );
210
-
211
- // Usage
212
- @Get('profile')
213
- getProfile(@CurrentUser() user: User) { ... }
93
+ npm run start:dev # Dev with watch
94
+ npm run build # Production build
95
+ npm run test # Unit tests
96
+ npm run test:e2e # E2E tests
97
+ npm run test:cov # Coverage
214
98
  ```
215
99
 
216
- ### Global Exception Filter
217
-
218
- ```typescript
219
- @Catch()
220
- export class AllExceptionsFilter implements ExceptionFilter {
221
- catch(exception: unknown, host: ArgumentsHost) {
222
- const ctx = host.switchToHttp();
223
- const response = ctx.getResponse<Response>();
224
-
225
- const status =
226
- exception instanceof HttpException
227
- ? exception.getStatus()
228
- : HttpStatus.INTERNAL_SERVER_ERROR;
229
-
230
- const message =
231
- exception instanceof HttpException
232
- ? exception.message
233
- : 'Internal server error';
234
-
235
- response.status(status).json({
236
- statusCode: status,
237
- message,
238
- timestamp: new Date().toISOString(),
239
- });
240
- }
241
- }
242
- ```
243
-
244
- ### Response Interceptor
100
+ ## Code Style
245
101
 
246
- ```typescript
247
- @Injectable()
248
- export class TransformInterceptor<T>
249
- implements NestInterceptor<T, Response<T>>
250
- {
251
- intercept(
252
- context: ExecutionContext,
253
- next: CallHandler,
254
- ): Observable<Response<T>> {
255
- return next.handle().pipe(
256
- map((data) => ({
257
- success: true,
258
- data,
259
- })),
260
- );
261
- }
262
- }
263
- ```
102
+ - Constructor injection (not property injection)
103
+ - `readonly` for injected dependencies
104
+ - Async methods return `Promise<T>`
105
+ - Use `ParseUUIDPipe`, `ParseIntPipe` for params
@@ -0,0 +1,300 @@
1
+ ---
2
+ paths:
3
+ - "src/common/**/*.ts"
4
+ - "src/**/*.decorator.ts"
5
+ - "src/**/*.filter.ts"
6
+ - "src/**/*.interceptor.ts"
7
+ - "src/**/*.pipe.ts"
8
+ - "src/main.ts"
9
+ ---
10
+
11
+ # NestJS Common Patterns
12
+
13
+ ## Global Setup (main.ts)
14
+
15
+ ```typescript
16
+ import { NestFactory } from '@nestjs/core';
17
+ import { ValidationPipe } from '@nestjs/common';
18
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
19
+ import { AppModule } from './app.module';
20
+
21
+ async function bootstrap() {
22
+ const app = await NestFactory.create(AppModule);
23
+
24
+ // Global validation pipe
25
+ app.useGlobalPipes(
26
+ new ValidationPipe({
27
+ whitelist: true,
28
+ forbidNonWhitelisted: true,
29
+ transform: true,
30
+ transformOptions: {
31
+ enableImplicitConversion: true,
32
+ },
33
+ }),
34
+ );
35
+
36
+ // Global prefix
37
+ app.setGlobalPrefix('api/v1');
38
+
39
+ // CORS
40
+ app.enableCors({
41
+ origin: process.env.CORS_ORIGIN?.split(',') || '*',
42
+ credentials: true,
43
+ });
44
+
45
+ // Swagger (dev only)
46
+ if (process.env.NODE_ENV !== 'production') {
47
+ const config = new DocumentBuilder()
48
+ .setTitle('API')
49
+ .setVersion('1.0')
50
+ .addBearerAuth()
51
+ .build();
52
+ const document = SwaggerModule.createDocument(app, config);
53
+ SwaggerModule.setup('docs', app, document);
54
+ }
55
+
56
+ await app.listen(process.env.PORT ?? 3000);
57
+ }
58
+ bootstrap();
59
+ ```
60
+
61
+ ## Custom Decorators
62
+
63
+ ### @CurrentUser Decorator
64
+
65
+ ```typescript
66
+ // common/decorators/current-user.decorator.ts
67
+ import { createParamDecorator, ExecutionContext } from '@nestjs/common';
68
+
69
+ export const CurrentUser = createParamDecorator(
70
+ (data: string | undefined, ctx: ExecutionContext) => {
71
+ const request = ctx.switchToHttp().getRequest();
72
+ const user = request.user;
73
+ return data ? user?.[data] : user;
74
+ },
75
+ );
76
+
77
+ // Usage
78
+ @Get('profile')
79
+ getProfile(@CurrentUser() user: User) {
80
+ return user;
81
+ }
82
+
83
+ @Get('email')
84
+ getEmail(@CurrentUser('email') email: string) {
85
+ return { email };
86
+ }
87
+ ```
88
+
89
+ ### @Public Decorator
90
+
91
+ ```typescript
92
+ // common/decorators/public.decorator.ts
93
+ import { SetMetadata } from '@nestjs/common';
94
+
95
+ export const IS_PUBLIC_KEY = 'isPublic';
96
+ export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
97
+
98
+ // Usage
99
+ @Public()
100
+ @Get('health')
101
+ healthCheck() {
102
+ return { status: 'ok' };
103
+ }
104
+ ```
105
+
106
+ ### @Roles Decorator
107
+
108
+ ```typescript
109
+ // common/decorators/roles.decorator.ts
110
+ import { SetMetadata } from '@nestjs/common';
111
+
112
+ export const ROLES_KEY = 'roles';
113
+ export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
114
+
115
+ // Usage
116
+ @Roles('admin')
117
+ @Get('admin')
118
+ adminOnly() { ... }
119
+ ```
120
+
121
+ ## Exception Filters
122
+
123
+ ### Global Exception Filter
124
+
125
+ ```typescript
126
+ // common/filters/all-exceptions.filter.ts
127
+ import {
128
+ ExceptionFilter,
129
+ Catch,
130
+ ArgumentsHost,
131
+ HttpException,
132
+ HttpStatus,
133
+ Logger,
134
+ } from '@nestjs/common';
135
+ import { Request, Response } from 'express';
136
+
137
+ @Catch()
138
+ export class AllExceptionsFilter implements ExceptionFilter {
139
+ private readonly logger = new Logger(AllExceptionsFilter.name);
140
+
141
+ catch(exception: unknown, host: ArgumentsHost) {
142
+ const ctx = host.switchToHttp();
143
+ const response = ctx.getResponse<Response>();
144
+ const request = ctx.getRequest<Request>();
145
+
146
+ const status =
147
+ exception instanceof HttpException
148
+ ? exception.getStatus()
149
+ : HttpStatus.INTERNAL_SERVER_ERROR;
150
+
151
+ const message =
152
+ exception instanceof HttpException
153
+ ? exception.message
154
+ : 'Internal server error';
155
+
156
+ // Log error
157
+ this.logger.error(
158
+ `${request.method} ${request.url} - ${status} - ${message}`,
159
+ exception instanceof Error ? exception.stack : undefined,
160
+ );
161
+
162
+ response.status(status).json({
163
+ statusCode: status,
164
+ message,
165
+ timestamp: new Date().toISOString(),
166
+ path: request.url,
167
+ });
168
+ }
169
+ }
170
+
171
+ // Register globally in main.ts
172
+ app.useGlobalFilters(new AllExceptionsFilter());
173
+ ```
174
+
175
+ ## Interceptors
176
+
177
+ ### Transform Response Interceptor
178
+
179
+ ```typescript
180
+ // common/interceptors/transform.interceptor.ts
181
+ import {
182
+ Injectable,
183
+ NestInterceptor,
184
+ ExecutionContext,
185
+ CallHandler,
186
+ } from '@nestjs/common';
187
+ import { Observable } from 'rxjs';
188
+ import { map } from 'rxjs/operators';
189
+
190
+ export interface Response<T> {
191
+ success: boolean;
192
+ data: T;
193
+ }
194
+
195
+ @Injectable()
196
+ export class TransformInterceptor<T>
197
+ implements NestInterceptor<T, Response<T>>
198
+ {
199
+ intercept(
200
+ context: ExecutionContext,
201
+ next: CallHandler,
202
+ ): Observable<Response<T>> {
203
+ return next.handle().pipe(
204
+ map((data) => ({
205
+ success: true,
206
+ data,
207
+ })),
208
+ );
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### Logging Interceptor
214
+
215
+ ```typescript
216
+ // common/interceptors/logging.interceptor.ts
217
+ import {
218
+ Injectable,
219
+ NestInterceptor,
220
+ ExecutionContext,
221
+ CallHandler,
222
+ Logger,
223
+ } from '@nestjs/common';
224
+ import { Observable } from 'rxjs';
225
+ import { tap } from 'rxjs/operators';
226
+
227
+ @Injectable()
228
+ export class LoggingInterceptor implements NestInterceptor {
229
+ private readonly logger = new Logger(LoggingInterceptor.name);
230
+
231
+ intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
232
+ const request = context.switchToHttp().getRequest();
233
+ const { method, url } = request;
234
+ const now = Date.now();
235
+
236
+ return next.handle().pipe(
237
+ tap(() => {
238
+ this.logger.log(`${method} ${url} - ${Date.now() - now}ms`);
239
+ }),
240
+ );
241
+ }
242
+ }
243
+ ```
244
+
245
+ ## Guards
246
+
247
+ ### Roles Guard
248
+
249
+ ```typescript
250
+ // common/guards/roles.guard.ts
251
+ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
252
+ import { Reflector } from '@nestjs/core';
253
+ import { ROLES_KEY } from '../decorators/roles.decorator';
254
+
255
+ @Injectable()
256
+ export class RolesGuard implements CanActivate {
257
+ constructor(private reflector: Reflector) {}
258
+
259
+ canActivate(context: ExecutionContext): boolean {
260
+ const requiredRoles = this.reflector.getAllAndOverride<string[]>(
261
+ ROLES_KEY,
262
+ [context.getHandler(), context.getClass()],
263
+ );
264
+
265
+ if (!requiredRoles) {
266
+ return true;
267
+ }
268
+
269
+ const { user } = context.switchToHttp().getRequest();
270
+ return requiredRoles.some((role) => user.roles?.includes(role));
271
+ }
272
+ }
273
+ ```
274
+
275
+ ## Pipes
276
+
277
+ ### Parse Optional Int Pipe
278
+
279
+ ```typescript
280
+ // common/pipes/parse-optional-int.pipe.ts
281
+ import { PipeTransform, Injectable } from '@nestjs/common';
282
+
283
+ @Injectable()
284
+ export class ParseOptionalIntPipe implements PipeTransform {
285
+ transform(value: string | undefined): number | undefined {
286
+ if (value === undefined || value === '') {
287
+ return undefined;
288
+ }
289
+ const val = parseInt(value, 10);
290
+ return isNaN(val) ? undefined : val;
291
+ }
292
+ }
293
+
294
+ // Usage
295
+ @Get()
296
+ findAll(
297
+ @Query('page', ParseOptionalIntPipe) page?: number,
298
+ @Query('limit', ParseOptionalIntPipe) limit?: number,
299
+ ) { ... }
300
+ ```