Available Everywhere — Use Sparingly
Global Modules
`@Global()` makes a module's exports visible in every other module without an explicit import. It's a powerful escape valve, with real downsides.
What you'll learn
- Mark a module @Global to expose its providers everywhere
- Recognize the trade-off (loose coupling becomes implicit)
- Pick the right candidates — config, logger, database
By default, a module can only inject providers from modules it explicitly
imports. @Global() lifts that rule — once the global module is imported
once anywhere, its exports are available everywhere.
Marking a Module Global
Add the decorator above @Module. That’s it.
import { Global, Module } from '@nestjs/common';
@Global()
@Module({
providers: [LoggerService],
exports: [LoggerService],
})
export class LoggerModule {} Now any feature can inject LoggerService without listing LoggerModule in
its imports:
@Module({
controllers: [UsersController],
providers: [UsersService], // UsersService can inject LoggerService for free
})
export class UsersModule {} You still have to import the global module once in the root (AppModule)
to register it.
Dynamic + Global
Most real-world global modules are also dynamic — they take config in
forRoot(). Set global: true on the returned DynamicModule:
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
global: true,
providers: [{ provide: 'CONFIG_OPTS', useValue: options }, ConfigService],
exports: [ConfigService],
};
} The Trade-Off
Global modules feel like a free lunch — fewer imports, less typing. The hidden cost: dependencies become invisible. Open a feature module’s file and you can’t tell what it really uses; tooling can’t either.
That makes a few things harder:
- Refactoring. You can’t safely delete a “global” provider without searching the whole codebase.
- Testing. Test modules don’t get the global registration automatically; you’ll need to add it explicitly anyway.
- Onboarding. New developers see a service appear out of thin air.
When Global Makes Sense
A small, well-defined list:
| Module | Why global is OK |
|---|---|
ConfigModule | Truly every feature needs it |
LoggerModule | Same — and you want a uniform interface |
DatabaseModule | One connection, used everywhere |
EventEmitterModule | Cross-cutting pub/sub |
If you find yourself reaching for @Global on a feature module, that’s a
sign the dependency boundary isn’t right yet — extract the truly shared
bits and keep the feature module non-global.