Introduction
Caching is essential for improving performance and reducing database load. With new update on the nestjs , there are few breaking changes modules. One of them is caching. They have migrated to Keyv
In this post, I’ll show how to integrate Redis caching using Keyv in a NestJS application. Keyv is a simple key-value storage adapter that supports multiple backends, including Redis.
First, install Keyv and the Redis adapter:
npm install keyv @keyv/redis
And have a seperate module for cache as below
import Keyv from 'keyv';
import KeyvRedis from '@keyv/redis';
import { CacheService } from './cache.service';
@Module({
providers: [
{
provide: 'CACHE_INSTANCE',
useFactory: () => {
// Connect to Redis
const redisStore = new KeyvRedis('redis://localhost:6379');
return new Keyv({ store: redisStore, namespace: '' });
},
},
CacheService,
],
exports: [CacheService, 'CACHE_INSTANCE'],
})
export class CacheModule {}
Creating a cache services
import Keyv from 'keyv';
@Injectable()
export class CacheService {
constructor(@Inject('CACHE_INSTANCE') private readonly cache: Keyv) {}
async get<T>(key: string): Promise<T | undefined> {
console.log(`Getting from Redis: ${key}`);
const value = await this.cache.get<string>(key);
if (!value) return undefined;
try {
return JSON.parse(value) as T;
} catch (error) {
console.error('Failed to parse cache value:', error);
return value as T;
}
}
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
console.log(`Storing in Redis: ${key} =>`, JSON.stringify(value));
await this.cache.set(key, JSON.stringify(value), ttl);
}
async delete(key: string): Promise<void> {
console.log(`Deleting from Redis: ${key}`);
await this.cache.delete(key);
}
}
And having those in place we can use this forany other services , in this case I have a simple user service where I query users from database and store it in the cache.
import { PrismaService } from 'src/prisma.service';
import { CacheService } from 'src/cache/cache.service';
import { User } from '@prisma/client';
@Injectable()
export class UserService {
constructor(
private prisma: PrismaService,
private readonly cacheService: CacheService,
) {}
async getUsers(page: number, limit: number): Promise<User[]> {
const cacheKey = `all_users_page_${page}_limit_${limit}`;
const cachedUsers = await this.cacheService.get<User[]>(cacheKey);
if (cachedUsers) {
console.log('Returning users from cache:', cacheKey);
return cachedUsers;
}
const skip = (page - 1) * limit;
const users = await this.prisma.user.findMany({
skip,
take: limit,
orderBy: { id: 'asc' },
include: { country: true },
});
console.log('Fetching from DB and caching result:', cacheKey);
await this.cacheService.set(cacheKey, users, 60000);
return users;
}
}
Below is the user controller
import { UserService } from './user.service';
import { ParseIntPipe } from '@nestjs/common';
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
async findAll(
@Query('page', ParseIntPipe) page: number,
@Query('limit', ParseIntPipe) limit: number,
) {
return this.userService.getUsers(page, limit);
}
}
You can check Kevy documention here for details explanation.
Happy coding ✌️