Architecture
This document describes the high-level architecture of the Awesome NestJS Boilerplate, including design patterns, project structure, and implementation details.
- Architecture
Project Structure
awesome-nest-boilerplate/
├── src/
│ ├── common/ # Shared components and utilities
│ │ ├── dto/ # Common DTOs (PageDto, AbstractDto)
│ │ └── abstract.entity.ts # Base entity with common fields
│ ├── constants/ # Application-wide constants
│ │ └── role-type.ts # User role enumeration
│ ├── database/ # Database configuration and migrations
│ │ └── migrations/ # TypeORM migration files
│ ├── decorators/ # Custom decorators
│ │ ├── auth-user.decorator.ts # Extract authenticated user
│ │ ├── http.decorators.ts # HTTP-related decorators
│ │ └── use-dto.decorator.ts # Entity-DTO mapping
│ ├── entity-subscribers/ # TypeORM entity subscribers
│ │ └── user-subscriber.ts # Password hashing subscriber
│ ├── exceptions/ # Custom exception classes
│ ├── filters/ # Exception filters
│ │ ├── bad-request.filter.ts # HTTP exception filter
│ │ └── query-failed.filter.ts # Database error filter
│ ├── guards/ # Authentication and authorization guards
│ ├── i18n/ # Internationalization files
│ │ ├── en_US/ # English translations
│ │ └── ru_RU/ # Russian translations
│ ├── interceptors/ # Request/Response interceptors
│ │ ├── translation-interceptor.service.ts
│ │ └── language-interceptor.service.ts
│ ├── interfaces/ # TypeScript interfaces
│ ├── modules/ # Feature modules
│ │ ├── auth/ # Authentication module
│ │ ├── user/ # User management module
│ │ ├── post/ # Post management module
│ │ └── health-checker/ # Health check module
│ ├── providers/ # Custom providers
│ ├── shared/ # Shared services and utilities
│ │ └── services/ # Global services (ApiConfigService, TranslationService)
│ ├── validators/ # Custom validators
│ ├── app.module.ts # Root application module
│ ├── main.ts # Application entry point
│ └── setup-swagger.ts # Swagger configuration
├── test/ # E2E tests
├── docs/ # Documentation
├── docker-compose.yml # Docker development setup
└── package.json # Dependencies and scriptsCore Concepts
Module Organization
Each feature module follows a consistent structure based on Domain-Driven Design principles:
modules/feature/
├── commands/ # CQRS command handlers
│ └── create-feature/
│ ├── create-feature.command.ts
│ └── create-feature.handler.ts
├── queries/ # CQRS query handlers
│ └── get-feature/
│ ├── get-feature.query.ts
│ └── get-feature.handler.ts
├── dtos/ # Data Transfer Objects
│ ├── create-feature.dto.ts
│ ├── update-feature.dto.ts
│ └── feature.dto.ts
├── exceptions/ # Module-specific exceptions
│ └── feature-not-found.exception.ts
├── feature.controller.ts # HTTP route handlers
├── feature.service.ts # Business logic
├── feature.module.ts # Module configuration
├── feature.entity.ts # Database entity
└── feature-related.entity.ts # Related entitiesExample: User Module Structure
typescript
// user.entity.ts
@Entity({ name: 'users' })
@UseDto(UserDto)
export class UserEntity extends AbstractEntity<UserDto, UserDtoOptions> {
@Column({ nullable: true, type: 'varchar' })
firstName!: string | null;
@Column({ unique: true, nullable: true, type: 'varchar' })
email!: string | null;
@Column({ type: 'enum', enum: RoleType, default: RoleType.USER })
role!: RoleType;
@OneToMany(() => PostEntity, (postEntity) => postEntity.user)
posts?: PostEntity[];
}Design Patterns
1. CQRS (Command Query Responsibility Segregation)
Separates read and write operations for better scalability and maintainability:
typescript
// Command for state changes
export class CreatePostCommand extends Command {
constructor(
public readonly userId: Uuid,
public readonly createPostDto: CreatePostDto,
) {
super();
}
}
// Query for data retrieval
export class GetPostQuery extends Query {
constructor(public readonly userId: Uuid) {
super();
}
}2. Repository Pattern
Implemented through TypeORM repositories with dependency injection:
typescript
@Injectable()
export class PostService {
constructor(
@InjectRepository(PostEntity)
private postRepository: Repository<PostEntity>,
private commandBus: CommandBus,
) {}
@Transactional()
createPost(userId: Uuid, createPostDto: CreatePostDto): Promise<PostEntity> {
return this.commandBus.execute<CreatePostCommand, PostEntity>(
new CreatePostCommand(userId, createPostDto),
);
}
}3. Dependency Injection
Leverages NestJS's powerful DI container for loose coupling and testability.
4. Entity-DTO Mapping
Uses custom decorators for automatic entity-to-DTO conversion:
typescript
@Entity({ name: 'posts' })
@UseDto(PostDto)
export class PostEntity extends AbstractEntity<PostDto> {
// Entity properties
}Database Layer
TypeORM Configuration
- Data Mapper Pattern: Separates entity definition from persistence logic
- Snake Case Naming Strategy: Converts camelCase to snake_case for database columns
- Migration System: Version-controlled database schema changes
- Entity Subscribers: Automatic operations (e.g., password hashing)
Entity Relationships
typescript
// One-to-Many relationship
@OneToMany(() => PostEntity, (postEntity) => postEntity.user)
posts?: PostEntity[];
// Many-to-One with cascade options
@ManyToOne(() => UserEntity, (userEntity) => userEntity.posts, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'user_id' })
user!: Relation<UserEntity>;Transaction Support
typescript
@Transactional()
async createPost(userId: Uuid, createPostDto: CreatePostDto): Promise<PostEntity> {
// All database operations within this method are wrapped in a transaction
}Authentication & Authorization
JWT-based Authentication
- Stateless authentication using JSON Web Tokens
- Configurable expiration times
- Secure token signing and verification
Role-based Access Control (RBAC)
typescript
@Auth([RoleType.ADMIN, RoleType.USER])
@Get(':id')
async getUser(@UUIDParam('id') userId: Uuid): Promise<UserDto> {
return this.userService.getUser(userId);
}Custom Decorators
typescript
// Extract authenticated user from request
@AuthUser() user: UserEntity
// Validate UUID parameters
@UUIDParam('id') userId: UuidAPI Documentation
Swagger Integration
- Auto-generated API documentation
- Request/response examples
- DTO validation documentation
- Available at
/documentationendpoint
typescript
@ApiTags('users')
@ApiOkResponse({ type: UserDto, description: 'User retrieved successfully' })
@Get(':id')
async getUser(@UUIDParam('id') userId: Uuid): Promise<UserDto> {
return this.userService.getUser(userId);
}Error Handling
Global Exception Filters
typescript
// HTTP exception handling with i18n support
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
// Handle and format exceptions consistently
}
}Custom Exceptions
typescript
export class UserNotFoundException extends NotFoundException {
constructor(error?: string) {
super('error.user_not_found', error);
}
}Validation
DTO Validation with Custom Decorators
typescript
export class CreateUserDto {
@EmailField()
readonly email!: string;
@StringField({ minLength: 6 })
readonly password!: string;
@PhoneFieldOptional()
readonly phone?: string;
}Custom Validation Decorators
@EmailField(): Email validation with Swagger documentation@StringField(): String validation with length constraints@NumberField(): Number validation with range constraints@BooleanField(): Boolean validation@UUIDField(): UUID validation
Internationalization (i18n)
Multi-language Support
- Nested translation files
- Dynamic language switching via headers/query parameters
- Translation interpolation and formatting
typescript
// Translation usage in exceptions
throw new UserNotFoundException('error.user_not_found');
// Translation in DTOs and responses
@DynamicTranslate()
title?: string;Testing
Testing Strategy
- Unit Tests: Individual component testing with mocks
- Integration Tests: Module-level testing with real dependencies
- E2E Tests: Full application flow testing
Test Structure
typescript
describe('UserService', () => {
let service: UserService;
let repository: Repository<UserEntity>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(UserEntity),
useValue: mockRepository,
},
],
}).compile();
service = module.get<UserService>(UserService);
});
});Security
Built-in Security Features
- CORS Configuration: Configurable cross-origin resource sharing
- Helmet Middleware: Security headers protection
- Rate Limiting: Request throttling with configurable limits
- Input Validation: Comprehensive DTO validation
- SQL Injection Prevention: TypeORM query parameterization
Environment-based Configuration
typescript
export class ApiConfigService {
get corsOrigins(): string[] {
return this.getString('CORS_ORIGINS')?.split(',') || ['http://localhost:3000'];
}
get throttlerConfigs(): ThrottlerOptions {
return {
ttl: this.getNumber('THROTTLE_TTL', 60),
limit: this.getNumber('THROTTLE_LIMIT', 10),
};
}
}Technology Stack
Core Technologies
- NestJS: Progressive Node.js framework
- TypeScript: Type-safe JavaScript development
- TypeORM: Object-relational mapping with PostgreSQL
- Vite: Fast development build tool
- Jest: Testing framework
Development Tools
- Biome + ESLint: Code linting and formatting
- Husky: Git hooks for code quality
- Docker: Containerized development environment
- Swagger: API documentation generation
Runtime Support
- Node.js: Primary runtime environment
- Bun: High-performance alternative runtime
- Deno: Secure TypeScript runtime
Best Practices
Code Organization
- Single Responsibility Principle: Each class/module has one clear purpose
- Dependency Injection: Use NestJS DI for loose coupling
- Type Safety: Leverage TypeScript features extensively
- Consistent Naming: Follow established naming conventions
Performance
- Database Optimization: Use indexes, query optimization, and pagination
- Caching Strategies: Implement appropriate caching layers
- Lazy Loading: Load related entities only when needed
- Connection Pooling: Efficient database connection management
Code Quality Standards
- Remove Unused Code: Eliminate dead code and unused imports
- Simple Code Style: Write clear, readable code (KISS principle)
- No Passthrough Functions: Avoid unnecessary function wrappers
- DRY Principle: Extract common functionality into reusable components
- Early Returns: Reduce nesting with early return statements
- Explicit Error Handling: Handle errors explicitly and meaningfully
Security
- Input Validation: Validate all incoming data
- Authentication: Secure JWT implementation
- Authorization: Role-based access control
- Environment Variables: Secure configuration management
Development Workflow
Version Control
- Feature Branch Workflow: Develop features in separate branches
- Conventional Commits: Use meaningful commit messages
- Pull Request Reviews: Code review process
- Automated CI/CD: Continuous integration and deployment
Code Quality
- Pre-commit Hooks: Automated linting and formatting
- Test Coverage: Maintain high test coverage
- Documentation: Keep documentation up-to-date
- Performance Monitoring: Track application performance
Deployment
- Docker Support: Containerized deployment
- Environment Configurations: Environment-specific settings
- Database Migrations: Automated schema updates
- Health Checks: Application health monitoring