memory-journal-mcp 7.0.1 → 7.2.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 (124) hide show
  1. package/README.md +75 -66
  2. package/dist/{chunk-6J4RPJ4I.js → chunk-GR4T3SRW.js} +146 -105
  3. package/dist/{chunk-ARLH46WS.js → chunk-IWKLHSPU.js} +89 -3
  4. package/dist/{chunk-2BJHLTYP.js → chunk-ORV7ZZOE.js} +1086 -86
  5. package/dist/cli.js +30 -4
  6. package/dist/github-integration-2TFMXHIJ.js +1 -0
  7. package/dist/index.d.ts +6 -2
  8. package/dist/index.js +3 -3
  9. package/dist/{tools-FFFGXIKN.js → tools-CXR2FEB2.js} +2 -2
  10. package/package.json +2 -2
  11. package/skills/README.md +77 -0
  12. package/skills/autonomous-dev/SKILL.md +56 -0
  13. package/skills/bin/sync.js +50 -0
  14. package/skills/bun/SKILL.md +156 -0
  15. package/skills/github-commander/SKILL.md +1 -1
  16. package/skills/github-commander/workflows/code-quality-audit.md +7 -5
  17. package/skills/github-commander/workflows/issue-triage.md +13 -4
  18. package/skills/github-commander/workflows/milestone-sprint.md +9 -1
  19. package/skills/github-commander/workflows/perf-audit.md +2 -0
  20. package/skills/github-commander/workflows/pr-review.md +9 -3
  21. package/skills/github-commander/workflows/roadmap-kickoff.md +79 -0
  22. package/skills/github-commander/workflows/security-audit.md +3 -3
  23. package/skills/github-commander/workflows/update-deps.md +2 -2
  24. package/skills/gitlab/SKILL.md +115 -0
  25. package/skills/gitlab/package-lock.json +392 -0
  26. package/skills/gitlab/package.json +14 -0
  27. package/skills/gitlab/scripts/gitlab-client.ts +125 -0
  28. package/skills/gitlab/scripts/gitlab-helper.ts +80 -0
  29. package/skills/golang/SKILL.md +54 -0
  30. package/skills/mysql/SKILL.md +30 -0
  31. package/skills/package.json +48 -0
  32. package/skills/playwright-standard/SKILL.md +58 -0
  33. package/skills/playwright-standard/examples/fixtures.ts +66 -0
  34. package/skills/playwright-standard/examples/type-stubs.d.ts +10 -0
  35. package/skills/playwright-standard/references/advanced-scenarios.md +59 -0
  36. package/skills/playwright-standard/references/infrastructure.md +43 -0
  37. package/skills/postgres/SKILL.md +33 -0
  38. package/skills/react-best-practices/AGENTS.md +2883 -0
  39. package/skills/react-best-practices/README.md +127 -0
  40. package/skills/react-best-practices/SKILL.md +138 -0
  41. package/skills/react-best-practices/metadata.json +17 -0
  42. package/skills/react-best-practices/rules/_sections.md +46 -0
  43. package/skills/react-best-practices/rules/_template.md +28 -0
  44. package/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  45. package/skills/react-best-practices/rules/advanced-init-once.md +42 -0
  46. package/skills/react-best-practices/rules/advanced-use-latest.md +39 -0
  47. package/skills/react-best-practices/rules/async-api-routes.md +35 -0
  48. package/skills/react-best-practices/rules/async-defer-await.md +80 -0
  49. package/skills/react-best-practices/rules/async-dependencies.md +48 -0
  50. package/skills/react-best-practices/rules/async-parallel.md +24 -0
  51. package/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  52. package/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  53. package/skills/react-best-practices/rules/bundle-conditional.md +37 -0
  54. package/skills/react-best-practices/rules/bundle-defer-third-party.md +48 -0
  55. package/skills/react-best-practices/rules/bundle-dynamic-imports.md +34 -0
  56. package/skills/react-best-practices/rules/bundle-preload.md +44 -0
  57. package/skills/react-best-practices/rules/client-event-listeners.md +78 -0
  58. package/skills/react-best-practices/rules/client-localstorage-schema.md +74 -0
  59. package/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  60. package/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  61. package/skills/react-best-practices/rules/js-batch-dom-css.md +110 -0
  62. package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  63. package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  64. package/skills/react-best-practices/rules/js-cache-storage.md +68 -0
  65. package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  66. package/skills/react-best-practices/rules/js-early-exit.md +50 -0
  67. package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  68. package/skills/react-best-practices/rules/js-index-maps.md +37 -0
  69. package/skills/react-best-practices/rules/js-length-check-first.md +50 -0
  70. package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  71. package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  72. package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  73. package/skills/react-best-practices/rules/rendering-activity.md +24 -0
  74. package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +38 -0
  75. package/skills/react-best-practices/rules/rendering-conditional-render.md +32 -0
  76. package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  77. package/skills/react-best-practices/rules/rendering-hoist-jsx.md +36 -0
  78. package/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +72 -0
  79. package/skills/react-best-practices/rules/rendering-hydration-suppress-warning.md +26 -0
  80. package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  81. package/skills/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  82. package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  83. package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  84. package/skills/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  85. package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  86. package/skills/react-best-practices/rules/rerender-functional-setstate.md +77 -0
  87. package/skills/react-best-practices/rules/rerender-lazy-state-init.md +56 -0
  88. package/skills/react-best-practices/rules/rerender-memo-with-default-value.md +36 -0
  89. package/skills/react-best-practices/rules/rerender-memo.md +44 -0
  90. package/skills/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  91. package/skills/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  92. package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  93. package/skills/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  94. package/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  95. package/skills/react-best-practices/rules/server-auth-actions.md +96 -0
  96. package/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  97. package/skills/react-best-practices/rules/server-cache-react.md +76 -0
  98. package/skills/react-best-practices/rules/server-dedup-props.md +65 -0
  99. package/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
  100. package/skills/react-best-practices/rules/server-serialization.md +38 -0
  101. package/skills/rust/SKILL.md +86 -0
  102. package/skills/shadcn-ui/SKILL.md +72 -0
  103. package/skills/skill-builder/SKILL.md +457 -0
  104. package/skills/skill-builder/checklist.md +65 -0
  105. package/skills/sqlite/SKILL.md +38 -0
  106. package/skills/typescript/SKILL.md +453 -0
  107. package/skills/typescript/assets/eslint-template.js +102 -0
  108. package/skills/typescript/assets/tsconfig-template.json +45 -0
  109. package/skills/typescript/references/enterprise-patterns.md +531 -0
  110. package/skills/typescript/references/generics.md +493 -0
  111. package/skills/typescript/references/nestjs-integration.md +579 -0
  112. package/skills/typescript/references/react-integration.md +616 -0
  113. package/skills/typescript/references/toolchain.md +547 -0
  114. package/skills/typescript/references/type-system.md +481 -0
  115. package/skills/vitest-standard/SKILL.md +82 -0
  116. package/skills/vitest-standard/examples/service-mock.ts +60 -0
  117. package/skills/vitest-standard/examples/tdd-calculator.ts +41 -0
  118. package/skills/vitest-standard/examples/type-stubs.d.ts +18 -0
  119. package/skills/vitest-standard/references/async-and-errors.md +58 -0
  120. package/skills/vitest-standard/references/coverage-and-config.md +53 -0
  121. package/skills/vitest-standard/references/mocking.md +61 -0
  122. package/skills/vitest-standard/references/tdd-patterns.md +60 -0
  123. package/dist/github-integration-PDRLXKGM.js +0 -1
  124. package/skills/github-commander/workflows/full-audit.md +0 -134
