Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Cursor rules that block deprecated, phantom, or incorrect NestJS imports, decorators, providers, modules, and testing patterns.
.claude/skills/amariahak-cursor-rules-that-block-deprecated-phantom-or-incorrect-nestjs-imports-decora/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-18 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-19 | ✓→✗ | ▼ Worse | 64% | 0% |
| case-07 | ✓→✗ | ▼ Worse | 73% | 0% |
| case-12 | ✓→✓ | = Same ✓ | 62% | 0% |
Cursor rules that block deprecated, phantom, or incorrect NestJS imports, decorators, providers, modules, and testing patterns.
Synced from https://github.com/PatrickJS/awesome-cursorrules/tree/main/rules/nestjs-anti-hallucination-cursorrules-prompt-file.mdc.
These rules OVERRIDE all other generation behavior. Check EVERY line of generated code against these rules.
❌ @nestjs/core/decorators — not a real export path
❌ @nestjs/swagger/decorators — import from @nestjs/swagger directly
❌ @nestjs/typeorm/repository — not a real export path
❌ @nestjs/passport/strategies — import from passport-jwt, passport-local, etc.
❌ @nestjs/bull/decorators — import from @nestjs/bullmq (bull is legacy)
❌ nestjs-redis — use @nestjs-modules/ioredis or ioredis directly
❌ @nestjs/cqrs/decorators — import from @nestjs/cqrs directly
❌ nestjs-config — use @nestjs/config (official)
❌ nestjs-pino/logger — import from nestjs-pino directlytypescript// ✅ Swagger import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger'; // ✅ TypeORM import { InjectRepository } from '@nestjs/typeorm'; import { Repository, DataSource } from 'typeorm'; // ✅ BullMQ (NOT Bull) import { InjectQueue, Processor, WorkerHost } from '@nestjs/bullmq'; // ✅ Config import { ConfigService, ConfigModule } from '@nestjs/config'; // ✅ Passport import { AuthGuard } from '@nestjs/passport'; import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'; // ✅ CQRS import { CommandHandler, ICommandHandler, EventBus } from '@nestjs/cqrs';
typescript// ❌ DEPRECATED — removed in TypeORM 0.3+ const repo = getRepository(User); const user = await getConnection().getRepository(User).find(); // ✅ CORRECT — inject via constructor constructor( @InjectRepository(User) private readonly userRepo: Repository<User>, ) {}
typescript// ❌ OLD — @nestjs/bull with @Process decorator import { Process, Processor } from '@nestjs/bull'; @Processor('queue') class MyProcessor { @Process() async handle(job: Job) {} } // ✅ CURRENT — @nestjs/bullmq with WorkerHost import { Processor, WorkerHost } from '@nestjs/bullmq'; @Processor('queue') class MyProcessor extends WorkerHost { async process(job: Job): Promise<void> {} }
typescript// ❌ WRONG — Express req/res types in NestJS import { Request, Response } from 'express'; @Get() async findAll(@Req() req: Request, @Res() res: Response) { res.json(data); // Bypasses interceptors, serialization, exception filters } // ✅ CORRECT — Use NestJS decorators, return values @Get() async findAll(@Query() query: FindAllQueryDto): Promise<UserResponseDto[]> { return this.usersService.findAll(query); } // Only use @Res() when streaming files or SSE — add { passthrough: true } @Get('download') async download(@Res({ passthrough: true }) res: Response) { res.set('Content-Type', 'application/octet-stream'); return new StreamableFile(stream); }
typescript// ❌ WRONG — @Injectable() on a controller @Injectable() @Controller('users') export class UsersController {} // ❌ WRONG — @Controller() on a service @Controller() @Injectable() export class UsersService {} // ❌ WRONG — @Body() in a GET handler @Get() async findAll(@Body() body: any) {} // GET requests should not have a body // ❌ WRONG — Both @Param and @Query with same name @Get(':id') async findOne(@Param('id') paramId: string, @Query('id') queryId: string) {}
typescript// ❌ WRONG — Validation without enabling in main.ts // (AI often forgets this critical line) // main.ts MUST have: app.useGlobalPipes(new ValidationPipe({ whitelist: true, // Strip unknown properties forbidNonWhitelisted: true, // Throw on unknown properties transform: true, // Auto-transform payloads to DTO instances transformOptions: { enableImplicitConversion: true, }, })); // ❌ WRONG — Mixing validation decorators with wrong transform export class CreateUserDto { @IsString() name: string; @IsNumber() age: string; // Type mismatch! Decorator says number, type says string } // ✅ CORRECT — Types match decorators export class CreateUserDto { @IsString() @IsNotEmpty() name: string; @IsInt() @Min(0) age: number; }
typescript// ❌ WRONG — useFactory without async when awaiting TypeOrmModule.forRootAsync({ useFactory: (config: ConfigService) => ({ type: 'postgres', url: config.get('DATABASE_URL'), // Not awaited, no inject }), }) // ✅ CORRECT TypeOrmModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: async (config: ConfigService) => ({ type: 'postgres', url: config.getOrThrow<string>('DATABASE_URL'), autoLoadEntities: true, synchronize: false, // NEVER true in production }), })
anytypescript// ❌ BANNED catch (error: any) { ... } const data: any = await response.json(); private cache = new Map<string, any>(); // ✅ REQUIRED catch (error: unknown) { if (error instanceof DomainError) { ... } throw error; } const data = await response.json() as PaymentGatewayResponse; private cache = new Map<string, CachedSession>();
typescript// ❌ WRONG eventBus.emit('order.created', { order }); // ✅ CORRECT — Typed events export class OrderCreatedEvent { constructor( public readonly orderId: string, public readonly userId: string, public readonly totalAmount: number, public readonly occurredAt: Date = new Date(), ) {} } eventBus.emit(new OrderCreatedEvent(order.id, order.userId, order.total));
typescript// ❌ WRONG const port = process.env.PORT || 3000; const dbUrl = process.env.DATABASE_URL; // ✅ CORRECT const port = this.configService.getOrThrow<number>('app.port'); const dbUrl = this.configService.getOrThrow<string>('database.url');
typescript// ❌ synchronize: true in production config // ❌ DROP TABLE or TRUNCATE in migration files // ❌ Raw SQL without parameterized queries await this.dataSource.query(`SELECT * FROM users WHERE id = '${userId}'`); // SQL injection! // ✅ CORRECT await this.dataSource.query(`SELECT * FROM users WHERE id = $1`, [userId]);
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-18 | fail→pass | 13,104 | 10,120 | -23% | 1 | 1 | 0% | 2,556 | 4,054 | +59% | 0 | 0 | — |
case-19 | pass→fail | 10,158 | 5,403 | -47% | 1 | 1 | 0% | 1,868 | 3,068 | +64% | 0 | 0 | — |
case-02 | fail→fail | 13,384 | 10,259 | -23% | 1 | 1 | 0% | 2,891 | 4,004 | +38% | 0 | 0 | — |
case-01 | fail→fail | 13,799 | 9,180 | -33% | 1 | 1 | 0% | 2,678 | 3,733 | +39% | 0 | 0 | — |
case-12 | pass→pass | 15,449 | 7,376 | -52% | 1 | 1 | 0% | 2,271 | 3,670 | +62% | 0 | 0 | — |
case-03 | pass→pass | 4,531 | 3,790 | -16% | 1 | 1 | 0% | 901 | 2,731 | +203% | 0 | 0 | — |
case-04 | pass→pass | 6,457 | 5,761 | -11% | 1 | 1 | 0% | 1,343 | 3,083 | +130% | 0 | 0 | — |
case-05 | pass→pass | 16,378 | 9,909 | -39% | 1 | 1 | 0% | 2,685 | 4,283 | +60% | 0 | 0 | — |
case-06 | pass→pass | 5,300 | 4,788 | -10% | 1 | 1 | 0% | 1,076 | 2,948 | +174% | 0 | 0 | — |
case-17 | fail→pass | 8,738 | 5,514 | -37% | 1 | 1 | 0% | 1,832 | 3,146 | +72% | 0 | 0 | — |
case-07 | pass→fail | 8,783 | 5,544 | -37% | 1 | 1 | 0% | 1,780 | 3,081 | +73% | 0 | 0 | — |
case-08 | pass→pass | 10,588 | 11,589 | +9% | 1 | 1 | 0% | 2,123 | 3,748 | +77% | 0 | 0 | — |
case-09 | pass→pass | 12,683 | 7,620 | -40% | 1 | 1 | 0% | 2,807 | 3,627 | +29% | 0 | 0 | — |
case-10 | pass→pass | 33,848 | 9,368 | -72% | 1 | 1 | 0% | 3,413 | 3,999 | +17% | 0 | 0 | — |
case-11 | pass→pass | 13,087 | 10,078 | -23% | 1 | 1 | 0% | 2,666 | 4,083 | +53% | 0 | 0 | — |
case-13 | pass→pass | 8,957 | 10,627 | +19% | 1 | 1 | 0% | 2,039 | 4,334 | +113% | 0 | 0 | — |
case-14 | pass→pass | 15,704 | 9,688 | -38% | 1 | 1 | 0% | 2,713 | 3,954 | +46% | 0 | 0 | — |
case-15 | pass→pass | 5,446 | 4,706 | -14% | 1 | 1 | 0% | 1,199 | 2,932 | +145% | 0 | 0 | — |
case-16 | pass→pass | 7,622 | 4,556 | -40% | 1 | 1 | 0% | 1,435 | 2,949 | +106% | 0 | 0 | — |
case-20 | pass→pass | 8,910 | 6,113 | -31% | 1 | 1 | 0% | 1,835 | 3,184 | +74% | 0 | 0 | — |
case-21 | pass→pass | 9,692 | 6,193 | -36% | 1 | 1 | 0% | 1,949 | 3,103 | +59% | 0 | 0 | — |
case-22 | pass→pass | 11,150 | 8,632 | -23% | 1 | 1 | 0% | 2,068 | 3,802 | +84% | 0 | 0 | — |
case-23 | pass→pass | 5,514 | 2,776 | -50% | 1 | 1 | 0% | 815 | 2,425 | +198% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 23 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.