skip to Main Content

I try to implement nestjs backend and redis as caching. I can do it according to the official document https://docs.nestjs.com/techniques/caching#in-memory-cache.

I use the package cache-manager-redis-store and the code in app.module.ts is as shown below.

import { Module, CacheModule } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import * as redisStore from 'cache-manager-redis-store';
import * as Joi from 'joi';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    CacheModule.registerAsync({
      imports: [
        ConfigModule.forRoot({
          validationSchema: Joi.object({
            REDIS_HOST: Joi.string().default('localhost'),
            REDIS_PORT: Joi.number().default(6379),
            REDIS_PASSWORD: Joi.string(),
          }),
        }),
      ],
      useFactory: async (configService: ConfigService) => ({
        store: redisStore,
        auth_pass: configService.get('REDIS_PASSWORD'),
        host: configService.get('REDIS_HOST'),
        port: configService.get('REDIS_PORT'),
        ttl: 0,
      }),
      inject: [ConfigService],
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

With this setting, I can use get and set as expected but I want to use other redis command such as hget and zadd. Unfortunately, I cannot find the guide anywhere.

I think there must be a way since cache-manager-redis-store package said that it just passing the configuration to the underlying node_redis package. And node-redis package can use those fancy redis commands.

Would be appreciated if you have solutions.

3

Answers


  1. Chosen as BEST ANSWER

    Thank you both Micael Levi and Hamidreza Vakilian for the help. I did more research and found my solution so I will share it for other people have the same problem.

    It seem cache-manager-redis-store allow only normal redis operation and if you want to use more, you have to access cacheManager.store.getClient() as Micael suggest.

    Cache interface got no getClient but RedisCache had! The problem is RedisCache interface inside cache-manager-redis-store package don't export those interface for outsider.

    The easiest way to fix this is to create redis.interface.ts or whatever name for your own.

    import { Cache, Store } from 'cache-manager';
    import { RedisClient } from 'redis';
    
    export interface RedisCache extends Cache {
      store: RedisStore;
    }
    
    export interface RedisStore extends Store {
      name: 'redis';
      getClient: () => RedisClient;
      isCacheableValue: (value: any) => boolean;
    }
    

    Now you can replace Cache with RedisCache interface.

    The usage will be as follows.

    import {
      CACHE_MANAGER,
      Inject,
      Injectable,
    } from '@nestjs/common';
    import { RedisClient } from 'redis';
    import { RedisCache } from './interface/redis.interface';
    
    @Injectable()
    export class RedisService {
      private redisClient: RedisClient;
      constructor(@Inject(CACHE_MANAGER) private cacheManager: RedisCache) {
        this.redisClient = this.cacheManager.store.getClient();
      }
    
      hgetall() {
        this.redisClient.hgetall('mykey', (err, reply) => {
          console.log(reply);
        })
      }
    }
    

    Note that these redisClient commands do not return value so you have to use callback to get the value.

    As you can see that the answer is in callback structure, I prefer await and async more so I did a little more work.

    import {
      BadRequestException,
      CACHE_MANAGER,
      Inject,
      Injectable,
    } from '@nestjs/common';
    import { RedisClient } from 'redis';
    import { RedisCache } from './interface/redis.interface';
    
    @Injectable()
    export class RedisService {
      private redisClient: RedisClient;
      constructor(@Inject(CACHE_MANAGER) private cacheManager: RedisCache) {
        this.redisClient = this.cacheManager.store.getClient();
      }
    
      async hgetall(key: string): Promise<{ [key: string]: string }> {
        return new Promise<{ [key: string]: string }>((resolve, reject) => {
          this.redisClient.hgetall(key, (err, reply) => {
            if (err) {
              console.error(err);
              throw new BadRequestException();
            }
            resolve(reply);
          });
        });
      }
    }
    

    By using promise, I can get the answer as if the function return. The usage is as follows.

    import { Injectable } from '@nestjs/common';
    import { RedisService } from './redis.service';
    
    @Injectable()
    export class UserService {
      constructor(private readonly redisService: RedisService) {}
    
      async getUserInfo(): Promise<{ [key: string]: string }> {
        const userInfo = await this.redisService.hgetall('mykey');
        return userInfo;
      }
    }
    

  2. You can retrieve the underlying redis client by calling
    cacheManager.store.getClient() even tho the interface Cache (from cache-manager) doesn’t has that getClient method defined. I don’t know what’s the best way to type that.

    The return of that will be the Redis client created by cache-manager-redis-store

    Login or Signup to reply.
  3. I suggest nestjs-redis package. It’s compatible with typescript and is dependent on ioredis package thus you get all other Redis APIs as well.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search