--- title: Use Caching Strategically impact: HIGH impactDescription: Dramatically reduces database load and response times tags: performance, caching, redis, optimization --- ## Use Caching Strategically Implement caching for expensive operations, frequently accessed data, and external API calls. Use NestJS CacheModule with appropriate TTLs and cache invalidation strategies. Don't cache everything - focus on high-impact areas. **Incorrect (no caching or caching everything):** ```typescript // No caching for expensive, repeated queries @Injectable() export class ProductsService { async getPopular(): Promise { // Runs complex aggregation query EVERY request return this.productsRepo .createQueryBuilder('p') .leftJoin('p.orders', 'o') .select('p.*, COUNT(o.id) as orderCount') .groupBy('p.id') .orderBy('orderCount', 'DESC') .limit(20) .getMany(); } } // Cache everything without thought @Injectable() export class UsersService { @CacheKey('users') @CacheTTL(3600) @UseInterceptors(CacheInterceptor) async findAll(): Promise { // Caching user list for 1 hour is wrong if data changes frequently return this.usersRepo.find(); } } ``` **Correct (strategic caching with proper invalidation):** ```typescript // Setup caching module @Module({ imports: [ CacheModule.registerAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ stores: [ new KeyvRedis(config.get('REDIS_URL')), ], ttl: 60 * 1000, // Default 60s }), }), ], }) export class AppModule {} // Manual caching for granular control @Injectable() export class ProductsService { constructor( @Inject(CACHE_MANAGER) private cache: Cache, private productsRepo: ProductRepository, ) {} async getPopular(): Promise { const cacheKey = 'products:popular'; // Try cache first const cached = await this.cache.get(cacheKey); if (cached) return cached; // Cache miss - fetch and cache const products = await this.fetchPopularProducts(); await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL return products; } // Invalidate cache on changes async updateProduct(id: string, dto: UpdateProductDto): Promise { const product = await this.productsRepo.save({ id, ...dto }); await this.cache.del('products:popular'); // Invalidate return product; } } // Decorator-based caching with auto-interceptor @Controller('categories') @UseInterceptors(CacheInterceptor) export class CategoriesController { @Get() @CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change findAll(): Promise { return this.categoriesService.findAll(); } @Get(':id') @CacheTTL(60 * 1000) // 1 minute @CacheKey('category') findOne(@Param('id') id: string): Promise { return this.categoriesService.findOne(id); } } // Event-based cache invalidation @Injectable() export class CacheInvalidationService { constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} @OnEvent('product.created') @OnEvent('product.updated') @OnEvent('product.deleted') async invalidateProductCaches(event: ProductEvent) { await Promise.all([ this.cache.del('products:popular'), this.cache.del(`product:${event.productId}`), ]); } } ``` Reference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)