NestJS - How to register dynamic module provider multiple times using different configuration?

Viewed 2511

I have a knex module which is implemented like this:

import { DynamicModule, Module } from '@nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';

export const KNEX_MODULE = 'KNEX_MODULE';

@Module({})
export class KnexModule {
  static register(options: Knex.Config): DynamicModule {
    return {
      module: KnexModule,
      providers: [
        {
          inject: [WINSTON_MODULE_PROVIDER],
          provide: KNEX_MODULE,
          useFactory: (logger: Logger) => {
            logger.info('Creating new knex instance', {
              context: KnexModule.name,
              tags: ['instance', 'knex', 'create'],
            });
            return knex(options);
          },
        },
      ],
      exports: [KNEX_MODULE],
    };
  }
}

My application requires access to multiple databases, I know I can do that by creating multiple knex instances. So I tried to register the module twice, passing different configurations. However, the module only registered once. The second register call seems to be reusing the existing object instead of creating a new knex instance.

What is the correct way to generate multiple providers, depending on the configuration passed? The closest thing I found is the forFeature functions in typeORM and Sequelize

1 Answers

I just found the solution. I was thinking the wrong way. I needed to register two providers to my Module. Not create two instances of my module. I solved it by adding one more parameter to my module, which is the provider token. Now it correctly creates the two providers.

import { DynamicModule, Module } from '@nestjs/common';
import { Knex, knex } from 'knex';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger } from 'winston';

export const KNEX_MODULE = 'KNEX_MODULE';

@Module({})
export class KnexModule {
  static register(token: string, options: Knex.Config): DynamicModule {
    return {
      module: KnexModule,
      providers: [
        {
          inject: [WINSTON_MODULE_PROVIDER],
          provide: token,
          useFactory: (logger: Logger) => {
            logger.info('Creating new knex instance', {
              context: KnexModule.name,
              tags: ['instance', 'knex', 'create'],
            });
            return knex(options);
          },
        },
      ],
      exports: [token],
    };
  }
}

And whenever I want to use it I register it like this:

@Module({
  imports: [KnexModule.register(CatRepository.KNEX_TOKEN, knexConfigs)],
  providers: [CatRepository, CatService],
  controllers: [CatController],
  exports: [CatService],
})
export class CatModule {}

Then in the repository I can inject the knex instance of the cats database.

@Injectable()
export class CatRepository implements Repository<Cat> {
  // eslint-disable-next-line no-useless-constructor
  public static KNEX_TOKEN = 'KNEX_CATS_TOKEN';

  // eslint-disable-next-line no-useless-constructor
  constructor(
    @Inject(CatRepository.KNEX_TOKEN)
    protected knex: Knex,
  ) {}

  ...
}
Related