NestJS providers are private to the module that declares them unless that module exports them. To use a provider in another module, export it from its host module and import that host module in the consumer. The exports list is the host module’s public API; a TypeScript import statement alone does not grant dependency-injection access.
How NestJS module encapsulation works
A class annotated with @Module() describes a part of the application graph through its providers, controllers, imports and exports. A provider declared in a module is available to that module’s components by default. Other modules cannot inject it just because its class is reachable in TypeScript: Nest’s module metadata controls dependency-injection visibility.
As an Amazon Associate I earn from qualifying purchases.
The practical rule is: the host module exports the provider, and the consuming module imports the host module. Nest describes exported providers as a module’s public interface. See the official Modules documentation and its Dynamic modules documentation.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Quick reference: choose the right sharing pattern
| Need | Pattern | What it means |
|---|---|---|
| Use a provider within its feature | Declare it in that module’s providers |
It is available to that module’s components by default. |
| Inject a provider from another feature | Export it from its host module; import the host module in the consumer | The host controls its public surface, and the consumer declares the dependency. |
| Share one provider instance | Export the provider from a shared module and import that module where needed | Consumers can use the provider instance supplied through the shared module. |
| Expose a custom provider | Add its token or provider object to exports |
Consumers can inject a custom provider through its exported token or object. |
| Reduce repeated imports for widely used infrastructure | Register a global module once, commonly from the root or core module | Consumers need not list that global module in each imports array, but its exports still define what it exposes. |
| Configure providers at runtime | Use a dynamic module, often through a method such as forRoot() |
Runtime configuration changes module metadata; it does not remove the normal export-and-import visibility rules. |
| Expose generated database providers through a feature | Re-export the integration module from the feature module | Nest’s TypeORM guide demonstrates re-exporting TypeOrmModule for providers created with forFeature(). |
How to share a provider between NestJS modules
Keep implementation providers private unless another module has a genuine reason to depend on them. Export only the service or token that forms the feature’s intended interface. In this conceptual example, OrdersService can inject CatsService because CatsModule exports it and OrdersModule imports CatsModule:
#1 Best Overall
@Module({
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
@Module({
imports: [CatsModule],
providers: [OrdersService],
})
export class OrdersModule {}
If either side of that relationship is missing, the intended cross-module dependency is not established: exporting makes the provider available beyond its host, while importing the host makes that exported provider available to the consumer. Nest’s module guide documents this pattern.
Shared provider instance versus repeated registration
Nest modules are shared by default. When a module exports a provider and consumers import that module, those consumers can use the shared provider instance. Registering the same service class independently in several modules is different: each registration creates a separate instance. If the service holds state, those instances may have different internal state; separate registrations can also use more memory.
When consumers should use one common service instance, put its registration in a shared host module, export it there, and import that module where needed. Avoid copying the service into each consumer’s providers array.
Explicit imports or a global module?
| Approach | Dependency visibility | Boilerplate | Best fit |
|---|---|---|---|
| Explicit imports | The consumer’s imports array shows which module it depends on. |
Consumers repeat the module import where needed. | Most feature-to-feature dependencies, especially when keeping the application graph easy to follow matters. |
| Global module | Consumers can use exported providers without listing the global module in each imports array, so dependencies are less apparent at the point of use. |
Fewer repeated imports; register the global module once, typically in the root or core module. | A limited set of widely used infrastructure services where the convenience justifies less explicit relationships. |
A global module does not make every provider public: its exports still determine what it exposes. Nest recommends avoiding a design in which everything is global, because explicit imports make module relationships easier to see and maintain. See NestJS Modules.
Rank #3
Custom provider tokens and exports
A custom provider is scoped to its declaring module until exported. If consumers inject a string or symbol token instead of a class, export that token; you can also export the provider object. This lets the host expose the dependency by the identifier consumers actually use. The official Custom providers guide covers provider tokens and exports.
Dynamic modules and runtime configuration
A dynamic module returns module metadata configured at runtime. A common API has a shape such as FeatureModule.forRoot(options), with options supplied by the importing module. Dynamic configuration does not make providers automatically visible everywhere: providers needed outside their host still need to be exported and made available to consumers through the module graph.
Rank #4
Do not assume that calling a configuration method such as forRoot() in multiple places is always harmless or appropriate. Follow the registration rules for the particular integration and the versions used by the application. Nest’s Dynamic modules guide explains the pattern.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Re-exporting an imported module
A module can import another module and re-export it. This lets a feature module offer a curated public surface without asking every consumer to import the lower-level integration directly. For example, Nest’s TypeORM guide shows a feature importing TypeOrmModule.forFeature([Entity]) and exporting TypeOrmModule so consumers can use the repository providers generated for that feature.
Best Value
When injection fails, check the module boundary
- Provider is not available in the consumer: confirm that the declaring module lists it in
providers, that the host exports it, and that the consumer imports the host module. - A class import exists, but injection still fails: check the Nest module metadata. A TypeScript
importonly makes a symbol available to the source file; it does not replace a Nest module import or export. - Unexpected separate state or duplicate work: check whether the service was registered independently in multiple modules rather than exported from one shared host.
- A custom provider cannot be injected by its token: verify that the host exports the token or provider object used by consumers.
- An integration’s generated provider is missing: check the integration’s documented registration and re-export pattern, such as the TypeORM
forFeature()example, against the project’s library version.
For any integration or version-specific registration behavior, verify the current guidance in Nest’s Providers documentation and the relevant integration guide.
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.




