Redis and NestJS Logos

Using Redis in NestJS

Overriding the NestJS Cache system with Redis

I recently implemented Redis within one of my backend NestJS APIs and encountered several headaches. I’m writing this post to help others quickly implement it; as of the time of writing, the proper configuration is not in the NestJS Caching docs. The solution is relatively simple, so this will be a brief post.

Setup

NestJS Cache docs state to use cache-manager-redis. However, upon going to the npm page for the package, there’s an alert that it’s deprecated in favor of Keyv - which we will be using instead.

  1. Install: @keyv/redis

We will be following the direct documentation from Keyv integration for NestJS - Link

  1. Create file: cache.module.ts
import { Module } from '@nestjs/common'
import { Cacheable } from 'cacheable'
import { CacheService } from './cache.service'
import KeyvRedis from '@keyv/redis'
import { ConfigModule, ConfigService } from '@nestjs/config'

@Module({
  imports: [ConfigModule],
  providers: [
    {
      provide: 'CACHE_INSTANCE',
      useFactory: (configService: ConfigService) => {
        const redisUrl = configService.get<string>('REDIS_URL')
        const secondary = new KeyvRedis(redisUrl)
        return new Cacheable({ secondary, ttl: '4h' })
      },
      inject: [ConfigService],
    },
    CacheService,
  ],
  exports: ['CACHE_INSTANCE', CacheService],
})
export class CacheModule {}
  1. Create cache.service.ts
import { Inject, Injectable } from '@nestjs/common'
import type { Cacheable } from 'cacheable'

@Injectable()
export class CacheService {
	constructor(@Inject('CACHE_INSTANCE') private readonly cache: Cacheable) {}

	async get<T>(key: string): Promise<T> {
		return await this.cache.get(key)
	}

	async set<T>(key: string, value: T, ttl?: number | string): Promise<void> {
		await this.cache.set(key, value, ttl)
	}

	async delete(key: string): Promise<void> {
		await this.cache.delete(key)
	}
}
  1. You are practically finished here. Don’t forget to import the CacheModule into your app.module.ts

Example Usage

import { Controller, Get } from '@nestjs/common';
import { CacheService } from '../cache/cache.service';

@Controller('sample')
export class SampleController {
	constructor(private readonly cacheService: CacheService) {}

	@Get()
	async sampleQuery() { 
		const CACHE_KEY = 'sample-health-check';
		let cacheHit = null;
		let data;

			data = await this.cacheService.get(CACHE_KEY);
			if (data) {
				cacheHit = true;
			} else {
				await new Promise(resolve => setTimeout(resolve, 1000)) // Simulation of API req
				data = 'Example Data'
				cacheHit = false
				await this.cacheService.set(CACHE_KEY, data)
			}

			return {
				data,
				cacheHit,
			};
		}
}