A Nest module is a class annotated with @Module that declares the controllers and providers belonging to a cohesive part of the application, and the imports/exports that make dependencies available across module boundaries.
In simpler words
A module is a labeled box: it says which controllers and services live here, what it needs from elsewhere, and what it is willing to share.
Every Nest feature — tickets, auth, health — is a module. AppModule is the root module that assembles the whole app. Read modules before controllers; they tell you what exists.
Trace how AppModule assembles Health/Users/Auth/Tickets modules
Know when a provider must be exported
@Module anatomy
Definition
The @Module decorator accepts a metadata object with optional imports, controllers, providers, and exports arrays that Nest reads to build the application’s dependency graph.
In simpler words
imports = other modules this one needs; controllers = routes it exposes; providers = services it owns; exports = which of its providers others may use.
Nest reads @Module metadata at bootstrap to construct a dependency-injection graph — it does not scan folders or filenames, only decorator metadata.
A provider not listed in providers cannot be injected here, even if the class exists in the same folder. A provider not listed in exports cannot be injected by a different module that imports this one.
forFeature([Ticket]) registers a TypeORM Repository<Ticket> that TicketsService can inject.
The root module wires the whole app
Definition
A root module is the single module passed to NestFactory.create that transitively imports every feature module the application needs.
In simpler words
AppModule is the top of the tree — if a module is not (directly or indirectly) imported by AppModule, Nest never loads it.
apps/api/src/app.module.ts imports ConfigModule, TypeOrmModule.forRoot(...), HealthModule, UsersModule, AuthModule, and TicketsModule — that list is the entire backend surface.
AppModule also implements NestModule to register CorrelationIdMiddleware across every route via configure(consumer) — middleware registration is a module-level concern, separate from providers/controllers.
Add a new feature module here and its controllers become reachable; forget this step and its routes 404.
Exporting providers across module boundaries
Definition
A module makes a provider available to importing modules by listing that provider in its exports array; the importing module must also import the exporting module.
In simpler words
To borrow a service from another module, that module must export it and your module must import it — both sides are required.
If AuthModule needs UsersService to look up credentials, UsersModule must export UsersService and AuthModule must import UsersModule.
Forgetting exports is a very common first bug: the provider exists and even compiles, but Nest throws a runtime "cannot resolve dependencies" error, because the DI container cannot see it outside its owning module.
Mistake: missing export
// Wrong — UsersService not exported
@Module({ providers: [UsersService] })
export class UsersModule {}
// Right
@Module({ providers: [UsersService], exports: [UsersService] })
export class UsersModule {}
Without exports, importing UsersModule elsewhere still cannot inject UsersService.
Keep in mind
Read a feature by its module first: imports tell you dependencies, controllers/providers tell you what it does.
Circular module imports usually mean a boundary is drawn in the wrong place — extract a shared module.
Keep feature modules focused: tickets should not import unrelated modules “just in case”.
Test
Check your understanding
At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥ 80% (enforced by the API) to save progress.