ioredis
ioredis is the standard Redis client for Node.js: standalone, Sentinel, and Cluster support with TypeScript-friendly APIs.
Search across all documentation pages
ioredis is the standard Redis client for Node.js: standalone, Sentinel, and Cluster support with TypeScript-friendly APIs.
Quick-reference recipe card - copy-paste ready.
import Redis from "ioredis";
export const redis = new Redis(process.env.REDIS_URL!, {
maxRetriesPerRequest: 3,
enableReadyCheck: true,
lazyConnect: false,
});
redis.on("error", (err) => {
console.error({ msg: "redis_error", err: err.message });
});
export async function cacheGet(key: string) {
return redis.get(key);
}When to reach for this:
// src/redis.ts
import Redis from "ioredis";
function createRedis() {
const url = process.env.REDIS_URL!;
if (process.env.REDIS_CLUSTER === "true") {
return new Redis.Cluster([{ host: process.env.REDIS_HOST!, port: 6379 }], {
redisOptions: { password: process.env.REDIS_PASSWORD },
});
}
return new Redis(url, { maxRetriesPerRequest: 3 });
}
export const redis = createRedis();
// src/cache/batch.ts
export async function mgetJson<T>(keys: string[]): Promise<(T | null)[]> {
if (keys.length === 0) return [];
const pipeline = redis.pipeline();
keys.forEach((k) => pipeline.get(k));
const results = await pipeline.exec();
return (results ?? []).map(([err, val]) => {
if (err || val == null) return null;
return JSON.parse(val as string) as T;
});
}
// Graceful shutdown
process.on("SIGTERM", async () => {
await redis.quit();
});What this demonstrates:
quit() on shutdown for clean connection close| Option | Purpose |
|---|---|
maxRetriesPerRequest | Limit retries per command (BullMQ often sets null) |
connectTimeout | Fail fast when Redis unreachable |
tls | ElastiCache in-transit encryption |
lazyConnect | Defer connect until first command |
const pipe = redis.pipeline();
pipe.set("a", "1");
pipe.incr("counter");
await pipe.exec();
await redis.mset("a", "1", "b", "2");MULTI/EXEC transactions: atomic when all keys same slot (Cluster caveat)const script = `
local current = redis.call('GET', KEYS[1])
if current == false then
redis.call('SET', KEYS[1], ARGV[1], 'EX', ARGV[2])
return 1
end
return 0
`;
await redis.eval(script, 1, "lock:job", "token", "30");import fp from "fastify-plugin";
import { redis } from "./redis";
export const redisPlugin = fp(async (fastify) => {
fastify.decorate("redis", redis);
fastify.addHook("onClose", async () => {
await redis.quit();
});
});maxRetriesPerRequest: null on blocking commands - separate client instances for BullMQ vs app cacheCROSSSLOT errors. Fix: hash tags {tenant}:key or separate keys.error events crash Node. Fix: redis.on("error", ...).| Alternative | Use When | Don't Use When |
|---|---|---|
node-redis (official) | Prefer official client | Need mature Cluster patterns today |
In-memory lru-cache | Single-instance dev tool | Multi-replica consistency |
| Memcached | Simple GET/SET at huge scale | Need data structures, streams, Lua |
| Upstash HTTP Redis | Edge without TCP Redis | Ultra-low latency LAN workloads |
Both work on Node 24. ioredis is widely used with BullMQ and Cluster. Pick one per codebase.
One per process for cache + dedicated BullMQ connections for workers. Sum across replicas.
Use rediss:// scheme or explicit tls: {} option per provider docs.
ioredis multiplexes on one connection for most commands. Blocking commands may need extra connections.
ioredis-mock for unit tests; Testcontainers Redis for integration tests.
SET key val EX seconds or SETEX. Use TTL on every cache key by policy.
Separate subscriber connection - subscriber mode blocks other commands on same connection.
Functions, ACL improvements - verify managed provider version before adopting.
Provide REDIS injection token wrapping singleton ioredis instance.
Track command latency, error rate, connected clients metric from Redis INFO.
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026