Node.js Redis cheatsheet.

Choose

  • ioredis: feature-rich, cluster-aware.
  • node-redis: official, modern API.

ioredis

npm i ioredis
import Redis from "ioredis";

const r = new Redis("redis://localhost:6379");
await r.set("k", "v");
const v = await r.get("k");
await r.quit();

Pool / cluster (ioredis)

import Redis, { Cluster } from "ioredis";

const cluster = new Cluster([
  { host: "node1", port: 7000 },
  { host: "node2", port: 7000 },
]);

node-redis (v4+)

npm i redis
import { createClient } from "redis";

const r = await createClient({ url: "redis://localhost:6379" })
  .on("error", (e) => console.error(e))
  .connect();

await r.set("k", "v");
const v = await r.get("k");
await r.quit();

Pipeline

// ioredis
const p = r.pipeline();
p.set("a", 1);
p.set("b", 2);
const results = await p.exec();

// node-redis
const multi = r.multi();
multi.set("a", 1);
multi.set("b", 2);
const results = await multi.exec();

Transactions

// ioredis
const result = await r.multi()
  .set("a", 1)
  .incr("counter")
  .exec();

Pub/sub

// ioredis
const sub = new Redis();
sub.subscribe("channel");
sub.on("message", (channel, msg) => {
  console.log(msg);
});

// Publish on different client (subscribed cannot publish)
const pub = new Redis();
await pub.publish("channel", "hi");

Streams

// Add
await r.xadd("events", "*", "type", "click", "url", "/");

// Read
const res = await r.xread("BLOCK", 5000, "STREAMS", "events", "$");

Hashes

await r.hset("user:1", { name: "Alice", age: 30 });
const all = await r.hgetall("user:1");
await r.hget("user:1", "name");

Set / get JSON

const set = (k: string, v: unknown, ttl?: number) =>
  ttl ? r.setex(k, ttl, JSON.stringify(v)) : r.set(k, JSON.stringify(v));
const get = async <T>(k: string): Promise<T | null> => {
  const raw = await r.get(k);
  return raw ? JSON.parse(raw) : null;
};

TTL

await r.set("k", "v", "EX", 60);    // ioredis style
await r.setex("k", 60, "v");
await r.expire("k", 60);
const ttl = await r.ttl("k");

SCAN

const stream = r.scanStream({ match: "user:*", count: 100 });
stream.on("data", (keys) => {
  for (const k of keys) console.log(k);
});
stream.on("end", () => {});

Lua

const script = `return redis.call('GET', KEYS[1])`;
const val = await r.eval(script, 1, "mykey");

// Cache
r.defineCommand("myCmd", { numberOfKeys: 1, lua: script });
const val2 = await r.myCmd("mykey");

Error handling

r.on("error", (err) => console.error("Redis error:", err));
r.on("connect", () => console.log("connected"));
r.on("ready", () => console.log("ready"));
r.on("close", () => console.log("close"));
r.on("reconnecting", () => console.log("reconnecting"));

Express + Redis session

npm i express-session connect-redis ioredis
import session from "express-session";
import RedisStore from "connect-redis";

app.use(session({
  store: new RedisStore({ client: redis }),
  secret: "...",
  resave: false,
  saveUninitialized: false,
  cookie: { httpOnly: true, secure: true, sameSite: "lax", maxAge: 86400 * 7 * 1000 },
}));

BullMQ (queue lib)

npm i bullmq
import { Queue, Worker } from "bullmq";

const queue = new Queue("emails", { connection: r });
await queue.add("send", { to: "[email protected]" });

const worker = new Worker("emails", async (job) => {
  await sendEmail(job.data);
}, { connection: r });

Robust job queue on Redis.

Common mistakes

  • Single client used for pub/sub + commands (won’t work; subscribed clients can’t run other commands).
  • Not handling reconnects.
  • Forgetting await.
  • r.set(k, JSON) without stringify.
  • Mixing ioredis + node-redis API.

Read this next

If you want my BullMQ setup, it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .