Developer-kit nestjs-best-practices
Provides comprehensive NestJS best practices including modular architecture, dependency injection scoping, exception filters, DTO validation with class-validator, and Drizzle ORM integration. Use when designing NestJS modules, implementing providers, creating exception filters, validating DTOs, or integrating Drizzle ORM within NestJS applications.
git clone https://github.com/giuseppe-trisciuoglio/developer-kit
T=$(mktemp -d) && git clone --depth=1 https://github.com/giuseppe-trisciuoglio/developer-kit "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/developer-kit-typescript/skills/nestjs-best-practices" ~/.claude/skills/giuseppe-trisciuoglio-developer-kit-nestjs-best-practices && rm -rf "$T"
plugins/developer-kit-typescript/skills/nestjs-best-practices/SKILL.mdNestJS Best Practices
Overview
Grounded in the Official NestJS Documentation, this skill enforces modular architecture, dependency injection scoping, exception filters, DTO validation with
class-validator, and Drizzle ORM integration patterns.
When to Use
- Designing/refactoring NestJS modules or dependency injection
- Creating exception filters, validating DTOs, or integrating Drizzle ORM
- Reviewing code for anti-patterns or onboarding to a NestJS codebase
Instructions
1. Modular Architecture
Follow strict module encapsulation. Each domain feature should be its own
@Module():
- Export only what other modules need — keep internal providers private
- Use
only as a last resort for circular dependencies; prefer restructuringforwardRef() - Group related controllers, services, and repositories within the same module
- Use a
for cross-cutting concerns (logging, configuration, caching)SharedModule
See
references/arch-module-boundaries.md for enforcement rules.
2. Dependency Injection
Choose the correct provider scope based on use case:
| Scope | Lifecycle | Use Case |
|---|---|---|
| Singleton (shared) | Stateless services, repositories |
| Per-request instance | Request-scoped data (tenant, user context) |
| New instance per injection | Stateful utilities, per-consumer caches |
- Default to
scope — only useDEFAULT
orREQUEST
when justifiedTRANSIENT - Use constructor injection exclusively — avoid property injection
- Register custom providers with
,useClass
,useValue
, oruseFactoryuseExisting
See
references/di-provider-scoping.md for enforcement rules.
3. Request Lifecycle
Understand and respect the NestJS request processing pipeline:
Middleware → Guards → Interceptors (before) → Pipes → Route Handler → Interceptors (after) → Exception Filters
- Middleware: Cross-cutting concerns (logging, CORS, body parsing)
- Guards: Authorization and authentication checks (return
/true
)false - Interceptors: Transform response data, add caching, measure timing
- Pipes: Validate and transform input parameters
- Exception Filters: Catch and format error responses
4. Error Handling
Standardize error responses across the application:
- Extend
for HTTP-specific errorsHttpException - Create domain-specific exception classes (e.g.,
)OrderNotFoundException - Implement a global
for consistent error formattingExceptionFilter - Use the Result pattern for expected business logic failures
- Never silently swallow exceptions
See
references/error-exception-filters.md for enforcement rules.
5. Validation
Enforce input validation at the API boundary:
- Enable
globally withValidationPipe
andtransform: truewhitelist: true - Decorate all DTO properties with
decoratorsclass-validator - Use
for type coercion (class-transformer
,@Type()
)@Transform() - Create separate DTOs for Create, Update, and Response operations
- Never trust raw user input — validate everything
See
references/api-validation-dto.md for enforcement rules.
6. Database Patterns (Drizzle ORM)
Integrate Drizzle ORM following NestJS provider conventions:
- Wrap the Drizzle client in an injectable provider
- Use the Repository pattern for data access encapsulation
- Define schemas in dedicated schema files per domain module
- Use transactions for multi-step operations
- Keep database logic out of controllers
See
references/db-drizzle-patterns.md for enforcement rules.
Best Practices
| Area | Do | Don't |
|---|---|---|
| Modules | One module per domain feature | Dump everything in |
| DI Scoping | Default to singleton scope | Use scope without justification |
| Error Handling | Custom exception filters + domain errors | Bare with |
| Validation | Global + DTO decorators | Manual checks in controllers |
| Database | Repository pattern with injected client | Direct DB queries in controllers |
| Testing | Unit test services, e2e test controllers | Skip tests or test implementation details |
| Configuration | with typed schemas | Hardcode values or use |
Examples
Example: New Domain Module with Validation
When building a "Product" feature, follow this workflow:
1. Create the module with proper encapsulation:
// product/product.module.ts @Module({ imports: [DatabaseModule], controllers: [ProductController], providers: [ProductService, ProductRepository], exports: [ProductService], // Only export what others need }) export class ProductModule {}
2. Create validated DTOs:
// product/dto/create-product.dto.ts import { IsString, IsNumber, IsPositive, MaxLength } from 'class-validator'; export class CreateProductDto { @IsString() @MaxLength(255) readonly name: string; @IsNumber() @IsPositive() readonly price: number; }
3. Service with error handling:
@Injectable() export class ProductService { constructor(private readonly productRepository: ProductRepository) {} async findById(id: string): Promise<Product> { const product = await this.productRepository.findById(id); if (!product) throw new ProductNotFoundException(id); return product; } }
4. Verify module registration:
# Check module is imported in AppModule grep -r "ProductModule" src/app.module.ts # Run e2e to confirm exports work npx jest --testPathPattern="product"
Constraints and Warnings
- Do not mix scopes without justification —
-scoped providers cascade to all dependentsREQUEST - Never access database directly from controllers — always go through service and repository layers
- Avoid
— restructure modules to eliminate circular dependenciesforwardRef() - Do not skip
— always validate at the API boundary with DTOsValidationPipe - Never hardcode secrets — use
with environment variables@nestjs/config - Keep modules focused — one domain feature per module, avoid "god modules"
References
— Deep-dive into NestJS architectural patternsreferences/architecture.md
— Individual enforcement rules with correct/incorrect examplesreferences/
— Starter templates for common NestJS componentsassets/templates/