@ticatec/redis-client
Version:
A lightweight TypeScript wrapper around ioredis with singleton pattern support, mock Redis for testing, and abstract caching framework.
320 lines (244 loc) • 8.65 kB
Markdown
# Ticatec Redis Client
[中文](./README_CN.md) | English
[](https://www.npmjs.com/package/@ticatec/redis-client)
[](https://opensource.org/licenses/MIT)
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
```bash
npm install /redis-client ioredis ioredis-mock log4js
```
## Quick Start
### Basic Usage
```typescript
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
```typescript
// 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 TTL
- `get(key)` - Get value by key
- `getObject(key)` - Get and parse JSON object
- `del(key)` - Delete key
- `expiry(key, seconds)` - Set expiration time
#### Hash Operations
- `hset(key, data, seconds?)` - Set hash fields
- `hget(key, field)` - Get hash field value
- `hgetall(key)` - Get all hash fields
- `hsetnx(key, field, value)` - Set hash field if not exists
#### Set Operations
- `sadd(key, members, seconds)` - Add members to set
- `scard(key)` - Get set cardinality
- `isSetMember(key, value)` - Check set membership
#### List Operations
- `rpush(key, data, seconds?)` - Push to list tail
- `lrange(key, start, end)` - Get list range
- `lrangeObject(key, start, end)` - Get list range and parse JSON
- `llen(key)` - Get list length
- `lpop(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()`:
```typescript
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:
```typescript
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:**
```typescript
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:**
```typescript
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:
```typescript
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:
```typescript
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
1. **Initialize once**: Call `RedisClient.init()` only once in your application
2. **Use mock for tests**: Always use `init(null)` for unit tests
3. **Handle JSON carefully**: Use `getObject()` for JSON data, `get()` for strings
4. **Set appropriate TTLs**: Always consider setting expiration times for cached data
5. **Use caching framework**: Extend `AbstractCachedData` for 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](LICENSE) file for details.
## Contributing
Contributions are welcome! Please submit issues and pull requests to the [GitHub repository](https://github.com/ticatec/redis-client).
## Contact
- Email: huili.f@gmail.com
- Repository: https://github.com/ticatec/redis-client