Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
40 prioritized NestJS best practices across architecture, DI, security, performance, testing, and microservices.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rules/perf-use-caching.md
1---2title: Use Caching Strategically3impact: HIGH4impactDescription: Dramatically reduces database load and response times5tags: performance, caching, redis, optimization6---78## Use Caching Strategically910Implement 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.1112**Incorrect (no caching or caching everything):**1314```typescript15// No caching for expensive, repeated queries16@Injectable()17export class ProductsService {18async getPopular(): Promise<Product[]> {19// Runs complex aggregation query EVERY request20return this.productsRepo21.createQueryBuilder('p')22.leftJoin('p.orders', 'o')23.select('p.*, COUNT(o.id) as orderCount')24.groupBy('p.id')25.orderBy('orderCount', 'DESC')26.limit(20)27.getMany();28}29}3031// Cache everything without thought32@Injectable()33export class UsersService {34@CacheKey('users')35@CacheTTL(3600)36@UseInterceptors(CacheInterceptor)37async findAll(): Promise<User[]> {38// Caching user list for 1 hour is wrong if data changes frequently39return this.usersRepo.find();40}41}42```4344**Correct (strategic caching with proper invalidation):**4546```typescript47// Setup caching module48@Module({49imports: [50CacheModule.registerAsync({51imports: [ConfigModule],52inject: [ConfigService],53useFactory: (config: ConfigService) => ({54stores: [55new KeyvRedis(config.get('REDIS_URL')),56],57ttl: 60 * 1000, // Default 60s58}),59}),60],61})62export class AppModule {}6364// Manual caching for granular control65@Injectable()66export class ProductsService {67constructor(68@Inject(CACHE_MANAGER) private cache: Cache,69private productsRepo: ProductRepository,70) {}7172async getPopular(): Promise<Product[]> {73const cacheKey = 'products:popular';7475// Try cache first76const cached = await this.cache.get<Product[]>(cacheKey);77if (cached) return cached;7879// Cache miss - fetch and cache80const products = await this.fetchPopularProducts();81await this.cache.set(cacheKey, products, 5 * 60 * 1000); // 5 min TTL82return products;83}8485// Invalidate cache on changes86async updateProduct(id: string, dto: UpdateProductDto): Promise<Product> {87const product = await this.productsRepo.save({ id, ...dto });88await this.cache.del('products:popular'); // Invalidate89return product;90}91}9293// Decorator-based caching with auto-interceptor94@Controller('categories')95@UseInterceptors(CacheInterceptor)96export class CategoriesController {97@Get()98@CacheTTL(30 * 60 * 1000) // 30 minutes - categories rarely change99findAll(): Promise<Category[]> {100return this.categoriesService.findAll();101}102103@Get(':id')104@CacheTTL(60 * 1000) // 1 minute105@CacheKey('category')106findOne(@Param('id') id: string): Promise<Category> {107return this.categoriesService.findOne(id);108}109}110111// Event-based cache invalidation112@Injectable()113export class CacheInvalidationService {114constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}115116@OnEvent('product.created')117@OnEvent('product.updated')118@OnEvent('product.deleted')119async invalidateProductCaches(event: ProductEvent) {120await Promise.all([121this.cache.del('products:popular'),122this.cache.del(`product:${event.productId}`),123]);124}125}126```127128Reference: [NestJS Caching](https://docs.nestjs.com/techniques/caching)129