@koadz/cache - v1.1.4

Koadz Platform - Cache

This cache library provides a unified interface for different caching strategies, including Redis, in-memory LRU caching. It's designed to be modular, type-safe, and easily extensible.

  • Multiple Cache Providers: Redis, LRU memory caching, and Custom providers.
  • Advanced Strategies: Fallback for high availability, TTL configuration, and Redis key prefixing.
  • Developer Experience: TypeScript support, extensive logging, and customization.
  • Reliability: Graceful error handling, connection management, and LRU memory management.
  • Decorator: LoggerDecorator, FallbackDecorator for enhanced functionality.
  • Factory: CacheFactory for handler creation and custom registration.
  • Strategy: Interchangeable cache implementations via ICacheHandler interface.
  • Repository: Unified data storage/retrieval interface.
  • Options: Type-safe configuration using TypeScript.
  • Cache Strategy: Use memory for frequent access, Redis for distribution, and fallback for critical data.
  • Error Handling: Implement CacheError, manage connection failures.
  • Resource Management: Set TTLs, manage Redis connections, configure LRU sizes.
  • Security: Secure Redis connections, authentication, and sensitive data handling.

To install this package, execute the following commands in your terminal:

# Set the APM registry to local (one-time process)
npm set @koadz:registry <registry_url>

npm install @koadz/cache
import { Cache } from "@koadz/cache";

const cache = new Cache({
type: "redis",
clientUrl: "redis://localhost:6379",
});

The Cache repository is a unified class for storing and retrieving data from different cache providers. It's designed to be type-safe and easily extensible.

// Generic type `I` is used to represent the cache handler type.
// Generic type `T` is used to represent the cache data type.
class Cache<I, T> {}

// Custom data type
type Data = {
data: any;
createdAt: Date;
};

// Usage
const cache = new Cache<LRUMemoryHandler, Data>({
// ...
});

The Redis handler is used to cache data in Redis which internally uses the ioredis library. It provides methods for storing, retrieving, and deleting data from the cache.

Note: Install ioredis before using this handler.

npm install ioredis
  • get(key: string): Retrieves data from the cache.
  • set(key: string, value: any): Stores data in the cache.
  • delete(key: string): Deletes data from the cache.
  • clear(): Clears all data from the cache.
  • has(key: string): Checks if a key exists in the cache.
  • expire(key: string, ttl: number): Sets the expiration time for a key in the cache.
  • disconnect(): Closes the Redis connection.
  • flush(keys: string | string[]): Flushes specified keys from the cache.
  • restart(options?: RestartOptions): Restarts the Redis connection. If options include flushAll, all cache is flushed. If flushKeys is provided, only specified keys are flushed.
  • setBatch(entries: Record<string, any>): Sets multiple keys in the cache using a single Redis pipeline.
  • list(): Retrieves a list of all keys in the cache.
const redisCache = new Cache<RedisHandler, { id: number; name: string }>({
type: "redis",
clientUrl: "redis://localhost:6379",
ttl: 3600, // optional global TTL in seconds
});

await redisCache.set("user:123", { id: 123, name: "John" });
const user = await redisCache.get("user:123");
console.log(user); // { id: 123, name: "John" }

await redisCache.setBatch({
"user:456": { id: 456, name: "Jane" },
"user:789": { id: 789, name: "Bob" },
});

await redisCache.handler.expire("user:123", 1800); // Set TTL for specific key
await redisCache.handler.disconnect(); // Clean up connection

// Restart Redis connection
await redisHandler.handler.restart({
flushAll: true, // Optional
// or
flushKeys: ["user:123", "some-key"], // Optional
});

A simple in-memory LRU(Least Recently Used) cache.

  • get(key: string): Retrieves data from the cache.
  • set(key: string, value: any): Stores data in the cache.
  • delete(key: string): Deletes data from the cache.
  • has(key: string): Checks if a key exists in the cache.
  • clear(): Clears all data from the cache.
  • expire(key: string, ttl: number): Sets the expiration time for a key in the cache.
  • size(): Returns the current size of the cache.
  • getOldestKey(): Returns the oldest key in the cache.
const memoryCache = new Cache<LRUMemoryHandler>({
type: "lru-memory",
maxSize: 100, // Maximum number of items to store
ttl: 3600, // Time-to-live in seconds
});

await memoryCache.set("stats", { visits: 100 });
const stats = await memoryCache.get("stats");

// Check if key exists in Cache
const exists = await memoryCache.has("some-key");
console.log(exists); // Output: true

// Get oldest key in Cache
const oldestKey = await memoryCache.getOldestKey();
console.log(oldestKey); // Output: "some-key"

The logger decorator is used to log cache operations. It provides methods for logging cache hits and misses. Options can be provided to configure the logger.

// Add logging to any cache handler
const redisCache = new Cache<RedisHandler>({
type: "redis",
clientUrl: "redis://localhost:6379",
});

const loggedCache = new Cache({
type: "custom",
handler: new LoggerDecorator(redisCache.handler, {
// ...LoggerConfig
}),
});

await loggedCache.set("key", "value"); // Will log operations

The fallback handler is used to provide a fallback cache mechanism. It provides methods for storing, retrieving, and deleting data from the cache. The FallbackDecorator provides a way to combine two cache handlers with automatic fallback behavior.

  • Attempts operations on primary cache first
  • Falls back to secondary cache if primary fails
  • Automatically syncs data between handlers
  • Handles errors gracefully
  • Usage Example with Multiple Layers
  • get(key: string): Retrieves data from the cache.
  • set(key: string, value: any): Stores data in the cache.
  • has(key: string): Checks if a key exists in the cache.
  • delete(key: string): Deletes data from the cache.
  • clear(): Clears all data from the cache.
// Primary: Memory, Fallback: Redis

const memoryCache = new LRUMemoryHandler({
type: "lru-memory",
maxSize: 1000,
});

const redisCache = new RedisHandler({
type: "redis",
clientUrl: "redis://localhost:6379",
});

const fallbackHandler = new Cache({
type: "custom",
handler: new FallbackDecorator(
memoryCache.handler, // Primary (faster)
redisCache.handler // Fallback (more reliable)
),
});

// Will try memory first, then Redis if not found
const value = await fallbackHandler.get("my-key");

// Get from Cache
const data = await fallbackHandler.get("some-key");
console.log(data); // Output: "some-value"
// Create a multi-layer cache setup
const memoryCache = new LRUMemoryHandler({
type: "lru-memory",
maxSize: 100,
});

const redisCache = new RedisHandler({
type: "redis",
clientUrl: "redis://localhost:6379",
});

// Add logging to Redis cache
const loggedRedis = new LoggerDecorator(redisCache.handler);

// Create fallback chain: Memory -> Logged Redis
const fallbackHandler = new FallbackDecorator(memoryCache.handler, loggedRedis);

const cache = new Cache({
type: "custom",
handler: fallbackHandler,
});

// Usage
await cache.set("key", "value");
// 1. Tries to set in memory
// 2. Also sets in Redis
// 3. Logs the Redis operation

const value = await cache.get("key");
// 1. Tries to get from memory
// 2. If not found, tries Redis
// 3. If found in Redis, populates memory cache
// 4. Logs the Redis operation if used