@samuel-pro-tech/redis-helper
v1.0.1
Published
A lightweight Redis helper for Node.js providing cache-aside utilities, Pub/Sub support, and managed Redis connections.
Maintainers
Readme
Redis Helper
A lightweight, production-ready Redis helper library for Node.js with built-in cache-aside pattern, Pub/Sub support, circuit breaker protection, and automatic reconnection.
✨ Features
- 🚀 Cache-Aside Pattern: Built-in
getOrSet()for efficient caching - 🛡️ Circuit Breaker: Automatic failure detection and recovery
- 🔄 Auto-Reconnect: Resilient connection management
- 📡 Pub/Sub Support: Publisher/Subscriber pattern implementation
- 🏷️ Key Prefixing: Organized namespace management
- ⚡ Performance: Minimal overhead with optimal Redis operations
- 🔧 TypeScript Ready: Full type definitions included
📦 Installation
npm install @samuel-pro-tech/redis-helper🚀 Quick Start
import { initRedis, getOrSet, setData, getData } from '@samuel-pro-tech/redis-helper'
// Initialize Redis connection
await initRedis({
url: 'redis://localhost:6379',
keyPrefix: 'myapp:',
defaultTtl: 3600 // 1 hour
})
// Cache-aside pattern: Get from cache or execute function
const userData = await getOrSet('user:123', async () => {
// This function runs only if cache miss
return await fetchUserFromDatabase(123)
}, 1800) // Cache for 30 minutes
// Direct cache operations
await setData('session:abc', { userId: 123, role: 'admin' }, 7200)
const session = await getData('session:abc')🔧 Configuration
Default Configuration
const defaultConfig = {
url: 'redis://localhost:6379',
keyPrefix: 'myapp:',
defaultTtl: 3600, // 1 hour in seconds
socket: {
reconnectStrategy: (retries) => Math.min(retries * 100, 3000),
connectTimeout: 10000
}
}Custom Configuration
await initRedis({
url: 'redis://username:password@host:port',
keyPrefix: 'production:',
defaultTtl: 7200,
socket: {
connectTimeout: 5000,
reconnectStrategy: (retries) => Math.min(retries * 50, 1000)
}
})📚 API Reference
Core Functions
initRedis(config?)
Initialize Redis connection with optional configuration.
await initRedis({
url: 'redis://localhost:6379',
keyPrefix: 'app:',
defaultTtl: 3600
})getOrSet(key, handler, ttl?, parseJson?)
Cache-aside pattern: Get from cache or execute handler function.
const result = await getOrSet('expensive-query', async () => {
return await expensiveDatabaseQuery()
}, 1800) // Cache for 30 minutes
// With custom parsing
const rawResult = await getOrSet('raw-data', async () => {
return 'plain text data'
}, 3600, false) // Don't parse as JSONsetData(key, value, ttl?)
Store data in Redis with optional TTL.
await setData('user:123', { name: 'John', age: 30 }, 3600)
await setData('counter', 42) // Uses default TTL
await setData('permanent', 'data', 0) // No expirationgetData(key, parseJson?)
Retrieve data from Redis.
const user = await getData('user:123') // Returns parsed object
const rawData = await getData('user:123', false) // Returns string
const notFound = await getData('nonexistent') // Returns nullData Management Functions
deleteData(key)
Delete data from Redis.
const deletedCount = await deleteData('user:123')
console.log(`Deleted ${deletedCount} keys`) // 1 if key existed, 0 if notexists(key)
Check if key exists in Redis.
const userExists = await exists('user:123')
if (userExists) {
console.log('User found in cache')
}setTTL(key, ttl)
Set TTL for existing key.
const success = await setTTL('user:123', 7200) // 2 hours
console.log(success ? 'TTL set' : 'Key not found')getTTL(key)
Get TTL for key.
const ttl = await getTTL('user:123')
if (ttl > 0) console.log(`Expires in ${ttl} seconds`)
else if (ttl === -1) console.log('No expiration set')
else if (ttl === -2) console.log('Key does not exist')Pub/Sub Functions
publish(channel, message)
Publish message to Redis channel.
// Publish object
await publish('user-events', {
type: 'login',
userId: 123,
timestamp: Date.now()
})
// Publish string
await publish('notifications', 'System maintenance in 5 minutes')subscribe(channel, callback)
Subscribe to Redis channel.
// Subscribe to user events
await subscribe('user-events', (message) => {
console.log('User event:', message)
// Handle the event based on message.type
})
// Subscribe to notifications
await subscribe('notifications', (message) => {
console.log('Notification:', message)
})Connection Management
isRedisReady()
Check if Redis client is initialized and ready.
if (isRedisReady()) {
console.log('Redis is connected and ready')
} else {
console.log('Redis is not ready')
}isAvailable()
Check if Redis is available (includes circuit breaker status).
if (isAvailable()) {
console.log('Redis is available for operations')
} else {
console.log('Redis unavailable (down or circuit breaker open)')
}getRedisClient() | getRedisPublisher() | getRedisSubscriber()
Get Redis client instances for advanced operations.
// Get main client for custom commands
const client = getRedisClient()
await client.hSet('hash:key', 'field', 'value')
// Get publisher for custom pub/sub
const publisher = getRedisPublisher()
await publisher.publish('custom:channel', 'message')
// Get subscriber for custom subscriptions
const subscriber = getRedisSubscriber()
await subscriber.pSubscribe('pattern:*', (message, channel) => {
console.log(`Pattern message from ${channel}:`, message)
})getConfig()
Get current Redis configuration.
const config = getConfig()
console.log('Current prefix:', config.keyPrefix)
console.log('Default TTL:', config.defaultTtl)
console.log('Redis URL:', config.url)closeRedis()
Gracefully close all Redis connections.
// Graceful shutdown
await closeRedis()
console.log('All Redis connections closed')🛡️ Circuit Breaker
The library includes built-in circuit breaker protection:
- Failure Threshold: 3 consecutive failures
- Recovery Time: 30 seconds
- Automatic Fallback: Functions continue to work even when Redis is down
// Even if Redis is down, this will execute the handler
const data = await getOrSet('key', () => fetchFromDatabase())💡 Usage Examples
Basic Caching
import { initRedis, getOrSet, setData, getData } from '@samuel-pro-tech/redis-helper'
// Initialize
await initRedis({ keyPrefix: 'myapp:', defaultTtl: 3600 })
// Cache expensive operations
const userData = await getOrSet(`user:${userId}`, async () => {
return await db.user.findById(userId)
}, 1800) // Cache for 30 minutesSession Management
// Store session
await setData(`session:${sessionId}`, {
userId: 123,
role: 'admin',
loginTime: Date.now()
}, 86400) // 24 hours
// Retrieve session
const session = await getData(`session:${sessionId}`)
if (session) {
console.log('User authenticated:', session.userId)
}
// Extend session
await setTTL(`session:${sessionId}`, 86400) // Extend by 24 hours
// Logout
await deleteData(`session:${sessionId}`)Pub/Sub Messaging
// Real-time notifications
await subscribe('user:notifications', async (notification) => {
await sendEmailNotification(notification.email, notification.message)
})
// Send notification
await publish('user:notifications', {
userId: 123,
email: '[email protected]',
message: 'Your order has been shipped!'
})Data Analytics
// Increment counters
const client = getRedisClient()
await client.incr('myapp:page:views')
await client.hincrBy('myapp:user:stats', `user:${userId}`, 1)
// Get statistics
const pageViews = await client.get('myapp:page:views')
const userStats = await client.hGetAll('myapp:user:stats')Background Jobs with Circuit Breaker
const processJob = async (jobData) => {
// Try to get cached result first
const cached = await getOrSet(`job:result:${jobData.id}`, async () => {
// This will run even if Redis is down
const result = await heavyProcessing(jobData)
return result
}, 3600)
return cached
}
// Circuit breaker automatically handles Redis failures
const result = await processJob({ id: 'job-123', data: 'process-this' })⚠️ Error Handling
Graceful Degradation
try {
const data = await getData('user:123')
if (data) {
return data // Cache hit
}
} catch (error) {
console.error('Redis error, falling back to database:', error)
}
// Always fallback to primary data source
return await database.getUser(123)Circuit Breaker States
// Check availability before operations
if (isAvailable()) {
await setData('key', 'value')
} else {
console.log('Redis circuit breaker is open, skipping cache')
}
// Use getOrSet for automatic fallback
const data = await getOrSet('key', async () => {
return await fetchFromDatabase() // Always executes if Redis is down
})Connection Monitoring
// Monitor connection status
setInterval(() => {
if (isRedisReady()) {
console.log('✅ Redis: Connected')
} else {
console.log('❌ Redis: Disconnected')
}
if (isAvailable()) {
console.log('🟢 Circuit: Closed')
} else {
console.log('🔴 Circuit: Open')
}
}, 30000) // Check every 30 seconds🚀 Production Usage
Environment Setup
# .env file
REDIS_URL=redis://username:password@host:port
REDIS_PREFIX=production:
REDIS_TTL=3600import { initRedis } from '@samuel-pro-tech/redis-helper'
await initRedis({
url: process.env.REDIS_URL,
keyPrefix: process.env.REDIS_PREFIX,
defaultTtl: parseInt(process.env.REDIS_TTL)
})Graceful Shutdown
process.on('SIGTERM', async () => {
await closeRedis()
process.exit(0)
})🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
MIT License - see LICENSE file for details.