@@ -0,0 +1,579 @@
1
+ # NestJS Integration Reference
2
+
3
+ > **Load when:** User asks about NestJS with TypeScript, API development, DTOs, validation, authentication, or backend patterns.
4
+
5
+ Type-safe API development with NestJS 11+.
6
+
7
+ ## Contents
8
+
9
+ - [Project Structure](#project-structure)
10
+ - [Controllers and Routes](#controllers-and-routes)
11
+ - [DTOs and Validation](#dtos-and-validation)
12
+ - [Services and Dependency Injection](#services-and-dependency-injection)
13
+ - [Authentication](#authentication)
14
+ - [Error Handling](#error-handling)
15
+
16
+ ---
17
+
18
+ ## Project Structure
19
+
20
+ ### Recommended Layout
21
+
22
+ ```
23
+ src/
24
+ ├── main.ts # Application entry point
25
+ ├── app.module.ts # Root module
26
+ ├── common/ # Shared utilities
27
+ │ ├── decorators/
28
+ │ ├── filters/
29
+ │ ├── guards/
30
+ │ ├── interceptors/
31
+ │ └── pipes/
32
+ ├── config/ # Configuration
33
+ │ ├── config.module.ts
34
+ │ └── env.validation.ts
35
+ └── modules/
36
+ ├── users/
37
+ │ ├── users.module.ts
38
+ │ ├── users.controller.ts
39
+ │ ├── users.service.ts
40
+ │ ├── users.repository.ts
41
+ │ ├── dto/
42
+ │ │ ├── create-user.dto.ts
43
+ │ │ └── update-user.dto.ts
44
+ │ ├── entities/
45
+ │ │ └── user.entity.ts
46
+ │ └── __tests__/
47
+ └── auth/
48
+ └── ...
49
+ ```
50
+
51
+ ### Module Configuration
52
+
53
+ ```typescript
54
+ // users.module.ts
55
+ import { Module } from '@nestjs/common'
56
+ import { UsersController } from './users.controller'
57
+ import { UsersService } from './users.service'
58
+ import { UsersRepository } from './users.repository'
59
+
60
+ @Module({
61
+ controllers: [UsersController],
62
+ providers: [UsersService, UsersRepository],
63
+ exports: [UsersService], // Export for use in other modules
64
+ })
65
+ export class UsersModule {}
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Controllers and Routes
71
+
72
+ ### Basic Controller
73
+
74
+ ```typescript
75
+ import {
76
+ Controller,
77
+ Get,
78
+ Post,
79
+ Put,
80
+ Delete,
81
+ Param,
82
+ Body,
83
+ Query,
84
+ HttpCode,
85
+ HttpStatus,
86
+ } from '@nestjs/common'
87
+ import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'
88
+ import { UsersService } from './users.service'
89
+ import { CreateUserDto, UpdateUserDto, UserResponseDto } from './dto'
90
+ import { PaginationDto } from '@/common/dto'
91
+
92
+ @ApiTags('users')
93
+ @Controller('users')
94
+ export class UsersController {
95
+ constructor(private readonly usersService: UsersService) {}
96
+
97
+ @Get()
98
+ @ApiOperation({ summary: 'Get all users' })
99
+ @ApiResponse({ status: 200, type: [UserResponseDto] })
100
+ async findAll(@Query() query: PaginationDto): Promise<UserResponseDto[]> {
101
+ return this.usersService.findAll(query)
102
+ }
103
+
104
+ @Get(':id')
105
+ @ApiOperation({ summary: 'Get user by ID' })
106
+ @ApiResponse({ status: 200, type: UserResponseDto })
107
+ @ApiResponse({ status: 404, description: 'User not found' })
108
+ async findOne(@Param('id') id: string): Promise<UserResponseDto> {
109
+ return this.usersService.findOne(id)
110
+ }
111
+
112
+ @Post()
113
+ @HttpCode(HttpStatus.CREATED)
114
+ @ApiOperation({ summary: 'Create new user' })
115
+ @ApiResponse({ status: 201, type: UserResponseDto })
116
+ async create(@Body() dto: CreateUserDto): Promise<UserResponseDto> {
117
+ return this.usersService.create(dto)
118
+ }
119
+
120
+ @Put(':id')
121
+ @ApiOperation({ summary: 'Update user' })
122
+ async update(@Param('id') id: string, @Body() dto: UpdateUserDto): Promise<UserResponseDto> {
123
+ return this.usersService.update(id, dto)
124
+ }
125
+
126
+ @Delete(':id')
127
+ @HttpCode(HttpStatus.NO_CONTENT)
128
+ @ApiOperation({ summary: 'Delete user' })
129
+ async remove(@Param('id') id: string): Promise<void> {
130
+ return this.usersService.remove(id)
131
+ }
132
+ }
133
+ ```
134
+
135
+ ---
136
+
137
+ ## DTOs and Validation
138
+
139
+ ### Class-Validator Approach
140
+
141
+ ```typescript
142
+ // dto/create-user.dto.ts
143
+ import {
144
+ IsString,
145
+ IsEmail,
146
+ MinLength,
147
+ MaxLength,
148
+ IsOptional,
149
+ IsEnum,
150
+ ValidateNested,
151
+ IsArray,
152
+ } from 'class-validator'
153
+ import { Type } from 'class-transformer'
154
+ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'
155
+
156
+ export enum UserRole {
157
+ User = 'user',
158
+ Admin = 'admin',
159
+ Moderator = 'moderator',
160
+ }
161
+
162
+ export class AddressDto {
163
+ @ApiProperty()
164
+ @IsString()
165
+ street: string
166
+
167
+ @ApiProperty()
168
+ @IsString()
169
+ city: string
170
+
171
+ @ApiProperty()
172
+ @IsString()
173
+ country: string
174
+ }
175
+
176
+ export class CreateUserDto {
177
+ @ApiProperty({ example: 'john@example.com' })
178
+ @IsEmail()
179
+ email: string
180
+
181
+ @ApiProperty({ minLength: 2, maxLength: 50 })
182
+ @IsString()
183
+ @MinLength(2)
184
+ @MaxLength(50)
185
+ name: string
186
+
187
+ @ApiProperty({ minLength: 8 })
188
+ @IsString()
189
+ @MinLength(8)
190
+ password: string
191
+
192
+ @ApiPropertyOptional({ enum: UserRole, default: UserRole.User })
193
+ @IsOptional()
194
+ @IsEnum(UserRole)
195
+ role?: UserRole = UserRole.User
196
+
197
+ @ApiPropertyOptional({ type: AddressDto })
198
+ @IsOptional()
199
+ @ValidateNested()
200
+ @Type(() => AddressDto)
201
+ address?: AddressDto
202
+
203
+ @ApiPropertyOptional({ type: [String] })
204
+ @IsOptional()
205
+ @IsArray()
206
+ @IsString({ each: true })
207
+ tags?: string[]
208
+ }
209
+
210
+ // dto/update-user.dto.ts
211
+ import { PartialType } from '@nestjs/swagger'
212
+ import { CreateUserDto } from './create-user.dto'
213
+
214
+ export class UpdateUserDto extends PartialType(CreateUserDto) {}
215
+ ```
216
+
217
+ ### Zod-Based DTOs (Modern Approach)
218
+
219
+ ```typescript
220
+ // dto/user.schema.ts
221
+ import { z } from 'zod'
222
+ import { createZodDto } from 'nestjs-zod'
223
+
224
+ // Define Zod schemas
225
+ export const UserRoleSchema = z.enum(['user', 'admin', 'moderator'])
226
+
227
+ export const AddressSchema = z.object({
228
+ street: z.string(),
229
+ city: z.string(),
230
+ country: z.string(),
231
+ })
232
+
233
+ export const CreateUserSchema = z.object({
234
+ email: z.string().email(),
235
+ name: z.string().min(2).max(50),
236
+ password: z.string().min(8),
237
+ role: UserRoleSchema.default('user').optional(),
238
+ address: AddressSchema.optional(),
239
+ tags: z.array(z.string()).optional(),
240
+ })
241
+
242
+ export const UpdateUserSchema = CreateUserSchema.partial()
243
+
244
+ // Create DTO classes from schemas
245
+ export class CreateUserDto extends createZodDto(CreateUserSchema) {}
246
+ export class UpdateUserDto extends createZodDto(UpdateUserSchema) {}
247
+
248
+ // Infer types
249
+ export type CreateUser = z.infer<typeof CreateUserSchema>
250
+ export type UpdateUser = z.infer<typeof UpdateUserSchema>
251
+ ```
252
+
253
+ ### Global Validation Pipe
254
+
255
+ ```typescript
256
+ // main.ts
257
+ import { ValidationPipe } from '@nestjs/common'
258
+ import { NestFactory } from '@nestjs/core'
259
+ import { AppModule } from './app.module'
260
+
261
+ async function bootstrap() {
262
+ const app = await NestFactory.create(AppModule)
263
+
264
+ app.useGlobalPipes(
265
+ new ValidationPipe({
266
+ whitelist: true, // Strip unknown properties
267
+ forbidNonWhitelisted: true, // Throw on unknown properties
268
+ transform: true, // Transform payloads to DTO classes
269
+ transformOptions: {
270
+ enableImplicitConversion: true,
271
+ },
272
+ })
273
+ )
274
+
275
+ await app.listen(3000)
276
+ }
277
+ bootstrap()
278
+ ```
279
+
280
+ ---
281
+
282
+ ## Services and Dependency Injection
283
+
284
+ ### Typed Service
285
+
286
+ ```typescript
287
+ // users.service.ts
288
+ import { Injectable, NotFoundException } from '@nestjs/common'
289
+ import { UsersRepository } from './users.repository'
290
+ import { CreateUserDto, UpdateUserDto, UserResponseDto } from './dto'
291
+ import { PaginationDto } from '@/common/dto'
292
+
293
+ @Injectable()
294
+ export class UsersService {
295
+ constructor(private readonly usersRepository: UsersRepository) {}
296
+
297
+ async findAll(query: PaginationDto): Promise<UserResponseDto[]> {
298
+ const users = await this.usersRepository.findAll(query)
299
+ return users.map(this.toResponseDto)
300
+ }
301
+
302
+ async findOne(id: string): Promise<UserResponseDto> {
303
+ const user = await this.usersRepository.findById(id)
304
+ if (!user) {
305
+ throw new NotFoundException(`User with ID ${id} not found`)
306
+ }
307
+ return this.toResponseDto(user)
308
+ }
309
+
310
+ async create(dto: CreateUserDto): Promise<UserResponseDto> {
311
+ const user = await this.usersRepository.create(dto)
312
+ return this.toResponseDto(user)
313
+ }
314
+
315
+ async update(id: string, dto: UpdateUserDto): Promise<UserResponseDto> {
316
+ const user = await this.usersRepository.update(id, dto)
317
+ if (!user) {
318
+ throw new NotFoundException(`User with ID ${id} not found`)
319
+ }
320
+ return this.toResponseDto(user)
321
+ }
322
+
323
+ async remove(id: string): Promise<void> {
324
+ const deleted = await this.usersRepository.delete(id)
325
+ if (!deleted) {
326
+ throw new NotFoundException(`User with ID ${id} not found`)
327
+ }
328
+ }
329
+
330
+ private toResponseDto(user: User): UserResponseDto {
331
+ return {
332
+ id: user.id,
333
+ email: user.email,
334
+ name: user.name,
335
+ role: user.role,
336
+ createdAt: user.createdAt,
337
+ }
338
+ }
339
+ }
340
+ ```
341
+
342
+ ### Repository Pattern
343
+
344
+ ```typescript
345
+ // users.repository.ts
346
+ import { Injectable } from '@nestjs/common'
347
+ import { PrismaService } from '@/prisma/prisma.service'
348
+ import { User, Prisma } from '@prisma/client'
349
+ import { PaginationDto } from '@/common/dto'
350
+
351
+ @Injectable()
352
+ export class UsersRepository {
353
+ constructor(private readonly prisma: PrismaService) {}
354
+
355
+ async findAll(query: PaginationDto): Promise<User[]> {
356
+ return this.prisma.user.findMany({
357
+ skip: query.skip,
358
+ take: query.take,
359
+ orderBy: { createdAt: 'desc' },
360
+ })
361
+ }
362
+
363
+ async findById(id: string): Promise<User | null> {
364
+ return this.prisma.user.findUnique({ where: { id } })
365
+ }
366
+
367
+ async findByEmail(email: string): Promise<User | null> {
368
+ return this.prisma.user.findUnique({ where: { email } })
369
+ }
370
+
371
+ async create(data: Prisma.UserCreateInput): Promise<User> {
372
+ return this.prisma.user.create({ data })
373
+ }
374
+
375
+ async update(id: string, data: Prisma.UserUpdateInput): Promise<User | null> {
376
+ try {
377
+ return await this.prisma.user.update({ where: { id }, data })
378
+ } catch {
379
+ return null
380
+ }
381
+ }
382
+
383
+ async delete(id: string): Promise<boolean> {
384
+ try {
385
+ await this.prisma.user.delete({ where: { id } })
386
+ return true
387
+ } catch {
388
+ return false
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Authentication
397
+
398
+ ### JWT Authentication
399
+
400
+ ```typescript
401
+ // auth/strategies/jwt.strategy.ts
402
+ import { Injectable, UnauthorizedException } from '@nestjs/common'
403
+ import { PassportStrategy } from '@nestjs/passport'
404
+ import { ExtractJwt, Strategy } from 'passport-jwt'
405
+ import { ConfigService } from '@nestjs/config'
406
+ import { UsersService } from '@/modules/users/users.service'
407
+
408
+ interface JwtPayload {
409
+ sub: string
410
+ email: string
411
+ role: string
412
+ iat: number
413
+ exp: number
414
+ }
415
+
416
+ @Injectable()
417
+ export class JwtStrategy extends PassportStrategy(Strategy) {
418
+ constructor(
419
+ configService: ConfigService,
420
+ private readonly usersService: UsersService
421
+ ) {
422
+ super({
423
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
424
+ ignoreExpiration: false,
425
+ secretOrKey: configService.getOrThrow<string>('JWT_SECRET'),
426
+ })
427
+ }
428
+
429
+ async validate(payload: JwtPayload) {
430
+ const user = await this.usersService.findOne(payload.sub)
431
+ if (!user) {
432
+ throw new UnauthorizedException()
433
+ }
434
+ return { id: payload.sub, email: payload.email, role: payload.role }
435
+ }
436
+ }
437
+ ```
438
+
439
+ ### Role-Based Access Control
440
+
441
+ ```typescript
442
+ // common/decorators/roles.decorator.ts
443
+ import { SetMetadata } from '@nestjs/common'
444
+
445
+ export const ROLES_KEY = 'roles'
446
+ export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles)
447
+
448
+ // common/guards/roles.guard.ts
449
+ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'
450
+ import { Reflector } from '@nestjs/core'
451
+ import { ROLES_KEY } from '../decorators/roles.decorator'
452
+
453
+ @Injectable()
454
+ export class RolesGuard implements CanActivate {
455
+ constructor(private reflector: Reflector) {}
456
+
457
+ canActivate(context: ExecutionContext): boolean {
458
+ const requiredRoles = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
459
+ context.getHandler(),
460
+ context.getClass(),
461
+ ])
462
+
463
+ if (!requiredRoles) {
464
+ return true
465
+ }
466
+
467
+ const { user } = context.switchToHttp().getRequest()
468
+ return requiredRoles.includes(user.role)
469
+ }
470
+ }
471
+
472
+ // Usage in controller
473
+ @Controller('admin')
474
+ @UseGuards(JwtAuthGuard, RolesGuard)
475
+ export class AdminController {
476
+ @Get('users')
477
+ @Roles('admin')
478
+ findAllUsers() {
479
+ return this.adminService.findAllUsers()
480
+ }
481
+
482
+ @Delete('users/:id')
483
+ @Roles('admin', 'moderator')
484
+ removeUser(@Param('id') id: string) {
485
+ return this.adminService.removeUser(id)
486
+ }
487
+ }
488
+ ```
489
+
490
+ ---
491
+
492
+ ## Error Handling
493
+
494
+ ### Exception Filters
495
+
496
+ ```typescript
497
+ // common/filters/http-exception.filter.ts
498
+ import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common'
499
+ import { Response } from 'express'
500
+
501
+ interface ErrorResponse {
502
+ statusCode: number
503
+ message: string
504
+ error: string
505
+ timestamp: string
506
+ path: string
507
+ }
508
+
509
+ @Catch()
510
+ export class AllExceptionsFilter implements ExceptionFilter {
511
+ catch(exception: unknown, host: ArgumentsHost) {
512
+ const ctx = host.switchToHttp()
513
+ const response = ctx.getResponse<Response>()
514
+ const request = ctx.getRequest()
515
+
516
+ let status = HttpStatus.INTERNAL_SERVER_ERROR
517
+ let message = 'Internal server error'
518
+ let error = 'Internal Server Error'
519
+
520
+ if (exception instanceof HttpException) {
521
+ status = exception.getStatus()
522
+ const exceptionResponse = exception.getResponse()
523
+
524
+ if (typeof exceptionResponse === 'string') {
525
+ message = exceptionResponse
526
+ } else if (typeof exceptionResponse === 'object') {
527
+ const responseObj = exceptionResponse as Record<string, unknown>
528
+ message = (responseObj.message as string) || message
529
+ error = (responseObj.error as string) || exception.name
530
+ }
531
+ }
532
+
533
+ const errorResponse: ErrorResponse = {
534
+ statusCode: status,
535
+ message,
536
+ error,
537
+ timestamp: new Date().toISOString(),
538
+ path: request.url,
539
+ }
540
+
541
+ response.status(status).json(errorResponse)
542
+ }
543
+ }
544
+
545
+ // main.ts
546
+ app.useGlobalFilters(new AllExceptionsFilter())
547
+ ```
548
+
549
+ ### Custom Exceptions
550
+
551
+ ```typescript
552
+ // common/exceptions/business.exception.ts
553
+ import { HttpException, HttpStatus } from '@nestjs/common'
554
+
555
+ export class BusinessException extends HttpException {
556
+ constructor(
557
+ message: string,
558
+ public readonly code: string,
559
+ status: HttpStatus = HttpStatus.BAD_REQUEST
560
+ ) {
561
+ super({ message, code }, status)
562
+ }
563
+ }
564
+
565
+ export class InsufficientFundsException extends BusinessException {
566
+ constructor(required: number, available: number) {
567
+ super(`Insufficient funds: required ${required}, available ${available}`, 'INSUFFICIENT_FUNDS')
568
+ }
569
+ }
570
+
571
+ export class DuplicateEmailException extends BusinessException {
572
+ constructor(email: string) {
573
+ super(`Email ${email} is already registered`, 'DUPLICATE_EMAIL')
574
+ }
575
+ }
576
+
577
+ // Usage
578
+ throw new InsufficientFundsException(100, 50)
579
+ ```