Search code examples
dependency-injectionnestjsioredis

How to setup service by injecting `ioredis` client instance using module provider factory in nestjs


nestjs 10.4.6, ioredis 5.4.1, @nestjs-modules/ioredis 2.0.2

I using nestjs framework and I need to inject ioredis client instance defined in RedisModule into a service (MyService), that is setted up in another module MyModule.

I know how to inject it using the way library suggests, using @InjectRedis() decorator from @nestjs-modules/ioredis. It can be done in this way:

MyModule code:

@Module({
  imports: [
    RedisModule,
  ],
  controllers: [],
  providers: [
    MyService,
  ],
  exports: [MyService],
})
export class LitModule {}

MyService code:

@Injectable()
export class MyService {
  constructor(
    @InjectRedis() private readonly redis: Redis
  ) {}
}

RedisModule code:

import { RedisModule as NestRedisModule } from '@nestjs-modules/ioredis';

@Module({
  imports: [
    NestRedisModule.forRootAsync(
      {
        useFactory: (config: ConfigService) => ({
          type: 'single',
          url: config.get<string>('redis.url'),
        }),
        inject: [ConfigService],
      },
    ),
    ConfigModule,
  ],
  providers: [],
  exports: [],
})
export class RedisModule {}

But in a project MyService requres more scalar parameter in constructor, and I should pass this parameter using module factory method. Example:

MyService code:

@Injectable()
export class MyService {
  constructor(
    @InjectRedis() private readonly redis: Redis,
    private readonly ttl: number,
  ) {}
}

MyModule code:

@Module({
  imports: [
    RedisModule,
  ],
  controllers: [],
  providers: [
    {
      provide: MyService,
      useFactory: (config: ConfigService, redis: Redis) => {
        return new RedisSessionStorage(
          redis,
          config.get<number>('lit.redis.session.ttl'),
        );
      },
      inject: [ConfigService, MISSING_TOKEN_OF_REDIS_CLIENT],
    },
  ],
  exports: [MyService],
})
export class LitModule {}

I tried to set connection name as second parameter for NestRedisModule.forRootAsync, and export this token in RedisModule, in order to use it as token in inject, but it does not work, nestjs cannot find such provider.

How to get know what token does redis client have to pass it as second parameter of inject property in MyModule?

For what value the MISSING_TOKEN_OF_REDIS_CLIENT should be replaced in MyModule.


Solution

  • The token is constructed like this, according to the source of the package you linked:

    import { Inject } from '@nestjs/common';
    import { getRedisConnectionToken } from './redis.utils';
    
    export const InjectRedis = (connection?: string) => {
      return Inject(getRedisConnectionToken(connection));
    };
    

    Therefore, the provider declaration should look like:

    import { getRedisConnectionToken } from '@nestjs-modules/ioredis';
    /* ... */
    {
      provide: MyService,
      useFactory: (config: ConfigService, redis: Redis) => {
        return new RedisSessionStorage(
          redis,
          config.get<number>('lit.redis.session.ttl'),
        );
      },
      inject: [ConfigService, getRedisConnectionToken()],
    }
    

    Also, the same technique is used in the unit tests for mocking the Redis instance.