Midday / packages /cache /src /redis-client.ts
Jules
Final deployment with all fixes and verified content
c09f67c
import { createLoggerWithContext } from "@midday/logger";
import { getSharedRedisClient } from "./shared-redis";
const logger = createLoggerWithContext("redis-cache");
export class RedisCache {
private prefix: string;
private defaultTTL: number;
constructor(prefix: string, defaultTTL: number = 30 * 60) {
this.prefix = prefix;
this.defaultTTL = defaultTTL;
}
// Get client lazily - allows picking up reconnected clients
private get redis() {
return getSharedRedisClient();
}
private parseValue<T>(value: string | null): T | undefined {
if (!value) return undefined;
try {
return JSON.parse(value) as T;
} catch {
// If parsing fails, return the raw string (for backwards compatibility)
return value as unknown as T;
}
}
private stringifyValue(value: unknown): string {
if (typeof value === "string") {
return value;
}
return JSON.stringify(value);
}
private getKey(key: string): string {
return `${this.prefix}:${key}`;
}
async get<T>(key: string): Promise<T | undefined> {
try {
const value = await this.redis.get(this.getKey(key));
return this.parseValue<T>(value);
} catch (error) {
logger.error(`Get error for ${this.prefix} cache`, {
key,
error: error instanceof Error ? error.message : String(error),
});
return undefined;
}
}
async set(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
try {
const serializedValue = this.stringifyValue(value);
const redisKey = this.getKey(key);
const ttl = ttlSeconds ?? this.defaultTTL;
if (ttl > 0) {
await this.redis.send("SETEX", [
redisKey,
ttl.toString(),
serializedValue,
]);
} else {
await this.redis.set(redisKey, serializedValue);
}
} catch (error) {
logger.error(`Set error for ${this.prefix} cache`, {
key,
error: error instanceof Error ? error.message : String(error),
});
}
}
async delete(key: string): Promise<void> {
try {
await this.redis.del(this.getKey(key));
} catch (error) {
logger.error(`Delete error for ${this.prefix} cache`, {
key,
error: error instanceof Error ? error.message : String(error),
});
}
}
async healthCheck(): Promise<void> {
try {
await this.redis.send("PING", []);
} catch (error) {
throw new Error(`Redis health check failed: ${error}`);
}
}
}