{"filename":"nestjs-anti-hallucination-cursorrules-prompt-file.mdc","content":"---\ndescription: \"Cursor rules that block deprecated, phantom, or incorrect NestJS imports, decorators, providers, modules, and testing patterns.\"\nglobs: **/*\nalwaysApply: false\n---\n# NestJS Anti-Hallucination Rules\n\nThese rules OVERRIDE all other generation behavior. Check EVERY line of generated code against these rules.\n\n## Banned Imports & Phantom Packages\n\n### NEVER import these — they don't exist or are deprecated:\n```\n❌ @nestjs/core/decorators    — not a real export path\n❌ @nestjs/swagger/decorators — import from @nestjs/swagger directly\n❌ @nestjs/typeorm/repository — not a real export path\n❌ @nestjs/passport/strategies — import from passport-jwt, passport-local, etc.\n❌ @nestjs/bull/decorators     — import from @nestjs/bullmq (bull is legacy)\n❌ nestjs-redis               — use @nestjs-modules/ioredis or ioredis directly\n❌ @nestjs/cqrs/decorators    — import from @nestjs/cqrs directly\n❌ nestjs-config              — use @nestjs/config (official)\n❌ nestjs-pino/logger          — import from nestjs-pino directly\n```\n\n### Correct import paths:\n```typescript\n// ✅ Swagger\nimport { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';\n\n// ✅ TypeORM\nimport { InjectRepository } from '@nestjs/typeorm';\nimport { Repository, DataSource } from 'typeorm';\n\n// ✅ BullMQ (NOT Bull)\nimport { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq';\n\n// ✅ Config\nimport { ConfigService, ConfigModule } from '@nestjs/config';\n\n// ✅ Passport\nimport { AuthGuard } from '@nestjs/passport';\nimport { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';\n\n// ✅ CQRS\nimport { CommandHandler, ICommandHandler, EventBus } from '@nestjs/cqrs';\n```\n\n## Deprecated Patterns — NEVER Generate These\n\n### 1. getRepository() outside providers\n```typescript\n// ❌ DEPRECATED — removed in TypeORM 0.3+\nconst repo = getRepository(User);\nconst user = await getConnection().getRepository(User).find();\n\n// ✅ CORRECT — inject via constructor\nconstructor(\n  @InjectRepository(User)\n  private readonly userRepo: Repository<User>,\n) {}\n```\n\n### 2. @nestjs/bull (use @nestjs/bullmq)\n```typescript\n// ❌ OLD — @nestjs/bull with @Process decorator\nimport { Process, Processor } from '@nestjs/bull';\n@Processor('queue')\nclass MyProcessor {\n  @Process() async handle(job: Job) {}\n}\n\n// ✅ CURRENT — @nestjs/bullmq with WorkerHost\nimport { Processor, WorkerHost } from '@nestjs/bullmq';\n@Processor('queue')\nclass MyProcessor extends WorkerHost {\n  async process(job: Job): Promise<void> {}\n}\n```\n\n### 3. Express-specific middleware mistakes\n```typescript\n// ❌ WRONG — Express req/res types in NestJS\nimport { Request, Response } from 'express';\n@Get()\nasync findAll(@Req() req: Request, @Res() res: Response) {\n  res.json(data); // Bypasses interceptors, serialization, exception filters\n}\n\n// ✅ CORRECT — Use NestJS decorators, return values\n@Get()\nasync findAll(@Query() query: FindAllQueryDto): Promise<UserResponseDto[]> {\n  return this.usersService.findAll(query);\n}\n\n// Only use @Res() when streaming files or SSE — add { passthrough: true }\n@Get('download')\nasync download(@Res({ passthrough: true }) res: Response) {\n  res.set('Content-Type', 'application/octet-stream');\n  return new StreamableFile(stream);\n}\n```\n\n### 4. Wrong decorator combinations\n```typescript\n// ❌ WRONG — @Injectable() on a controller\n@Injectable()\n@Controller('users')\nexport class UsersController {}\n\n// ❌ WRONG — @Controller() on a service\n@Controller()\n@Injectable()\nexport class UsersService {}\n\n// ❌ WRONG — @Body() in a GET handler\n@Get()\nasync findAll(@Body() body: any) {} // GET requests should not have a body\n\n// ❌ WRONG — Both @Param and @Query with same name\n@Get(':id')\nasync findOne(@Param('id') paramId: string, @Query('id') queryId: string) {}\n```\n\n### 5. class-validator / class-transformer mistakes\n```typescript\n// ❌ WRONG — Validation without enabling in main.ts\n// (AI often forgets this critical line)\n// main.ts MUST have:\napp.useGlobalPipes(new ValidationPipe({\n  whitelist: true,            // Strip unknown properties\n  forbidNonWhitelisted: true, // Throw on unknown properties\n  transform: true,            // Auto-transform payloads to DTO instances\n  transformOptions: {\n    enableImplicitConversion: true,\n  },\n}));\n\n// ❌ WRONG — Mixing validation decorators with wrong transform\nexport class CreateUserDto {\n  @IsString()\n  name: string;\n\n  @IsNumber()\n  age: string; // Type mismatch! Decorator says number, type says string\n}\n\n// ✅ CORRECT — Types match decorators\nexport class CreateUserDto {\n  @IsString()\n  @IsNotEmpty()\n  name: string;\n\n  @IsInt()\n  @Min(0)\n  age: number;\n}\n```\n\n### 6. Async module pitfalls\n```typescript\n// ❌ WRONG — useFactory without async when awaiting\nTypeOrmModule.forRootAsync({\n  useFactory: (config: ConfigService) => ({\n    type: 'postgres',\n    url: config.get('DATABASE_URL'), // Not awaited, no inject\n  }),\n})\n\n// ✅ CORRECT\nTypeOrmModule.forRootAsync({\n  imports: [ConfigModule],\n  inject: [ConfigService],\n  useFactory: async (config: ConfigService) => ({\n    type: 'postgres',\n    url: config.getOrThrow<string>('DATABASE_URL'),\n    autoLoadEntities: true,\n    synchronize: false, // NEVER true in production\n  }),\n})\n```\n\n## Type Safety Rules\n\n### NEVER generate `any`\n```typescript\n// ❌ BANNED\ncatch (error: any) { ... }\nconst data: any = await response.json();\nprivate cache = new Map<string, any>();\n\n// ✅ REQUIRED\ncatch (error: unknown) {\n  if (error instanceof DomainError) { ... }\n  throw error;\n}\nconst data = await response.json() as PaymentGatewayResponse;\nprivate cache = new Map<string, CachedSession>();\n```\n\n### NEVER generate untyped event payloads\n```typescript\n// ❌ WRONG\neventBus.emit('order.created', { order });\n\n// ✅ CORRECT — Typed events\nexport class OrderCreatedEvent {\n  constructor(\n    public readonly orderId: string,\n    public readonly userId: string,\n    public readonly totalAmount: number,\n    public readonly occurredAt: Date = new Date(),\n  ) {}\n}\neventBus.emit(new OrderCreatedEvent(order.id, order.userId, order.total));\n```\n\n## Configuration Safety\n\n### NEVER generate hardcoded values for:\n- Database connection strings\n- API keys or secrets\n- Port numbers\n- Feature flags\n- External service URLs\n\n### ALWAYS use ConfigService with getOrThrow:\n```typescript\n// ❌ WRONG\nconst port = process.env.PORT || 3000;\nconst dbUrl = process.env.DATABASE_URL;\n\n// ✅ CORRECT\nconst port = this.configService.getOrThrow<number>('app.port');\nconst dbUrl = this.configService.getOrThrow<string>('database.url');\n```\n\n## Database Safety\n\n### NEVER generate:\n```typescript\n// ❌ synchronize: true in production config\n// ❌ DROP TABLE or TRUNCATE in migration files\n// ❌ Raw SQL without parameterized queries\nawait this.dataSource.query(`SELECT * FROM users WHERE id = '${userId}'`); // SQL injection!\n\n// ✅ CORRECT\nawait this.dataSource.query(`SELECT * FROM users WHERE id = $1`, [userId]);\n```\n\n## Remember\n- If you're unsure whether a package exists, DO NOT import it. Ask or check.\n- If you're generating a NestJS pattern you haven't seen in the official docs, STOP and reconsider.\n- When in doubt, generate LESS code with correct patterns rather than MORE code with guesses.\n"}