Skip to main content
Практика завдань

Як реалізувати стратегії кешування в NestJS?

Кешування в NestJS

NestJS надає вбудований CacheModule, який підтримує кілька сховищ кешу: в пам'яті, Redis, Memcached та інші.


Налаштування

bash
npm install @nestjs/cache-manager cache-manager npm install cache-manager-redis-yet redis # для Redis

Кеш в пам'яті

typescript
import { CacheModule } from '@nestjs/cache-manager'; @Module({ imports: [ CacheModule.register({ ttl: 60000, // 60 секунд (в мс) max: 100, // максимальна кількість елементів у кеші }), ], }) export class AppModule {}

Redis Кеш

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 {}

Використання CacheInterceptor (Автоматичне кешування)

typescript
import { CacheInterceptor, CacheTTL, CacheKey } from '@nestjs/cache-manager'; @Controller('products') @UseInterceptors(CacheInterceptor) // Кешувати всі GET ендпоїнти export class ProductsController { @Get() @CacheTTL(30000) // Перезаписати TTL — 30 секунд findAll() { return this.productsService.findAll(); } @Get(':id') @CacheKey('product-detail') // Користувацький ключ кешу @CacheTTL(60000) findOne(@Param('id') id: string) { return this.productsService.findOne(id); } }

Ручне управління кешем

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) { // Спочатку пробуємо кеш const cached = await this.cacheManager.get(`product:${id}`); if (cached) return cached; // Отримуємо з БД const product = await this.productRepo.findById(id); // Зберігаємо в кеш await this.cacheManager.set(`product:${id}`, product, 60000); return product; } async update(id: string, data: UpdateProductDto) { const product = await this.productRepo.update(id, data); // Інвалідовуємо кеш await this.cacheManager.del(`product:${id}`); await this.cacheManager.del('products:list'); return product; } async clearAll() { await this.cacheManager.reset(); } }

Шаблон Cache-Aside

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; } // Використання const product = await this.getOrSet( `product:${id}`, () => this.productRepo.findById(id), 60000, );

Стратегії інвалідизації кешу

СтратегіяКоли використовувати
На основі TTLДані змінюються рідко
Write-throughІнвалідовувати/оновлювати кеш при кожному записі
На основі подійСлухати події змін
На основі шаблонуОчищати всі ключі, що відповідають шаблону

Резюме

ПідхідПлюсиМінуси
CacheInterceptorНульовий код, автоматичноМенше контролю
Ручний (CACHE_MANAGER)Повний контрольБільше коду
Допоміжник Cache-AsideПовторне використання, чистоПотрібно створити

Порада для виробництва: Використовуйте Redis для кешування в продукції. Встановлюйте відповідні TTL на основі волатильності даних. Завжди інвалідизуйте кеш при записах. Моніторте співвідношення попадань/промахів кешу для оптимізації TTL.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?
Практика завдань