UNPKG

express-redis-simple-cache

Version:

A simple and effective Redis-based caching middleware for Express.js APIs.

156 lines (153 loc) 4.8 kB
// src/index.ts import { createClient } from "redis"; // src/log.ts var currentLogLevel = "normal"; function setLogLevel(level) { currentLogLevel = level; } function log(message, level = "log") { if (currentLogLevel === "silent") return; if (level === "debug" && currentLogLevel !== "debug") return; console[level](`\u{1F5C4}\uFE0F [Cache] ${message}`); } // src/keyGenerator.ts function generateCacheKey(route, req) { const { method, route: routePath } = route; const url = req.originalUrl || req.url; switch (route.cache?.type) { case "always": return `cache:${method}:${routePath}`; case "per-auth-token": const authCookie = getAuthCookie(req); if (!authCookie) { log("No auth cookie found in request. Cache will not be applied. Please use per-custom-cookie and set your custom cookie name.", "warn"); return false; } return `cache:${method}:${routePath}:${authCookie}`; case "per-request-url": return `cache:${method}:${req.url}`; case "per-custom-cookie": if (route.cache.customCookie) { const cookies = req.cookies || {}; const customCookieValue = cookies[route.cache.customCookie] || ""; if (!customCookieValue) { log(`Custom cookie "${route.cache.customCookie}" not found in request. Cache will not be applied.`, "warn"); return false; } return `cache:${method}:${routePath}:${customCookieValue}`; } else { log("Custom cookie is not defined for per-custom-cookie cache type.", "error"); return false; } } return false; } function getAuthCookie(req) { const commonAuthCookieNames = [ "authToken", "accessToken", "refreshToken", "idToken", "jwt", "token", "sessionToken", "auth_token", "access_token", "refresh_token", "bearer_token" ]; const commonAuthHeaderNames = [ "Authorization", "X-Auth-Token", "X-Access-Token", "X-Refresh-Token", "X-ID-Token", "X-API-key", "Token", "Auth-Token" ]; let authCookie = commonAuthCookieNames.map((name) => req.cookies[name]).find((value) => value !== void 0) || null; if (!authCookie) { authCookie = commonAuthHeaderNames.map((name) => req.headers[name.toLowerCase()]).find((value) => value !== void 0) || null; } return authCookie; } // src/index.ts var DEFAULT_CACHE_TTL = 60; var redis = null; async function setupCache(redisClientOptions, logLevel) { redis = createClient(redisClientOptions); setLogLevel(logLevel); redis.on("connect", () => log("Connected to Redis")); redis.on("error", (err) => log(`Redis client error: ${err.message}`, "error")); await redis.connect(); return redis; } async function stopCache(redis2) { await redis2.quit(); log("Redis connection closed"); } function cache(route) { if (route.cache) { return cacheMiddleware(route); } else { return (req, res, next) => next(); } } function cacheMiddleware(route) { if (!redis || !redis.isReady) { log("Redis client is not initialized. Please call setupCache first. Cache will not be applied and next function be called.", "error"); return (req, res, next) => next(); } return async (req, res, next) => { if (!redis || !redis.isReady) { return next(); } const cacheKey = generateCacheKey(route, req); if (!cacheKey) { log("Cache key generation failed. Cache will not be applied.", "warn"); return next(); } const data = await redis.get(cacheKey); if (!data) { log(`Cache miss for key: ${cacheKey}`, "debug"); res.sendResponse = res.send; res.send = async (body) => { if (!redis || !redis.isReady) { return next(); } if (typeof body === "object") { body = JSON.stringify(body); } await redis.set(cacheKey, body, { EX: route.cache?.expire || DEFAULT_CACHE_TTL }); log(`Data cached for key: ${cacheKey}`, "debug"); res.sendResponse(body); }; const originalJson = res.json.bind(res); res.json = async (body) => { if (!redis || !redis.isReady) { return next(); } await redis.set(cacheKey, JSON.stringify(body), { EX: route.cache?.expire || DEFAULT_CACHE_TTL }); log(`JSON Data cached for key: ${cacheKey}`, "debug"); return originalJson(body); }; next(); } else { log(`Cache hit for key: ${cacheKey}`, "debug"); res.setHeader("X-Cache", "HIT"); const dataStr = data.toString(); if (dataStr.startsWith("{") || dataStr.startsWith("[")) { res.json(JSON.parse(dataStr)); } else { res.send(data); } } }; } export { cache, setupCache, stopCache }; //# sourceMappingURL=index.js.map