Fullstack CourseLearn by building
Back to week 1

Topic

Modules

Definition

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. Understand modules before controllers; they tell you what exists.

After this you can

  • Explain the four @Module keys: imports, controllers, providers, exports
  • Explain TypeOrmModule.forFeature([Ticket]) inside TicketsModule
  • 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.

TicketsModule

@Module({
  imports: [TypeOrmModule.forFeature([Ticket])],
  controllers: [TicketsController],
  providers: [TicketsService],
})
export class TicketsModule {}

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.

A root app.module.ts imports things like 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.

AppModule imports

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TypeOrmModule.forRoot({ type: 'postgres', url: env.DATABASE_URL, synchronize: false /* ... */ }),
    HealthModule,
    UsersModule,
    AuthModule,
    TicketsModule,
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(CorrelationIdMiddleware).forRoutes('*');
  }
}

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

  • Understand 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.

Checking your session…

15 questions · concept 5 · syntax 3 · practical 4 · logic 3

1. What must a module list for Nest to wire it?
Concept
2. When can another module inject a provider?
Syntax
3. Is @Module optional if @Injectable exists?
Practical
4. What do imports on @Module mean?
Logic
5. What is AppModule typically?
Concept
6. Why export a service from AuthModule?
Practical
7. What happens if you forget to add a provider to providers[]?
Syntax
8. Can controllers be provided without being listed?
Logic
9. What is a shared module pattern?
Concept
10. Which claim about global modules is true?
Practical
11. OrdersModule injects AuthService from AuthModule but DI fails. What is missing?
Conceptadvanced
@Module({
  providers: [AuthService],
  // exports: []
})
export class AuthModule {}
12. To use the exported providers of AuthModule, what does OrdersModule need?
Syntaxintermediate
@Module({
  imports: [/* ? */],
  controllers: [OrdersController],
  providers: [OrdersService],
})
export class OrdersModule {}
13. Why must TicketsService be listed in providers here?
Practicalintermediate
@Module({
  controllers: [TicketsController],
  providers: [TicketsService],
})
export class TicketsModule {}
14. What does @Global() change for ConfigService?
Logicadvanced
@Global()
@Module({ providers: [ConfigService], exports: [ConfigService] })
export class ConfigModule {}
15. TicketsController routes return 404. What is missing from the module?
Conceptintermediate
@Module({
  controllers: [/* ? */],
  providers: [TicketsService],
})
export class TicketsModule {}

Checking your session…