A module is a class with @Module() on it, and its four arrays are the whole model: providers are the classes it can construct, controllers own routes, imports are modules whose exports become visible here, and exports are what this module offers to importers. A provider that is not exported is private — Nest's only encapsulation boundary.
@Module({ imports: [AuthModule], // AuthModule exports JwtModule, needed by the guard
controllers: [BooksController], providers: [BooksService] })
export class BooksModule {}
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true, cache: true, validate: envSchema }),
DataModule, BooksModule, AuthModule],
providers: [{ provide: APP_FILTER, useClass: ProblemFilter },
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }] })
export class AppModule implements NestModule {
configure(c: MiddlewareConsumer) { c.apply(RequestIdMiddleware).forRoutes('*'); } }The APP_FILTER and APP_INTERCEPTOR tokens matter: registering this way makes a filter or interceptor global and leaves it inside the injector, so it can have constructor dependencies, where app.useGlobalFilters(new ProblemFilter()) builds the object outside the container. One store instance is shared by books and auth, so DataModule is @Global() @Module({ providers: [Store], exports: [Store] }), imported once in AppModule. Skip that and Nest tells you what is missing at boot:
ERROR [ExceptionHandler] UnknownDependenciesException [Error]: Nest can't resolve dependencies of the BooksService (?). Please make sure that the argument Store at index [0] is available in the BooksModule module. - If Store is exported from a separate @Module, is that module imported within BooksModule?
That is the most common message you will read while learning Nest, and it is always literally true: the class in (?) is not visible from the module that needs it. Reach for @Global() rarely — a database connection, a config service, a logger — because making feature modules global destroys the only encapsulation Nest gives you.