tessl install tessl/npm-cache-manager@7.2.0Cache Manager for Node.js with support for multi-store caching, background refresh, and Keyv-compatible storage adapters
Complete reference for all cache-manager configuration options.
type CreateCacheOptions = {
stores?: Keyv[];
ttl?: number;
refreshThreshold?: number;
refreshAllStores?: boolean;
nonBlocking?: boolean;
cacheId?: string;
};Type: Keyv[]
Default: [new Keyv()] (in-memory)
Description: Array of Keyv store instances in priority order (first = highest priority)
Example:
import { Keyv } from 'keyv';
import KeyvRedis from '@keyv/redis';
import { CacheableMemory } from 'cacheable';
const cache = createCache({
stores: [
new Keyv({ store: new CacheableMemory({ lruSize: 1000 }) }),
new Keyv({ store: new KeyvRedis('redis://localhost:6379') }),
],
});Behavior:
nonBlocking)Type: number
Default: undefined (depends on store)
Unit: Milliseconds
Description: Default time-to-live for all cache entries
Example:
const cache = createCache({
ttl: 300000, // 5 minutes default
});
await cache.set('key', 'value'); // Uses 300000ms TTL
await cache.set('other', 'value', 60000); // Overrides with 60000msNotes:
0 or negative values cause immediate expirationInfinity may mean no expiration (store-dependent)Type: number
Default: undefined (no automatic refresh)
Unit: Milliseconds
Description: Triggers background refresh when remaining TTL falls below this value
Example:
const cache = createCache({
ttl: 300000, // 5 minutes total
refreshThreshold: 60000 // Refresh when < 1 minute remains
});Requirements:
ttlwrap() methodBehavior:
TTL: 300000ms (5 minutes)
Threshold: 60000ms (1 minute)
Time 0s: Entry created
Time 4m: Remaining: 60s (equal to threshold)
Time 4m1s: wrap() called → returns cached, triggers background refreshType: boolean
Default: false
Description: Controls which stores are updated during background refresh
Example:
const cache = createCache({
stores: [memoryStore, redisStore],
refreshAllStores: false, // Default
});Behavior:
With refreshAllStores: false (default):
With refreshAllStores: true:
Use Cases:
false: Performance-critical, eventual consistency OKtrue: Strong consistency required across all storesType: boolean
Default: false
Description: Enables non-blocking mode for multi-store operations
Example:
const cache = createCache({
stores: [fastStore, slowStore],
nonBlocking: true,
});Behavior:
With nonBlocking: false (default):
With nonBlocking: true:
Promise.race(), returns fastest resultPerformance Impact:
// Blocking (default)
await cache.set('key', 'value'); // Waits: ~50ms (slowest store)
// Non-blocking
await cache.set('key', 'value'); // Waits: ~1ms (fastest store)Type: string
Default: Auto-generated unique ID
Description: Unique identifier for this cache instance
Example:
const cache1 = createCache({ cacheId: 'user-cache' });
const cache2 = createCache({ cacheId: 'product-cache' });
console.log(cache1.cacheId()); // 'user-cache'
console.log(cache2.cacheId()); // 'product-cache'Purpose:
wrap() coalescingWhen creating Keyv instances, you can configure store-specific options:
interface KeyvOptions {
store?: any; // Storage adapter instance
namespace?: string; // Key prefix namespace
ttl?: number; // Default TTL for this store
serialize?: (data: any) => string;
deserialize?: (data: string) => any;
compression?: any; // Compression adapter
}import { Keyv } from 'keyv';
import KeyvRedis from '@keyv/redis';
import msgpack from 'msgpack';
const keyv = new Keyv({
store: new KeyvRedis('redis://localhost:6379'),
namespace: 'myapp',
ttl: 3600000, // 1 hour default for this store
serialize: msgpack.encode,
deserialize: msgpack.decode,
});
const cache = createCache({
stores: [keyv],
});import { CacheableMemory } from 'cacheable';
new CacheableMemory({
ttl: 60000, // Default TTL in milliseconds
lruSize: 1000, // Maximum number of items (LRU eviction)
useClone: false // Clone values on get/set (default: false)
})import KeyvRedis from '@keyv/redis';
new KeyvRedis('redis://localhost:6379', {
// ioredis options
password: 'secret',
db: 0,
keyPrefix: 'cache:',
lazyConnect: false,
enableOfflineQueue: true,
maxRetriesPerRequest: 3,
})const cache = createCache({
stores: [
new Keyv({
store: new CacheableMemory({
lruSize: 100, // Small cache
ttl: 10000 // Short TTL for testing
}),
}),
],
ttl: 10000,
});const cache = createCache({
stores: [
// L1: Fast memory cache
new Keyv({
store: new CacheableMemory({
ttl: 60000, // 1 minute
lruSize: 5000 // 5000 hot items
}),
}),
// L2: Shared Redis cache
new Keyv({
store: new KeyvRedis(process.env.REDIS_URL!, {
password: process.env.REDIS_PASSWORD,
db: 0,
lazyConnect: false,
maxRetriesPerRequest: 3,
}),
ttl: 3600000, // 1 hour
}),
],
ttl: 300000, // 5 minutes default
refreshThreshold: 60000, // Refresh at 1 minute
refreshAllStores: true, // Ensure consistency
nonBlocking: false, // Strong consistency
cacheId: 'main-cache',
});const cache = createCache({
stores: [
new Keyv({
store: new CacheableMemory({
lruSize: 10000, // Large cache
ttl: 120000 // 2 minutes
}),
}),
new Keyv({
store: new KeyvRedis(redisUrl),
}),
],
nonBlocking: true, // Fast returns
refreshAllStores: false, // Reduce writes
});import { createCache } from 'cache-manager';
function createAppCache() {
const isDev = process.env.NODE_ENV === 'development';
const isTest = process.env.NODE_ENV === 'test';
if (isTest) {
// In-memory only for tests
return createCache({
stores: [new Keyv()],
ttl: 1000, // Short TTL
});
}
if (isDev) {
// Simple setup for development
return createCache({
stores: [
new Keyv({
store: new CacheableMemory({ lruSize: 100 })
}),
],
ttl: 60000,
});
}
// Production: multi-tier with Redis
return createCache({
stores: [
new Keyv({
store: new CacheableMemory({
lruSize: parseInt(process.env.CACHE_SIZE || '5000'),
ttl: 60000
})
}),
new Keyv({
store: new KeyvRedis(process.env.REDIS_URL!),
ttl: 3600000
}),
],
ttl: parseInt(process.env.CACHE_TTL || '300000'),
refreshThreshold: parseInt(process.env.CACHE_REFRESH_THRESHOLD || '60000'),
refreshAllStores: process.env.CACHE_REFRESH_ALL === 'true',
});
}
export const cache = createAppCache();function validateCacheConfig(options: CreateCacheOptions) {
if (options.refreshThreshold && options.ttl) {
if (options.refreshThreshold >= options.ttl) {
throw new Error('refreshThreshold must be less than ttl');
}
}
if (options.stores && options.stores.length === 0) {
console.warn('Empty stores array, using default in-memory store');
}
if (options.ttl && options.ttl <= 0) {
console.warn('TTL <= 0 will cause immediate expiration');
}
}
// Usage
const config: CreateCacheOptions = {
ttl: 300000,
refreshThreshold: 60000,
};
validateCacheConfig(config);
const cache = createCache(config);function calculateOptimalLruSize(
memoryBudgetMB: number,
avgItemSizeKB: number
): number {
const memoryBudgetBytes = memoryBudgetMB * 1024 * 1024;
const avgItemSizeBytes = avgItemSizeKB * 1024;
return Math.floor(memoryBudgetBytes / avgItemSizeBytes);
}
const lruSize = calculateOptimalLruSize(50, 1); // 50MB / 1KB = ~50,000 items
const cache = createCache({
stores: [
new Keyv({
store: new CacheableMemory({ lruSize }),
}),
],
});const TTL_STRATEGY = {
// Critical data: short TTL, frequent refresh
critical: { ttl: 60000, refreshThreshold: 20000 },
// Hot data: medium TTL
hot: { ttl: 300000, refreshThreshold: 60000 },
// Warm data: long TTL
warm: { ttl: 1800000, refreshThreshold: 300000 },
// Cold data: very long TTL
cold: { ttl: 86400000, refreshThreshold: 7200000 },
};
// Apply based on data type
await cache.wrap(
'critical:config',
fetchConfig,
TTL_STRATEGY.critical.ttl,
TTL_STRATEGY.critical.refreshThreshold
);