NestJS Modules
A module in NestJS is a container that groups related code together. Every NestJS application has at least one module — the root module — and most applications have many. Modules keep your codebase organized as it grows and make individual features easy to test, maintain, and reuse.
The Module as a Box
Picture a post office. The building has separate sections: one for sorting packages, one for managing staff, one for customer service. Each section has its own tools, people, and responsibilities. It can operate independently, but all sections work together to run the post office.
NestJS modules work exactly like those sections. Each module owns its controllers, services, and other components. Modules connect to each other through a formal registration system — no accidental sharing or unintended coupling.
Anatomy of a Module
A module is a TypeScript class decorated with @Module(). The decorator accepts an object with four optional properties:
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
imports: [], // Other modules this module depends on
controllers: [], // Controllers belonging to this module
providers: [], // Services and other providers
exports: [], // Providers to share with other modules
})
export class UsersModule {}
imports
List modules this module needs. For example, if UsersModule needs the AuthModule to verify tokens, you add AuthModule to the imports array.
controllers
List all controllers that handle HTTP routes for this module. The module registers them so NestJS knows they exist.
providers
List all services, repositories, and other injectable classes that belong to this module. NestJS's dependency injection system uses this list to create and share instances.
exports
List providers that other modules can use. If UsersModule defines a UsersService and another module needs it, you add UsersService to exports.
The Root Module
Every application starts with a root module — AppModule. This is the single module that main.ts loads. All other modules register inside AppModule through its imports array.
App Structure Diagram:
main.ts
└── AppModule (root)
├── UsersModule
│ ├── UsersController
│ └── UsersService
├── ProductsModule
│ ├── ProductsController
│ └── ProductsService
└── AuthModule
├── AuthController
└── AuthService
Each feature module plugs into the root. The root module acts as the application's table of contents.
Creating a Module with the CLI
Generate a module instantly using the NestJS CLI:
nest g module users
The CLI creates src/users/users.module.ts and automatically adds UsersModule to the imports array of AppModule. You do not need to register it manually.
Feature Modules
A feature module groups everything related to one business concept. The UsersModule owns everything about users — user controller, user service, user entities, and user DTOs. Nothing from ProductsModule bleeds into UsersModule unless explicitly imported.
This separation makes debugging straightforward. If a user-related bug appears, you open the users folder and look there only. You do not hunt through the entire project.
Shared Modules
Sometimes one service must be available across many modules. Instead of duplicating it, you create a shared module that exports that service. Any module that imports the shared module can then inject the shared service.
SharedModule
└── exports: [LoggerService]
UsersModule
└── imports: [SharedModule]
→ UsersService can now inject LoggerService
ProductsModule
└── imports: [SharedModule]
→ ProductsService can now inject LoggerService
Shared modules eliminate duplication and keep a single source of truth for common utilities like logging, email sending, or file handling.
Global Modules
For services used everywhere — like a configuration service or a database connection — you can mark a module as global using the @Global() decorator. A global module registers once, and every other module can use its exports without importing it explicitly.
import { Global, Module } from '@nestjs/common';
@Global()
@Module({
providers: [ConfigService],
exports: [ConfigService],
})
export class ConfigModule {}
Use global modules sparingly. Overusing them defeats the purpose of explicit module boundaries and makes the codebase harder to understand at a glance.
Why Modules Matter
Modules are the backbone of every NestJS application. They enforce boundaries between features, make dependencies explicit, and enable teams to work on separate parts of the application without stepping on each other. The more deliberately you design your modules, the easier your application becomes to scale and maintain over time.
