Modules — Nest's Composition Unit

Group Related Code Into a Self-Contained Unit

Modules — Nest's Composition Unit

A module is a class decorated with `@Module` that organizes a feature's controllers, providers, and dependencies.

4 min read Level 2/5 #nestjs#modules
What you'll learn
  • Create a module with `@Module`
  • Understand imports, controllers, providers, and exports
  • Import one module into another

A module is how Nest groups related code. Every Nest app has at least one (the root AppModule), and feature modules are how you scale up without turning your codebase into a spaghetti.

The Anatomy of @Module

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  imports: [],            // other modules this one depends on
  controllers: [UsersController],  // HTTP entry points
  providers: [UsersService],       // injectable classes
  exports: [UsersService],         // things other modules can import
})
export class UsersModule {}

Four arrays, each with a job:

  • imports — other modules whose exports you want to use.
  • controllers — classes that handle HTTP routes for this feature.
  • providers — services, repositories, factories — anything injectable.
  • exports — providers that other modules can use after importing this one.

Wiring a Feature Module Into the Root

Generate the feature, then add it to AppModule.imports:

nest g module users
nest g controller users
nest g service users
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';

@Module({
  imports: [UsersModule],
})
export class AppModule {}

Now everything inside UsersModule boots with the app, and any provider listed in its exports is injectable from anywhere that imports it.

Encapsulation Is Real

A provider that isn’t exported stays private. If UsersService isn’t in UsersModule’s exports, no other module can inject it — even if they import UsersModule. This is the same encapsulation you’d build by hand with folders and barrel files, except enforced by the framework.

Shared & Dynamic Modules

For cross-cutting concerns (config, database, logger) you make a shared module and import it everywhere. Some modules expose a static method like forRoot() to accept configuration — those are called dynamic modules. We’ll see them when we wire up databases.

Controllers — HTTP Request Handlers →