Code example
Third party library@Module({
providers: [AService]
exports: [AService]
})
export class AModule {
}
@Module({
imports: [AModule],
providers: [BService]
exports: [BService]
})
export class BModule {
}
My code
@Module({
imports: [BModule],
providers: [CService]
})
export class CModule {
}
Question
How can I override/replace the AService
provider from my code? (without third party library patching)
Code example
Third party library@Module({
providers: [AService]
exports: [AService]
})
export class AModule {
}
@Module({
imports: [AModule],
providers: [BService]
exports: [BService]
})
export class BModule {
}
My code
@Module({
imports: [BModule],
providers: [CService]
})
export class CModule {
}
Question
How can I override/replace the AService
provider from my code? (without third party library patching)
- Firstly, why would you want to? I would remend making your module dynamic and accepting a provider of a certain instance you can use – bashleigh Commented Nov 10, 2019 at 17:51
-
@bashleigh it's legit what he want. He has an external dependency, say
A
, and he wants to inject a custom service into it. He can't patch the external library. How would you inject a service you coded into the "DI system" of the imported module WITHOUT changing the imported module? – Sebastian Commented Mar 4, 2022 at 13:41 - Take a look at this DynamicModule implementation stackoverflow./a/75663183/409604 – Marco C. Commented Jan 11, 2024 at 23:22
- I have the same question - as a Java person, it's pretty normal to create a derivative class that extends the base object and overrides methods to use as a test variant. It's sort of one of the stated reasons to use injection in the first place. If you can't leverage abstractions - injection bees a lot less useful. – PlexQ Commented Jun 26, 2024 at 5:15
3 Answers
Reset to default 0If you try something like this:
@Module({
providers: [
{
provide: AService,
useClass: AClassOverride,
},
],
})
export class CModule {}
The service will be overridden in the CModule
, not the AModule
. To change the service
inside the AModule
you need to use a DynamicModule
.
import {
DynamicModule,
Injectable,
Module,
ModuleMetadata,
} from "@nestjs/mon";
export type AModuleAsyncOptions = {
imports?: ModuleMetadata["imports"];
inject: any[];
aServiceFactory: (...args: any[]) => Promise<AService> | AService;
};
@Module({
providers: [AService],
exports: [AService],
})
export class AModule {
static registerAsync(options: AModuleAsyncOptions): DynamicModule {
return {
module: AModule,
imports: options.imports,
providers: [
{
provide: AService,
useFactory: options.aServiceFactory,
inject: options.inject,
},
],
exports: [AService],
};
}
}
Now you can provide your own dependency from BModule
.
@Module({
imports: [
AModule.registerAsync({
imports: [ServiceModuleOverride], // import the module that haves the dependency that you want to override
inject: [AServiceOverride], // Inject the service
aServiceFactory: (aServiceOverride: AServiceOverride) => {
return aServiceOverride; // return the service
},
}),
],
})
export class BModule {}
If you want to override CModule
you can do what I did in AModule
in BModule
and pass down the
inject
, imports
, and the factory
.
Consider using injection tokens and interfaces.
For the implementation details refer to this answer: https://stackoverflow./a/70088972/11931595
Following on from my ment, this is how you would go about making a dynamic module with a dynamic provider
export interface ProviderInterface {
handle(): void;
}
@Injectable()
class SomeHandlingProvider {
constructor(@Inject('MY_DYNAMIC_PROVIDER') private readonly dynamicProvider: ProviderInterface) {}
handle(): void {
this.dynamicProvider.handle();
}
}
@Module({})
export class AModule {
public static forRoot(provider: ProviderInstance): DynamicModule {
return {
module: AModule,
providers: [
{
provide: 'MY_DYNAMIC_PROVIDER',
useClass: provider,
},
SomeHandlingProvider,
],
};
}
}
Then you can use like this
class GenericDynamicProviderExample implements ProviderInterface {
handle(): void {
console.log('hello');
}
}
@Module({
imports: [
AModule.forRoot(GenericDynamicProviderExample),
],
})
export class BModule {}