@ticatec/redis-client
v0.2.0
Published
A lightweight TypeScript wrapper around ioredis with singleton pattern support, mock Redis for testing, and abstract caching framework.
Readme
Ticatec Redis Client
中文 | English
A lightweight TypeScript wrapper around ioredis, providing convenient methods for Redis operations with singleton pattern support. Features both real Redis connections and mock Redis for testing.
Features
- Singleton Pattern: Easy-to-use singleton Redis client instance
- Mock Support: Built-in mock Redis for testing environments
- TypeScript Support: Full type definitions and IntelliSense support
- JSON Serialization: Automatic JSON serialization/deserialization for objects
- Comprehensive Operations: Support for strings, hashes, sets, and lists
- Caching Framework: Abstract caching data management system
- Logging Integration: Built-in logging with log4js
Installation
npm install @ticatec/redis-client ioredis ioredis-mock log4jsQuick Start
Basic Usage
import RedisClient from '@ticatec/redis-client';
// Initialize with real Redis
await RedisClient.init({
host: '127.0.0.1',
port: 6379,
// ... other ioredis options
});
const client = RedisClient.getInstance();
// String operations
await client.set('key', 'value', 3600); // with 1-hour TTL
const value = await client.get('key');
// Object operations with JSON serialization
await client.set('user', { name: 'John', age: 30 });
const user = await client.getObject('user');Testing with Mock Redis
// Initialize with mock Redis (pass null)
await RedisClient.init(null);
const client = RedisClient.getInstance();
await client.set('testKey', 'testValue');
const value = await client.get('testKey');
console.log(value); // 'testValue'API Reference
Core Methods
String Operations
set(key, value, seconds?)- Set key-value pair with optional TTLget(key)- Get value by keygetObject(key)- Get and parse JSON objectdel(key)- Delete keyexpiry(key, seconds)- Set expiration time
Hash Operations
hset(key, data, seconds?)- Set hash fieldshget(key, field)- Get hash field valuehgetall(key)- Get all hash fieldshsetnx(key, field, value)- Set hash field if not exists
Set Operations
sadd(key, members, seconds)- Add members to setscard(key)- Get set cardinalityisSetMember(key, value)- Check set membership
List Operations
rpush(key, data, seconds?)- Push to list taillrange(key, start, end)- Get list rangelrangeObject(key, start, end)- Get list range and parse JSONllen(key)- Get list lengthlpop(key)- Pop from list head
Caching Framework
The caching framework provides a simple and flexible way to manage cached data with automatic key generation and TTL handling.
AbstractCachedData
Abstract base class for implementing cached data operations. It provides three core methods: load(), save(), and clean():
import { AbstractCachedData, GetKey } from '@ticatec/redis-client';
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
// Define a user cache using partial keys for flexibility
class UserCache extends AbstractCachedData<User> {
constructor() {
// Key generator function accepts partial User object and TTL (1 hour)
super((key: Partial<User>) => `user:${key.id}`, 3600);
}
// Custom method to get user data with caching
async getUser(id: number): Promise<User | null> {
try {
// Try to load from cache using partial key
const cached = await this.load({ id });
if (cached) {
return cached;
}
} catch (error) {
console.warn('Cache miss for user:', id);
}
// If not in cache, fetch from database
const user = await this.fetchUserFromDatabase(id);
if (user) {
// Save to cache - the getKey function will extract id from user object
await this.save(user);
}
return user;
}
// Custom method to update user data
async updateUser(id: number, userData: Partial<User>): Promise<void> {
// Update in database
const updatedUser = await this.updateUserInDatabase(id, userData);
// Update cache with new data
if (updatedUser) {
await this.save(updatedUser);
}
}
// Custom method to invalidate user cache
async invalidateUser(id: number): Promise<void> {
await this.clean({ id });
}
private async fetchUserFromDatabase(id: number): Promise<User | null> {
// Your database logic here
return null;
}
private async updateUserInDatabase(id: number, userData: Partial<User>): Promise<User | null> {
// Your database update logic here
return null;
}
}CachedDataManager
Singleton manager for registering and retrieving caching instances:
import { CachedDataManager } from '@ticatec/redis-client';
// Initialize the manager
const manager = CachedDataManager.getInstance();
// Create and register cache instances
const userCache = new UserCache();
manager.register(UserCache, userCache);
// Retrieve and use cache instances anywhere in your application
const getUserCache = () => manager.get(UserCache);
// Usage in your application
async function handleUserRequest(userId: number): Promise<User | null> {
const cache = getUserCache();
if (cache) {
return await cache.getUser(userId);
}
return null;
}Advanced Usage Patterns
1. Different cache types with various TTLs:
class SessionCache extends AbstractCachedData<{ sessionId: string, data: any }> {
constructor() {
super(key => `session:${key.sessionId}`, 1800); // 30 minutes
}
}
class ConfigCache extends AbstractCachedData<{ key: string, value: any }> {
constructor() {
super(key => `config:${key.key}`, 86400); // 24 hours
}
}2. Composite keys for complex scenarios:
interface UserPost {
userId: number;
postId: number;
title: string;
content: string;
}
class UserPostsCache extends AbstractCachedData<UserPost[]> {
constructor() {
super(
(key: { userId: number, page: number }) =>
`user:${key.userId}:posts:page:${key.page}`,
3600 // 1 hour
);
}
async getUserPostsPage(userId: number, page: number): Promise<UserPost[]> {
const key = { userId, page };
try {
return await this.load(key);
} catch (error) {
// Cache miss, fetch from API
const posts = await this.fetchPostsFromAPI(userId, page);
await this.save(posts);
return posts;
}
}
private async fetchPostsFromAPI(userId: number, page: number): Promise<UserPost[]> {
// Your API logic here
return [];
}
}Configuration
Redis Configuration
Pass any valid ioredis configuration object:
await RedisClient.init({
host: 'localhost',
port: 6379,
password: 'your-password',
db: 0,
retryDelayOnFailover: 100,
maxRetriesPerRequest: 3,
lazyConnect: true
});Logging
The client uses log4js for logging. Configure logging in your application:
import log4js from 'log4js';
log4js.configure({
appenders: {
console: { type: 'console' }
},
categories: {
default: { appenders: ['console'], level: 'info' },
RedisClient: { appenders: ['console'], level: 'debug' }
}
});Error Handling
The client provides built-in error handling and logging:
- Connection errors are logged automatically
- JSON parsing errors are handled gracefully
- Redis command errors are propagated to the caller
Best Practices
- Initialize once: Call
RedisClient.init()only once in your application - Use mock for tests: Always use
init(null)for unit tests - Handle JSON carefully: Use
getObject()for JSON data,get()for strings - Set appropriate TTLs: Always consider setting expiration times for cached data
- Use caching framework: Extend
AbstractCachedDatafor complex caching logic
Dependencies
- ioredis (^5.3.2): Redis client for Node.js
- ioredis-mock (^8.9.0): Mock Redis implementation for testing
- log4js (peer dependency): Logging framework
License
MIT License. See LICENSE file for details.
Contributing
Contributions are welcome! Please submit issues and pull requests to the GitHub repository.
Contact
- Email: [email protected]
- Repository: https://github.com/ticatec/redis-client
