Skip to main content

NestJS Module Standards

Modules organize related components together. Each module groups its controllers, services, and other providers into a cohesive unit.

File Structure

Each module follows this structure:

module-name/
├── commands/ (optional - CLI commands)
│ ├── module-action.ts
│ └── module-action-detail.ts
├── dto/
│ ├── request.dto.ts
│ └── response.dto.ts
├── entities/ (if using database)
│ └── module-name.entity.ts
├── module-name.controller.ts
├── module-name.service.ts
├── module-name.error.ts
├── module-name.log.ts
└── module-name.module.ts

Basic Module

Modules import dependencies, declare controllers, and provide services.

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

// Import other modules
import { DependencyModule } from '../dependency/dependency.module';

// Import local files
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { UserEntity } from './entities/user.entity';

@Module({
imports: [
TypeOrmModule.forFeature([UserEntity]),
DependencyModule,
],
controllers: [UserController],
providers: [UserService],
exports: [UserService], // Export if other modules need it
})
export class UserModule {}

Naming Conventions

Files: kebab-case (lowercase with hyphens)

  • user.module.ts
  • user.controller.ts
  • user.service.ts

Classes: PascalCase with suffix

  • UserModule
  • UserController
  • UserService

Operations: dot notation

  • user.readMany
  • user.readOne

Common Patterns

Circular dependencies: Use forwardRef() when two modules depend on each other.

import { Module, forwardRef } from '@nestjs/common';

@Module({
imports: [
forwardRef(() => OtherModule),
],
})
export class UserModule {}

Global modules: Mark modules as global to make their exports available everywhere.

import { Module, Global } from '@nestjs/common';

@Global()
@Module({
providers: [ConfigService],
exports: [ConfigService],
})
export class ConfigModule {}

Database entities: Register entities with TypeORM using forFeature().

@Module({
imports: [
TypeOrmModule.forFeature([UserEntity, ProfileEntity]),
],
})
export class UserModule {}

RabbitMQ handlers: Register both controllers and handlers in the controllers array.

@Module({
controllers: [UserController, UserHandler], // Both go here
providers: [UserService],
})
export class UserModule {}

Anti-Patterns

❌ Don't forget to export services If other modules need your service, add it to exports.

// Bad - other modules can't use UserService
@Module({
providers: [UserService],
})

// Good - UserService available to other modules
@Module({
providers: [UserService],
exports: [UserService],
})

❌ Don't put handlers in providers RabbitMQ event handlers go in controllers, not providers.

// Bad
@Module({
controllers: [UserController],
providers: [UserService, UserHandler], // WRONG!
})

// Good
@Module({
controllers: [UserController, UserHandler], // Both here
providers: [UserService],
})

❌ Don't create circular dependencies without forwardRef Use forwardRef() for circular imports.

// Bad - circular dependency
@Module({
imports: [OtherModule], // OtherModule also imports UserModule
})

// Good - use forwardRef
@Module({
imports: [forwardRef(() => OtherModule)],
})

Other common mistakes:

  • ❌ Not importing TypeOrmModule.forFeature() for entities
  • ❌ Forgetting to register controllers in the module
  • ❌ Not using @Global() for shared utilities (logger, config, etc.)