From 53154739737609bfdcfe23d3cd9b37c116458600 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sat, 13 Jun 2026 18:56:58 +0300 Subject: [PATCH] feat: add cache module with getOrFetch pattern and configurable TTL --- .../backend/src/modules/cache/cache.module.ts | 16 +++++++ .../src/modules/cache/cache.service.ts | 44 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 apps/backend/src/modules/cache/cache.module.ts create mode 100644 apps/backend/src/modules/cache/cache.service.ts diff --git a/apps/backend/src/modules/cache/cache.module.ts b/apps/backend/src/modules/cache/cache.module.ts new file mode 100644 index 0000000..4682d12 --- /dev/null +++ b/apps/backend/src/modules/cache/cache.module.ts @@ -0,0 +1,16 @@ +import { Module } from '@nestjs/common'; +import { CacheModule as NestCacheModule } from '@nestjs/cache-manager'; +import { CacheService } from './cache.service'; + +@Module({ + imports: [ + NestCacheModule.register({ + ttl: 900, + max: 1000, + isGlobal: true, + }), + ], + providers: [CacheService], + exports: [CacheService], +}) +export class CacheModule {} diff --git a/apps/backend/src/modules/cache/cache.service.ts b/apps/backend/src/modules/cache/cache.service.ts new file mode 100644 index 0000000..11efcd2 --- /dev/null +++ b/apps/backend/src/modules/cache/cache.service.ts @@ -0,0 +1,44 @@ +import { Injectable, Inject } from '@nestjs/common'; +import { CACHE_MANAGER } from '@nestjs/cache-manager'; +import { Cache } from 'cache-manager'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class CacheService { + constructor( + @Inject(CACHE_MANAGER) private cacheManager: Cache, + private configService: ConfigService, + ) {} + + async get(key: string): Promise { + return this.cacheManager.get(key); + } + + async set(key: string, value: unknown, ttl?: number): Promise { + await this.cacheManager.set(key, value, ttl); + } + + private buildKey(...parts: string[]): string { + return parts.join(':'); + } + + async getOrFetch( + keyPrefix: string, + keyParts: string[], + fetchFn: () => Promise, + ttlConfigKey: string, + ): Promise<{ data: T; fromCache: boolean; cachedAt: string | null }> { + const key = this.buildKey(keyPrefix, ...keyParts); + const ttl = this.configService.get(`app.cache.${ttlConfigKey}`, 900); + + const cached = await this.get(key); + if (cached !== undefined) { + return { data: cached, fromCache: true, cachedAt: null }; + } + + const data = await fetchFn(); + await this.set(key, data, ttl); + + return { data, fromCache: false, cachedAt: new Date().toISOString() }; + } +}