NestJS Modules Guide

NestJS module system: feature modules, shared modules, dynamic modules, circular dependencies, and modular application architecture.

1. Feature Module

// src/articles/articles.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ArticlesController } from './articles.controller';
import { ArticlesService } from './articles.service';
import { Article } from './entities/article.entity';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [
    TypeOrmModule.forFeature([Article]),  // register entity in this scope
    UsersModule,                          // use exports from UsersModule
  ],
  controllers: [ArticlesController],
  providers: [ArticlesService],
  exports: [ArticlesService],             // expose to other modules
})
export class ArticlesModule {}

// app.module.ts
@Module({
  imports: [
    TypeOrmModule.forRoot({ ... }),
    ArticlesModule,
    UsersModule,
    AuthModule,
  ],
})
export class AppModule {}

2. Shared Module

// src/common/common.module.ts
@Global()  // available everywhere without importing
@Module({
  providers: [
    LoggerService,
    { provide: 'APP_CONFIG', useValue: { debug: true } },
  ],
  exports: [LoggerService, 'APP_CONFIG'],
})
export class CommonModule {}

// Custom provider with factory
@Module({
  providers: [
    {
      provide: 'REDIS_CLIENT',
      useFactory: async (configService: ConfigService) => {
        const client = createClient({ url: configService.get('REDIS_URL') });
        await client.connect();
        return client;
      },
      inject: [ConfigService],
    },
  ],
  exports: ['REDIS_CLIENT'],
})
export class RedisModule {}

3. Dynamic Modules

// src/mailer/mailer.module.ts
export interface MailerOptions {
  host: string;
  port: number;
  auth: { user: string; pass: string };
  from: string;
}

@Module({})
export class MailerModule {
  static register(options: MailerOptions): DynamicModule {
    return {
      module: MailerModule,
      providers: [
        { provide: 'MAILER_OPTIONS', useValue: options },
        MailerService,
      ],
      exports: [MailerService],
    };
  }

  // Async version — load from ConfigService
  static registerAsync(asyncOptions: {
    useFactory: (...args: any[]) => MailerOptions | Promise<MailerOptions>;
    inject?: any[];
  }): DynamicModule {
    return {
      module: MailerModule,
      providers: [
        {
          provide: 'MAILER_OPTIONS',
          useFactory: asyncOptions.useFactory,
          inject: asyncOptions.inject ?? [],
        },
        MailerService,
      ],
      exports: [MailerService],
    };
  }
}

// Usage
MailerModule.registerAsync({
  useFactory: (cfg: ConfigService) => ({
    host: cfg.get('SMTP_HOST'),
    port: cfg.getOrThrow<number>('SMTP_PORT'),
    auth: { user: cfg.get('SMTP_USER'), pass: cfg.get('SMTP_PASS') },
    from: cfg.get('SMTP_FROM'),
  }),
  inject: [ConfigService],
})

4. Provider Types

@Module({
  providers: [
    // Class provider (default)
    ArticlesService,

    // Explicit class provider
    { provide: ArticlesService, useClass: ArticlesService },

    // Value provider
    { provide: 'API_VERSION', useValue: 'v1' },

    // Factory provider
    {
      provide: 'RANDOM_ID',
      useFactory: () => Math.random().toString(36).slice(2),
    },

    // Factory with dependencies
    {
      provide: 'DB_POOL',
      useFactory: (cfg: ConfigService) => new Pool({ connectionString: cfg.get('DB_URL') }),
      inject: [ConfigService],
    },

    // Existing alias
    { provide: 'ALIAS', useExisting: ArticlesService },
  ],
})

5. Module Organization Pattern

src/
├── app.module.ts
├── main.ts
├── common/
│   ├── common.module.ts
│   ├── logger/
│   ├── guards/
│   ├── interceptors/
│   └── pipes/
├── config/
│   └── config.module.ts
├── database/
│   └── database.module.ts
├── auth/
│   ├── auth.module.ts
│   ├── auth.controller.ts
│   ├── auth.service.ts
│   └── strategies/
└── articles/
    ├── articles.module.ts
    ├── articles.controller.ts
    ├── articles.service.ts
    ├── dto/
    ├── entities/
    └── interfaces/

6. Module Metadata Properties

PropertyPurpose
importsOther modules to import (access their exports)
controllersRoute controllers in this module
providersServices, guards, pipes, interceptors
exportsSubset of providers available to other modules