Help us improve
Share bugs, ideas, or general feedback.
From nest
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).
npx claudepluginhub fabiensalles/claude-marketplace --plugin nestHow this skill is triggered — by the user, by Claude, or both
Slash command
/nest:nest-conventionsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**1 module = 1 bounded context.** Each module encapsulates its own controllers, services, and providers.
Enforces NestJS best practices for modular architecture, dependency injection scoping, exception filters, class-validator DTO validation, and Drizzle ORM integration. Use when designing modules, providers, filters, DTOs, or ORM in NestJS apps.
Provides NestJS patterns for modules, controllers, providers, DTO validation, guards, interceptors, config, and production TypeScript backends. Useful for structuring APIs, adding validation, and database integrations.
Creates and configures NestJS modules, controllers, services, DTOs, guards, and interceptors for TypeScript backend apps. Use for REST/GraphQL APIs, dependency injection, JWT/Passport auth, TypeORM/Prisma integration, validation, Swagger docs, and unit/E2E testing.
Share bugs, ideas, or general feedback.
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 |