From b7d036dbe29fcfa39c6573a0f02888093eb43d78 Mon Sep 17 00:00:00 2001 From: Ritesh Ghosh Date: Sat, 7 Dec 2024 21:12:34 +0530 Subject: feat(cache): add `AniwatchAPICache` class to implement API caching layer --- src/config/cache.ts | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 src/config/cache.ts diff --git a/src/config/cache.ts b/src/config/cache.ts new file mode 100644 index 0000000..6953fbc --- /dev/null +++ b/src/config/cache.ts @@ -0,0 +1,50 @@ +import { config } from "dotenv"; +import { Redis } from "ioredis"; + +config(); + +export class AniwatchAPICache { + private _client: Redis | null; + public isOptional: boolean = true; + + static DEFAULT_CACHE_EXPIRY_SECONDS = 60 as const; + static CACHE_EXPIRY_HEADER_NAME = "X-ANIWATCH-CACHE-EXPIRY" as const; + + constructor() { + const redisConnURL = process.env?.ANIWATCH_API_REDIS_CONN_URL; + this.isOptional = !Boolean(redisConnURL); + this._client = this.isOptional ? null : new Redis(String(redisConnURL)); + } + + set(key: string | Buffer, value: string | Buffer | number) { + if (this.isOptional) return; + return this._client?.set(key, value); + } + + get(key: string | Buffer) { + if (this.isOptional) return; + return this._client?.get(key); + } + + /** + * @param expirySeconds set to 60 by default + */ + async getOrSet( + key: string | Buffer, + setCB: () => Promise, + expirySeconds: number = AniwatchAPICache.DEFAULT_CACHE_EXPIRY_SECONDS + ) { + const cachedData = this.isOptional + ? null + : (await this._client?.get(key)) || null; + let data = JSON.parse(String(cachedData)) as T; + + if (!data) { + data = await setCB(); + await this._client?.set(key, JSON.stringify(data), "EX", expirySeconds); + } + return data; + } +} + +export const cache = new AniwatchAPICache(); -- cgit v1.2.3