Overview
Full-featured Redis client with caching, pub/sub, and hash operations.
Features
- Cache-aside pattern
- Pub/Sub support
- Hash and list operations
- Connection management
Installation
npx hanma add redis-service --framework nest
Dependencies
ioredis
Code
import {
Injectable,
OnModuleInit,
OnModuleDestroy,
Logger,
} from "@nestjs/common";
import Redis from "ioredis";
@Injectable()
export class RedisService implements OnModuleInit, OnModuleDestroy {
private readonly logger = new Logger(RedisService.name);
private client: Redis;
private subscriber: Redis;
private publisher: Redis;
async onModuleInit() {
const redisConfig = {
host: process.env.REDIS_HOST || "localhost",
port: parseInt(process.env.REDIS_PORT || "6379", 10),
password: process.env.REDIS_PASSWORD || undefined,
db: parseInt(process.env.REDIS_DB || "0", 10),
retryStrategy: (times: number) => {
if (times > 10) {
this.logger.error("Redis connection failed after 10 retries");
return null;
}
return Math.min(times * 100, 3000);
},
};
this.client = new Redis(redisConfig);
this.subscriber = new Redis(redisConfig);
this.publisher = new Redis(redisConfig);
this.client.on("connect", () => {
this.logger.log("Connected to Redis");
});
this.client.on("error", (err) => {
this.logger.error("Redis error", err);
});
}
async onModuleDestroy() {
await Promise.all([
this.client.quit(),
this.subscriber.quit(),
this.publisher.quit(),
]);
this.logger.log("Disconnected from Redis");
}
async isHealthy(): Promise<boolean> {
try {
const result = await this.client.ping();
return result === "PONG";
} catch {
return false;
}
}
// Basic cache operations
async get<T = string>(key: string): Promise<T | null> {
const value = await this.client.get(key);
if (!value) return null;
try {
return JSON.parse(value) as T;
} catch {
return value as unknown as T;
}
}
async set(key: string, value: unknown, ttlSeconds?: number): Promise<void> {
const serialized = typeof value === "string" ? value : JSON.stringify(value);
if (ttlSeconds) {
await this.client.setex(key, ttlSeconds, serialized);
} else {
await this.client.set(key, serialized);
}
}
async del(key: string): Promise<void> {
await this.client.del(key);
}
async exists(key: string): Promise<boolean> {
const result = await this.client.exists(key);
return result === 1;
}
// Cache-aside pattern
async getOrSet<T>(
key: string,
factory: () => Promise<T>,
ttlSeconds: number
): Promise<T> {
const cached = await this.get<T>(key);
if (cached !== null) return cached;
const value = await factory();
await this.set(key, value, ttlSeconds);
return value;
}
// Pub/Sub
async publish(channel: string, message: unknown): Promise<void> {
const serialized = typeof message === "string" ? message : JSON.stringify(message);
await this.publisher.publish(channel, serialized);
}
async subscribe(
channel: string,
callback: (message: string, channel: string) => void
): Promise<void> {
await this.subscriber.subscribe(channel);
this.subscriber.on("message", (ch, msg) => {
if (ch === channel) {
callback(msg, ch);
}
});
}
async unsubscribe(channel: string): Promise<void> {
await this.subscriber.unsubscribe(channel);
}
// Hash operations
async hget<T = string>(key: string, field: string): Promise<T | null> {
const value = await this.client.hget(key, field);
if (!value) return null;
try {
return JSON.parse(value) as T;
} catch {
return value as unknown as T;
}
}
async hset(key: string, field: string, value: unknown): Promise<void> {
const serialized = typeof value === "string" ? value : JSON.stringify(value);
await this.client.hset(key, field, serialized);
}
async hgetall<T = Record<string, string>>(key: string): Promise<T | null> {
const result = await this.client.hgetall(key);
if (!result || Object.keys(result).length === 0) return null;
return result as unknown as T;
}
// List operations
async lpush(key: string, ...values: unknown[]): Promise<number> {
const serialized = values.map((v) =>
typeof v === "string" ? v : JSON.stringify(v)
);
return this.client.lpush(key, ...serialized);
}
async rpop<T = string>(key: string): Promise<T | null> {
const value = await this.client.rpop(key);
if (!value) return null;
try {
return JSON.parse(value) as T;
} catch {
return value as unknown as T;
}
}
}
Usage
const value = await this.redis.get('key');
await this.redis.set('key', value, 3600);
Dependencies
ioredis
Frequently Asked Questions
How do I install Redis Service?
Run the following command:
npx hanma add redis-service --framework nestWhat dependencies are required?
This snippet requires: ioredis. Make sure to install them before using the snippet.
How do I use this snippet?
Copy the code and paste it into your project. The snippet will be saved to
src/providers/redis.service.ts.Can I customize this snippet?
Yes! All snippets are copy-paste code that you own. Feel free to modify them to fit your needs.
Does this work with other frameworks?
This snippet is designed for the specific framework. Check the other framework pages for equivalent implementations.