最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Nest.js: how to override providers in an imported module? - Stack Overflow

programmeradmin2浏览0评论

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)

Share Improve this question asked Nov 7, 2019 at 13:31 MikeMike 6631 gold badge11 silver badges23 bronze badges 4
  • 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
Add a ment  | 

3 Answers 3

Reset to default 0

If 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 {}
发布评论

评论列表(0)

  1. 暂无评论