iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
NestJS providers are private to the module that declares them unless that module exports them. To inject a provider from another module, export it from its host module and import that host module in the consumer. The module’s exports define its public API; a TypeScript import statement alone does not make a provider available to Nest’s dependency-injection system.
NestJS module encapsulation at a glance
A class decorated with @Module() describes a part of the application graph through its providers, controllers, imports, and exports. Nest encapsulates providers by default: components within a module can use providers declared there, but another module cannot use them unless the host exposes them and the consumer imports the host.
| What you need | Pattern | What to keep in mind |
|---|---|---|
| Use a provider inside its feature | Declare it in that module’s providers. |
It is available to the module’s components by default. |
| Inject a provider from another feature | Export it from its host module, then import that module in the consumer. | Exports define the host module’s public surface. |
| Share one provider instance | Export the provider from a shared module and import that module where needed. | Consumers can use the shared instance; independently registering the class creates separate instances. |
| Expose a custom provider | Put its token or provider object in exports. |
A custom provider remains scoped to its declaring module until exported. |
| Reduce repeated imports | Make a module global and register it once, typically from a root or core module. | Convenient, but dependencies are less visible at their points of use. |
| Configure providers at runtime | Use a dynamic module, commonly with a method such as forRoot(). |
Runtime configuration does not remove the usual export-and-import visibility rules. |
| Expose generated database providers | Re-export the integration module from the feature module. | The TypeORM guide demonstrates re-exporting TypeOrmModule for providers created with forFeature(). |
How do I share a provider between NestJS modules?
Declare the service in its owning module, export it there, and import that module wherever the service is needed. For example:
@Module({
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
@Module({
imports: [CatsModule],
providers: [OrdersService],
})
export class OrdersModule {}
In this arrangement, OrdersService can inject CatsService because CatsModule exports it and OrdersModule imports CatsModule. The provider is not exposed merely because the TypeScript file for OrdersService can import the CatsService symbol. Nest’s module metadata establishes dependency-injection visibility. See the official Modules documentation.
#1 Best Overall
What belongs in a module’s exports?
Treat exports as an intentional public interface. Export services or tokens that other modules are meant to depend on; leave implementation details out. This keeps consumers coupled to the feature’s supported surface rather than to internal providers that may change.
For a custom provider, export either its injection token or the provider object, as appropriate to how consumers resolve it. For example, if a consumer injects a string or symbol token, that token must be available through the host module’s exports. The official Custom providers documentation describes exporting custom providers by token or provider object.
Sharing a provider versus registering it more than once
When consumers should use one common service instance, have them import a shared host module that exports the provider. Nest modules are shared by default, so importing consumers can use that shared provider instance. Registering the same service class independently in multiple modules instead creates separate instances. If the service holds state, those instances can diverge; separate registrations can also use more memory.
This is a difference in provider registration, not just code organization: keep one registration in the host module and expose it to consumers through the host’s exports.
Rank #3
Explicit imports or a global module?
| Approach | Dependency visibility | Boilerplate | Following the application graph |
|---|---|---|---|
| Explicit imports | Consumers list the modules they depend on. | Requires imports where needed. | Relationships are easier to see in each module. |
| Global module | Consumers can inject exported providers without listing the global module in each imports array. |
Reduces repeated imports. | Dependencies are less apparent where they are used. |
A global module still exposes providers through its exports; global scope does not make every provider public. Nest recommends registering a global module once, generally from the root or core module, and advises against making everything global. Use this option as a limited convenience for widely used infrastructure, not as the default for feature services. See NestJS modules.
Dynamic modules do not bypass encapsulation
A dynamic module returns module metadata configured at runtime. A common pattern is FeatureModule.forRoot(options), which lets an importing module provide configuration. Providers still follow the same boundary: they are available within their declaring module, and providers needed outside it must be exported by the host and made available through imports.
Rank #4
Do not assume that calling forRoot() in multiple places is automatically harmless or always appropriate. Follow the registration pattern for the particular integration and application. Nest’s Dynamic modules documentation explains runtime module configuration and the standard visibility model.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsRe-exporting modules and integration-generated providers
A module can re-export a module it imports. This lets a higher-level feature module offer a deliberate public surface that includes selected capabilities from an integration module, rather than requiring every consumer to know the lower-level registration details.
Best Value
For example, Nest’s TypeORM guide shows importing TypeOrmModule.forFeature([Entity]) and exporting TypeOrmModule so consuming modules can use the generated repository providers. Follow the integration’s documented registration rules for the NestJS and library versions in your project.
Quick checks when injection fails
- Confirm the provider is registered in a module’s
providersor is supplied by an imported integration module. - Confirm the provider’s host module includes the provider or its token in
exports. - Confirm the consuming module lists the host module in
imports, unless the host is global. - For a custom provider, check that the exported token matches the token the consumer injects.
- Check whether the provider was registered separately in multiple modules when one shared instance was intended.
- For dynamic modules or generated providers, verify the relevant integration’s documented registration and export pattern.
For the general provider model, see NestJS’s Providers documentation.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

