Skip to main content
Practice Problems

How to implement caching strategies in NestJS?

Caching in NestJS

NestJS provides a built-in CacheModule that supports multiple cache stores: in-memory, Redis, Memcached, and more.


Setup

bash
npm install @nestjs/cache-manager cache-manager npm install cache-manager-redis-yet redis # for Redis

In-Memory Cache

typescript
import { CacheModule } from '@nestjs/cache-manager'; @Module({ imports: [ CacheModule.register({ ttl: 60000, // 60 seconds (in ms) max: 100, // max items in cache }), ], }) export class AppModule {}

Redis Cache

typescript
import { CacheModule } from '@nestjs/cache-manager'; import { redisStore } from 'cache-manager-redis-yet'; @Module({ imports: [ CacheModule.registerAsync({ useFactory: async () => ({ store: await redisStore({ socket: { host: 'localhost', port: 6379 }, ttl: 60000, }), }), }), ], }) export class AppModule {}

Using CacheInterceptor (Auto-caching)

typescript
import { CacheInterceptor, CacheTTL, CacheKey } from '@nestjs/cache-manager'; @Controller('products') @UseInterceptors(CacheInterceptor) // Cache all GET endpoints export class ProductsController { @Get() @CacheTTL(30000) // Override TTL — 30 seconds findAll() { return this.productsService.findAll(); } @Get(':id') @CacheKey('product-detail') // Custom cache key @CacheTTL(60000) findOne(@Param('id') id: string) { return this.productsService.findOne(id); } }

Manual Cache Management

typescript
import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; @Injectable() export class ProductsService { constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {} async findOne(id: string) { // Try cache first const cached = await this.cacheManager.get(`product:${id}`); if (cached) return cached; // Fetch from DB const product = await this.productRepo.findById(id); // Store in cache await this.cacheManager.set(`product:${id}`, product, 60000); return product; } async update(id: string, data: UpdateProductDto) { const product = await this.productRepo.update(id, data); // Invalidate cache await this.cacheManager.del(`product:${id}`); await this.cacheManager.del('products:list'); return product; } async clearAll() { await this.cacheManager.reset(); } }

Cache-Aside Pattern

typescript
async getOrSet<T>(key: string, factory: () => Promise<T>, ttl?: number): Promise<T> { const cached = await this.cacheManager.get<T>(key); if (cached !== undefined && cached !== null) { return cached; } const value = await factory(); await this.cacheManager.set(key, value, ttl); return value; } // Usage const product = await this.getOrSet( `product:${id}`, () => this.productRepo.findById(id), 60000, );

Cache Invalidation Strategies

StrategyWhen to Use
TTL-basedData changes infrequently
Write-throughInvalidate/update cache on every write
Event-drivenListen for change events
Pattern-basedClear all keys matching a pattern

Summary

ApproachProsCons
CacheInterceptorZero code, automaticLess control
Manual (CACHE_MANAGER)Full controlMore code
Cache-Aside helperReusable, cleanNeed to build

Production tip: Use Redis for production caching. Set appropriate TTLs based on data volatility. Always invalidate cache on writes. Monitor cache hit/miss ratios to optimize TTLs.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems