@digicroz/node-backend-utils
Version:
Backend utilities for Node.js applications - Redis client wrappers and more utilities for TypeScript/JavaScript projects
120 lines (116 loc) • 4.92 kB
TypeScript
import { RedisClientType } from 'redis';
/**
* Extract all possible cache key types from a cache key definition object
* Recursively extracts string literals and function return types
*
* @example
* ```typescript
* const CACHE_KEYS = {
* users: {
* profile: (userId: number) => `user:${userId}:profile` as const,
* },
* } as const;
*
* type CacheKey = ExtractCacheKeyType<typeof CACHE_KEYS>;
* // Result: "user:${number}:profile"
* ```
*/
type ExtractCacheKeyType<T> = T extends string ? T : T extends (...args: any[]) => infer R ? R extends string ? R : never : T extends Record<string, any> ? {
[K in keyof T]: ExtractCacheKeyType<T[K]>;
}[keyof T] : never;
interface RedisGenericClientConfig {
prefix: string;
isEnabled: boolean;
}
/**
* Generic Redis client with optional type-safe cache keys
* @template TCacheKeys - Optional type for cache keys (defaults to string)
*/
declare class RedisGenericClient<TCacheKeys = string> {
private readonly keyPrefix;
private readonly isEnabled;
constructor(config: RedisGenericClientConfig);
private addPrefix;
private removePrefix;
isClientEnabled(): boolean;
isClientConnected(): boolean;
getStatus(): {
isEnabled: boolean;
prefix: string;
isConnected: boolean;
isConnecting: boolean;
initializationAttempted: boolean;
};
set(key: TCacheKeys, value: string): Promise<string | null>;
setObj<T>(key: TCacheKeys, value: T): Promise<string | null>;
setEx(key: TCacheKeys, seconds: number, value: string): Promise<string | null>;
setObjEx<T>(key: TCacheKeys, seconds: number, value: T): Promise<string | null>;
get(key: TCacheKeys): Promise<string | null>;
getObj<T>(key: TCacheKeys): Promise<T | null>;
del(key: TCacheKeys): Promise<number | null>;
delMultiple(keys: TCacheKeys[]): Promise<number | null>;
/**
* Delete all keys matching a pattern (Production-safe using SCAN)
* @param pattern - Pattern to match (e.g., "games:*" to delete all keys starting with "games:")
* @param batchSize - Number of keys to scan per iteration (default: 100)
* @returns Number of keys deleted, or null if client is disabled
*
* @example
* ```typescript
* // Delete all game-related keys
* await cache.delByPattern("games:*");
*
* // With type-safe patterns
* await cache.delByPattern(CACHE_KEYS.games._pattern);
* ```
*/
delByPattern(pattern: TCacheKeys | string, batchSize?: number): Promise<number | null>;
exists(key: TCacheKeys): Promise<number | null>;
expire(key: TCacheKeys, seconds: number): Promise<number | null>;
ttl(key: TCacheKeys): Promise<number | null>;
incr(key: TCacheKeys): Promise<number | null>;
incrBy(key: TCacheKeys, increment: number): Promise<number | null>;
decr(key: TCacheKeys): Promise<number | null>;
keys(pattern: string): Promise<string[] | null>;
hSet(key: TCacheKeys, field: string, value: string): Promise<number | null>;
hGet(key: TCacheKeys, field: string): Promise<string | null>;
hGetAll(key: TCacheKeys): Promise<Record<string, string> | null>;
hDel(key: TCacheKeys, fields: string | string[]): Promise<number | null>;
lPush(key: TCacheKeys, ...elements: string[]): Promise<number | null>;
rPush(key: TCacheKeys, ...elements: string[]): Promise<number | null>;
lPop(key: TCacheKeys): Promise<string | null>;
rPop(key: TCacheKeys): Promise<string | null>;
lRange(key: TCacheKeys, start: number, stop: number): Promise<string[] | null>;
sAdd(key: TCacheKeys, ...members: string[]): Promise<number | null>;
sMembers(key: TCacheKeys): Promise<string[] | null>;
sRem(key: TCacheKeys, ...members: string[]): Promise<number | null>;
safeExecute<T>(operation: (client: RedisClientType, addPrefix: (key: string) => string) => Promise<T>): Promise<T | null>;
}
type TCreateRedisClient = {
prefix: string;
isEnabled?: boolean;
};
/**
* Create a Redis client instance
*
* @example
* ```typescript
* // Without type safety (accepts any string)
* const cache = createRedisGenericClient({ prefix: "myapp" });
* await cache.set("any:key", "value");
*
* // With type safety (only accepts defined cache keys)
* const CACHE_KEYS = {
* users: {
* profile: (userId: number) => `user:${userId}:profile` as const,
* },
* } as const;
*
* type CacheKey = ExtractCacheKeyType<typeof CACHE_KEYS>;
* const cache = createRedisGenericClient<CacheKey>({ prefix: "myapp" });
* await cache.set(CACHE_KEYS.users.profile(123), "value"); // ✅ Valid
* await cache.set("random:key", "value"); // ❌ Type error
* ```
*/
declare const createRedisGenericClient: <TCacheKeys = string>({ prefix, isEnabled, }: TCreateRedisClient) => RedisGenericClient<TCacheKeys>;
export { type ExtractCacheKeyType, RedisGenericClient, createRedisGenericClient };