Cache Package
A high-performance, type-safe caching library powered by ts-cache, supporting memory and Redis drivers with advanced caching patterns.
Installation
bun add @stacksjs/cache
Basic Usage
import { cache, createCache, createMemoryCache, createRedisCache } from '@stacksjs/cache'
await cache.set('key', 'value', 60)
const value = await cache.get('key')
const redisCache = createRedisCache({
host: 'localhost',
port: 6379,
prefix: 'myapp'
})
const customCache = createCache('memory', { maxKeys: 1000 })
Configuration
Memory Cache Options
import { createMemoryCache } from '@stacksjs/cache'
const cache = createMemoryCache({
stdTTL: 0,
checkPeriod: 600,
maxKeys: -1,
useClones: true,
prefix: 'myapp:'
})
Redis Cache Options
import { createRedisCache } from '@stacksjs/cache'
const cache = createRedisCache({
url: 'redis://localhost:6379',
host: 'localhost',
port: 6379,
username: 'default',
password: 'secret',
database: 0,
tls: true,
stdTTL: 3600,
prefix: 'myapp:'
})
Core Operations
Getting and Setting
await cache.set('user:1', { name: 'John' }, 3600)
await cache.setForever('config', { theme: 'dark' })
const user = await cache.get<User>('user:1')
const data = await cache.getOrSet('expensive-data', async () => {
return await computeExpensiveData()
}, 600)
Checking Existence
const exists = await cache.has('user:1')
const missing = await cache.missing('user:1')
Deleting
await cache.del('user:1')
await cache.remove('user:1')
await cache.del(['user:1', 'user:2', 'user:3'])
await cache.deleteMany(['key1', 'key2'])
await cache.flush()
await cache.clear()
Bulk Operations
const values = await cache.mget<User>(['user:1', 'user:2', 'user:3'])
await cache.mset([
{ key: 'user:1', value: user1, ttl: 3600 },
{ key: 'user:2', value: user2, ttl: 3600 },
{ key: 'user:3', value: user3 }
])
TTL Management
const ttl = await cache.getTtl('user:1')
await cache.ttl('user:1', 7200)
Take (Get and Delete)
const user = await cache.take<User>('user:1')
Listing Keys
const allKeys = await cache.keys()
const userKeys = await cache.keys('user:*')
Cache Statistics
const stats = await cache.getStats()
console.log(stats.hits)
console.log(stats.misses)
console.log(stats.keys)
console.log(stats.size)
console.log(stats.hitRate)
Advanced Patterns
Cache-Aside Pattern
import { CacheAsidePattern } from '@stacksjs/cache'
const cacheAside = new CacheAsidePattern(cache, {
defaultTtl: 3600
})
const user = await cacheAside.get('user:1', async () => {
return await db.selectFrom('users').where('id', '=', 1).first()
})
Write-Through Pattern
import { WriteThroughPattern } from '@stacksjs/cache'
const writeThrough = new WriteThroughPattern(cache, {
defaultTtl: 3600,
writeToStore: async (key, value) => {
await db.updateTable('users')
.set(value)
.where('id', '=', key.split(':')[1])
.execute()
}
})
await writeThrough.set('user:1', { name: 'Jane' })
Refresh-Ahead Pattern
import { RefreshAheadPattern } from '@stacksjs/cache'
const refreshAhead = new RefreshAheadPattern(cache, {
ttl: 3600,
refreshThreshold: 300,
fetcher: async (key) => {
return await fetchDataForKey(key)
}
})
const data = await refreshAhead.get('data:key')
Multi-Level Cache
import { MultiLevelPattern } from '@stacksjs/cache'
const l1Cache = createMemoryCache({ maxKeys: 1000 })
const l2Cache = createRedisCache({ host: 'localhost' })
const multiLevel = new MultiLevelPattern([l1Cache, l2Cache], {
defaultTtl: 3600
})
const value = await multiLevel.get('key')
Utility Classes
Rate Limiter
import { RateLimiter } from '@stacksjs/cache'
const limiter = new RateLimiter(cache, {
points: 100,
duration: 60,
keyPrefix: 'rl:'
})
const result = await limiter.consume('user:1')
if (result.allowed) {
console.log(`Remaining: ${result.remainingPoints}`)
} else {
console.log(`Retry after: ${result.retryAfter}ms`)
}
Cache Lock (Distributed Locking)
import { CacheLock } from '@stacksjs/cache'
const lock = new CacheLock(cache, {
lockTimeout: 30000,
retryDelay: 100
})
const acquired = await lock.acquire('resource:1')
if (acquired) {
try {
await processExclusiveTask()
} finally {
await lock.release('resource:1')
}
}
await lock.withLock('resource:1', async () => {
await processExclusiveTask()
})
Circuit Breaker
import { CircuitBreaker } from '@stacksjs/cache'
const breaker = new CircuitBreaker(cache, {
failureThreshold: 5,
recoveryTimeout: 30000
})
const result = await breaker.execute('external-api', async () => {
return await fetch('https://api.example.com/data')
})
const state = await breaker.getState('external-api')
Cache Invalidation
import { CacheInvalidation } from '@stacksjs/cache'
const invalidation = new CacheInvalidation(cache)
await invalidation.invalidateByPattern('user:*')
await cache.set('post:1', data, 3600)
await invalidation.tag('post:1', ['posts', 'user:1:posts'])
await invalidation.invalidateByTag('user:1:posts')
Batch Operations
import { BatchOperations } from '@stacksjs/cache'
const batch = new BatchOperations(cache)
await batch.execute([
{ operation: 'set', key: 'key1', value: 'value1', ttl: 3600 },
{ operation: 'set', key: 'key2', value: 'value2' },
{ operation: 'del', key: 'old-key' }
])
Memoization
import { memoize } from '@stacksjs/cache'
const memoizedFetch = memoize(
async (userId: number) => {
return await fetchUserFromDB(userId)
},
{
cache,
ttl: 3600,
keyGenerator: (userId) => `user:${userId}`
}
)
const user1 = await memoizedFetch(1)
const user1Again = await memoizedFetch(1)
Connection Management
await cache.close()
await cache.disconnect()
const manager = cache.cacheManager
Edge Cases
Handling Cache Misses
const value = await cache.get('non-existent')
if (value === undefined) {
}
const value = await cache.getOrSet('key', () => 'default', 3600)
Handling Serialization
await cache.set('user', {
name: 'John',
roles: ['admin', 'user'],
metadata: { lastLogin: new Date() }
})
const user = await cache.get('user')
Handling Large Values
const cache = createMemoryCache({
maxKeys: 10000,
})
const largeData = await getLargeData()
const chunks = chunkData(largeData, 1000)
for (let i = 0; i < chunks.length; i++) {
await cache.set(`data:chunk:${i}`, chunks[i])
}
Redis Connection Failures
try {
await cache.set('key', 'value')
} catch (error) {
if (error.code === 'ECONNREFUSED') {
console.warn('Redis unavailable, using fallback')
}
}
API Reference
Cache Driver Methods
| Method | Description |
get<T>(key) | Get cached value |
set<T>(key, value, ttl?) | Set cached value |
mget<T>(keys) | Get multiple values |
mset(entries) | Set multiple values |
setForever<T>(key, value) | Set without expiration |
getOrSet<T>(key, fetcher, ttl?) | Get or compute and cache |
has(key) | Check if key exists |
missing(key) | Check if key is missing |
del(keys) | Delete key(s) |
remove(key) | Alias for del |
deleteMany(keys) | Delete multiple keys |
clear() | Clear all cache |
flush() | Alias for clear |
keys(pattern?) | List keys |
getTtl(key) | Get TTL remaining |
ttl(key, seconds) | Update TTL |
take<T>(key) | Get and delete |
getStats() | Get statistics |
close() | Close connection |
disconnect() | Alias for close |
Factory Functions
| Function | Description |
createCache(driver, options) | Create cache by driver type |
createMemoryCache(options) | Create memory cache |
createRedisCache(options) | Create Redis cache |
Pattern Classes
| Class | Description |
CacheAsidePattern | Cache-aside implementation |
WriteThroughPattern | Write-through implementation |
RefreshAheadPattern | Refresh-ahead implementation |
MultiLevelPattern | Multi-level cache |
Utility Classes
| Class | Description |
RateLimiter | Rate limiting |
CacheLock | Distributed locking |
CircuitBreaker | Circuit breaker pattern |
CacheInvalidation | Tag-based invalidation |
BatchOperations | Batch cache operations |
Underlying Libraries
The Stacks cache package is built on top of these zero-dependency libraries from the Stacks ecosystem:
External Resources