Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Кеширование

Кеш — временное хранилище готовых результатов. Если список статей запрашивают сотни раз в минуту, а меняется он редко, незачем каждый раз обращаться к базе: результат можно запомнить на минуту и отдавать из памяти. В NestJS для этого есть модуль @nestjs/cache-manager.

Установка и подключение

npm i @nestjs/cache-manager cache-manager
// src/app.module.ts
import { CacheModule } from '@nestjs/cache-manager';
import { Module } from '@nestjs/common';
import { PostsModule } from './posts/posts.module';
 
@Module({
  imports: [CacheModule.register({ isGlobal: true, ttl: 60_000 }), PostsModule],
})
export class AppModule {}

ttl (time to live, «время жизни») — сколько миллисекунд хранится запись: здесь одна минута. По умолчанию кеш лежит в памяти процесса. isGlobal: true делает кеш доступным во всех модулях.

Кеширование ответов

Перехватчик CacheInterceptor запоминает ответы GET-маршрутов:

// src/posts/posts.controller.ts
import { CacheInterceptor, CacheTTL } from '@nestjs/cache-manager';
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { PostsService } from './posts.service';
 
@Controller('posts')
@UseInterceptors(CacheInterceptor)
export class PostsController {
  constructor(private readonly postsService: PostsService) {}
 
  @Get()
  @CacheTTL(30_000)
  findAll() {
    return this.postsService.findAll(1);
  }
}

Первый запрос к /posts выполнит метод и сохранит ответ, следующие 30 секунд ответ будет приходить из кеша без обращения к базе. Ключ записи — адрес запроса, поэтому /posts?page=2 кешируется отдельно. Декоратор @CacheTTL() переопределяет время жизни для одного маршрута. Запросы POST, PATCH и DELETE перехватчик не кеширует.

Ручная работа с кешем

Сервис может сам читать и записывать кеш. Для этого внедряют объект по ключу CACHE_MANAGER:

// src/posts/posts.service.ts
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Inject, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Cache } from 'cache-manager';
import { Repository } from 'typeorm';
import { Post } from './entities/post.entity';
 
@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postsRepository: Repository<Post>,
    @Inject(CACHE_MANAGER) private readonly cache: Cache,
  ) {}
 
  async countPosts(): Promise<number> {
    const cached = await this.cache.get<number>('posts:count');
    if (cached !== undefined && cached !== null) {
      return cached;
    }
 
    const count = await this.postsRepository.count();
    await this.cache.set('posts:count', count, 60_000);
    return count;
  }
}

@Inject() нужен потому, что CACHE_MANAGER — не класс, а строковый ключ. Методы get(), set() и del() асинхронные. Вызовите countPosts() из маршрута GET /posts/count — первый запрос обратится к базе, а следующие в течение минуты получат число из кеша.

МетодЧто делает
get(key)Возвращает значение или пустой результат
set(key, value, ttl)Сохраняет значение на ttl миллисекунд
del(key)Удаляет запись

Частая ошибка

После создания или изменения статьи список по-прежнему старый: ответ лежит в кеше, пока не истечёт ttl. Когда данные меняются, удаляйте связанные записи — например, вызовите await this.cache.del('/posts') в методе create(). Кешируйте только то, где небольшая задержка обновления допустима.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.