ACTIVATE when creating NestJS modules, controllers, services, guards, pipes, or configuring dependency injection. ACTIVATE for 'NestJS', '@Module', '@Controller', '@Injectable', 'guard', 'pipe', 'DI token'. Covers: module-per-bounded-context structure, thin controllers, ZodValidationPipe, guards for auth, exception filters, Symbol token-based DI for interfaces, constructor injection only. DO NOT use for: domain layer code (see nest-ddd-conventions), Zod schema design (see zod-conventions).
From nestnpx claudepluginhub fabiensalles/claude-marketplace --plugin nestThis skill uses the workspace's default tool permissions.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Configures VPN and dedicated connections like Direct Connect, ExpressRoute, Interconnect for secure on-premises to AWS, Azure, GCP, OCI hybrid networking.
1 module = 1 bounded context. Each module encapsulates its own controllers, services, and providers.
// ✅ CORRECT - Self-contained module
@Module({
imports: [SharedModule],
controllers: [ReceiptController],
providers: [
ReceiptService,
{ provide: RECEIPT_REPOSITORY, useClass: DrizzleReceiptRepository },
],
exports: [ReceiptService], // Only export what other modules need
})
export class ReceiptModule {}
| Type | Pattern | Example |
|---|---|---|
| Module | *.module.ts | receipt.module.ts |
| Controller | *.controller.ts | receipt.controller.ts |
| Service | *.service.ts | receipt.service.ts |
| Guard | *.guard.ts | auth.guard.ts |
| Pipe | *.pipe.ts | zod-validation.pipe.ts |
| Filter | *.filter.ts | http-exception.filter.ts |
| Interceptor | *.interceptor.ts | logging.interceptor.ts |
| Test | *.spec.ts | receipt.service.spec.ts |
Controllers handle HTTP concerns only. Delegate all business logic to services.
// ❌ AVOID - Business logic in controller
@Controller('receipts')
export class ReceiptController {
@Post()
async create(@Body() dto: CreateReceiptDto) {
const lease = await this.leaseRepo.findById(dto.leaseId);
if (!lease) {
throw new NotFoundException('Lease not found');
}
const amount = lease.monthlyRent;
const receipt = { leaseId: dto.leaseId, amount, period: dto.period };
return this.receiptRepo.save(receipt);
}
}
// ✅ CORRECT - Controller delegates to service
@Controller('receipts')
export class ReceiptController {
constructor(private readonly receiptService: ReceiptService) {}
@Post()
async create(@Body(ZodValidationPipe) dto: CreateReceiptDto) {
return this.receiptService.generate(dto);
}
}
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.replace('Bearer ', '');
if (!token) {
throw new UnauthorizedException();
}
// Validate token...
return true;
}
}
// Usage: at controller or route level
@UseGuards(AuthGuard)
@Controller('receipts')
export class ReceiptController { ... }
Use a ZodValidationPipe for DTO validation with Zod schemas:
@Injectable()
export class ZodValidationPipe implements PipeTransform {
constructor(private readonly schema: ZodSchema) {}
transform(value: unknown) {
const result = this.schema.safeParse(value);
if (!result.success) {
throw new BadRequestException(result.error.flatten());
}
return result.data;
}
}
// Usage
@Post()
async create(
@Body(new ZodValidationPipe(CreateReceiptSchema)) dto: CreateReceiptDto,
) {
return this.receiptService.generate(dto);
}
See also:
zod-conventionsfor Zod schema patterns.
Centralized error handling:
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.message,
timestamp: new Date().toISOString(),
});
}
}
// Register globally in main.ts
app.useGlobalFilters(new HttpExceptionFilter());
// Define token
export const RECEIPT_REPOSITORY = Symbol('ReceiptRepository');
// Register in module
@Module({
providers: [
{ provide: RECEIPT_REPOSITORY, useClass: DrizzleReceiptRepository },
],
})
// Inject in service
@Injectable()
export class ReceiptService {
constructor(
@Inject(RECEIPT_REPOSITORY) private readonly repo: ReceiptRepository,
) {}
}
// ✅ CORRECT - Constructor injection
@Injectable()
export class ReceiptService {
constructor(private readonly repo: ReceiptRepository) {}
}
// ❌ AVOID - Property injection
@Injectable()
export class ReceiptService {
@Inject() repo: ReceiptRepository;
}
| Rule | Convention |
|---|---|
| Module scope | 1 module = 1 bounded context |
| Controllers | Thin — delegate to services |
| Validation | ZodValidationPipe in @Body() |
| Auth | Guards at controller/route level |
| Error handling | Global exception filters |
| DI for interfaces | Symbol tokens + @Inject() |
| DI style | Constructor injection only |
| File naming | feature.type.ts pattern |