cache-manager
GitHub Overview
jaredwray/cacheable
a robust, scalable, and maintained set of caching packages
Topics
Star History
Library
cache-manager
Overview
cache-manager is a cache module for Node.js that provides a multi-store cache manager capable of managing multiple cache stores through a unified interface. With TypeScript support, it enables easy construction of hierarchical cache architectures.
Details
cache-manager is a Node.js cache library that provides easy wrapping of functions in cache, tiered caches, and a consistent interface through plugin architecture. Built with TypeScript and compatible with ESModules, with version 7.0.0 being the latest release. It provides a mechanism to refresh expiring cache keys in background and supports various storage backends for caching content like Redis, MongoDB, databases, and filesystems. The multi-cache functionality allows creating cache hierarchy wrappers such as level 1 (memory) and level 2 (Redis), with configurable non-blocking mode that returns the first (fastest) value found or operates without waiting for all stores to complete when using multiple stores. It has proven track record with adoption in NestJS and other frameworks, and is used by over 1404 projects in the npm registry.
Advantages and Disadvantages
Advantages
- Unified Interface: Operate multiple cache stores with consistent API
- Full TypeScript Support: Type safety and excellent developer experience
- Multi-store Support: Build hierarchical cache architectures
- Rich Driver Support: Wide support for Redis, MongoDB, filesystem, etc.
- Framework Integration: Proven track record and support in NestJS and others
- Non-blocking Mode: Achieve fast response times
- Plugin Architecture: Ensure extensibility and flexibility
Disadvantages
- Complexity: Excessive features for simple caching needs
- Performance Overhead: Slight performance degradation due to abstraction layers
- Configuration Complexity: Complex configuration management for multi-store setups
- Dependencies: Additional packages required depending on drivers used
- Learning Curve: Concept understanding required to leverage advanced features
Key Links
- cache-manager npm Package
- GitHub Repository
- New Repository
- TypeScript-enabled Version
- NestJS Cache Manager
- Usage Examples and Samples
Code Examples
Basic In-Memory Cache
import { createCache } from 'cache-manager'
// Create in-memory store
const memoryCache = createCache({
store: 'memory',
max: 100, // Maximum items
ttl: 60000 // TTL (milliseconds)
})
// Set value
await memoryCache.set('key', 'value', 30000) // 30 second TTL
// Get value
const value = await memoryCache.get('key')
console.log(value) // 'value'
// Delete value
await memoryCache.del('key')
// Clear cache
await memoryCache.reset()
Multi-store Setup (Tiered Cache)
import { createCache } from 'cache-manager'
import { createKeyv } from 'cacheable'
import { createKeyv as createKeyvRedis } from '@keyv/redis'
// Multiple store configuration
const memoryStore = createKeyv() // L1: Memory
const redisStore = createKeyvRedis('redis://user:pass@localhost:6379') // L2: Redis
const cache = createCache({
stores: [memoryStore, redisStore]
})
// Using tiered cache
await cache.set('user:123', { name: 'John', age: 30 })
// Returns first found value (usually from memory)
const user = await cache.get('user:123')
console.log(user)
Automatic Caching with wrap Function
import { createCache } from 'cache-manager'
const cache = createCache({
store: 'memory',
ttl: 60000
})
// Wrap database access function with cache
async function getUserFromDb(userId) {
console.log('Fetching from database...')
// Simulate actual DB processing
await new Promise(resolve => setTimeout(resolve, 100))
return { id: userId, name: `User${userId}`, email: `user${userId}@example.com` }
}
// Add caching functionality with wrap function
async function getUser(userId) {
return await cache.wrap(`user:${userId}`, () => getUserFromDb(userId))
}
// First call fetches from DB, subsequent calls from cache
const user1 = await getUser('123') // DB access
const user2 = await getUser('123') // From cache
Redis Integration
import { createCache } from 'cache-manager'
import { createRedisStore } from 'cache-manager-redis-store'
const redisCache = createCache({
store: createRedisStore({
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0
}),
ttl: 600 // 10 minutes
})
// Using Redis cache
await redisCache.set('session:abc123', {
userId: '456',
loginTime: new Date().toISOString()
}, 3600000) // 1 hour
const session = await redisCache.get('session:abc123')
Type-safe Usage with TypeScript
import { createCache, Cache } from 'cache-manager'
interface User {
id: string
name: string
email: string
}
interface CacheConfig {
userCache: Cache
sessionCache: Cache
}
class UserService {
private caches: CacheConfig
constructor() {
this.caches = {
userCache: createCache({
store: 'memory',
max: 1000,
ttl: 300000 // 5 minutes
}),
sessionCache: createCache({
store: 'memory',
max: 10000,
ttl: 1800000 // 30 minutes
})
}
}
async getUser(userId: string): Promise<User | null> {
const cacheKey = `user:${userId}`
// Try to get from cache
let user = await this.caches.userCache.get<User>(cacheKey)
if (!user) {
// Fetch from DB
user = await this.fetchUserFromDatabase(userId)
if (user) {
await this.caches.userCache.set(cacheKey, user)
}
}
return user
}
private async fetchUserFromDatabase(userId: string): Promise<User | null> {
// Database access implementation
return {
id: userId,
name: `User ${userId}`,
email: `user${userId}@example.com`
}
}
}
Multi-store Non-blocking Mode
import { createCache } from 'cache-manager'
import { createKeyv } from 'cacheable'
import { createKeyv as createKeyvRedis } from '@keyv/redis'
const memoryStore = createKeyv()
const redisStore = createKeyvRedis('redis://localhost:6379')
const cache = createCache({
stores: [memoryStore, redisStore],
nonBlocking: true // Non-blocking mode
})
// In non-blocking mode:
// set/mset - does not wait for all stores to complete
// get/mget - returns first found value immediately
// del/mdel - does not wait for all stores to complete
await cache.set('fast-key', 'fast-value') // Completes immediately
const value = await cache.get('fast-key') // Gets from fastest store
Custom Store Implementation
class CustomMemoryStore {
constructor() {
this.cache = new Map()
this.timers = new Map()
}
async get(key) {
return this.cache.get(key)
}
async set(key, value, ttl) {
this.cache.set(key, value)
if (ttl) {
// Clear existing timer
if (this.timers.has(key)) {
clearTimeout(this.timers.get(key))
}
// Set new timer
const timer = setTimeout(() => {
this.cache.delete(key)
this.timers.delete(key)
}, ttl)
this.timers.set(key, timer)
}
}
async del(key) {
if (this.timers.has(key)) {
clearTimeout(this.timers.get(key))
this.timers.delete(key)
}
this.cache.delete(key)
}
async reset() {
this.timers.forEach(timer => clearTimeout(timer))
this.cache.clear()
this.timers.clear()
}
}
// Using custom store
const customCache = createCache({
store: new CustomMemoryStore()
})