iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Redis

Connecting Node to Redis: official redis client vs ioredis, pooling, pub/sub, streams, and the patterns for caches + queues.

Node — Redis

EXAMPLE
# Install: npm install redis (official) or npm install ioredis
import { createClient } from 'redis';

# ===== Connect =====
const client = createClient({
  url: process.env.REDIS_URL,        # redis://user:pass@host:port/db
  socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 2000) },
});
client.on('error', (e) => console.error('redis', e));
await client.connect();

# ===== Basic ops =====
await client.set('user:1', 'Alex');
await client.set('user:1', 'Alex', { EX: 3600 });    # TTL 1h
const v = await client.get('user:1');
await client.del('user:1');

# Hashes:
await client.hSet('user:1', { name: 'Alex', email: 'a@x.io' });
const u = await client.hGetAll('user:1');

# Lists:
await client.lPush('queue', JSON.stringify({ id: 1 }));
const job = await client.lPop('queue');

# Sets / Sorted sets:
await client.sAdd('tags', 'vip');
await client.zAdd('leaderboard', { score: 100, value: 'alice' });

# ===== Pub/Sub =====
const sub = client.duplicate();
await sub.connect();
await sub.subscribe('chan', (message, channel) => {
  console.log(channel, message);
});

await client.publish('chan', 'hello');

# ===== Streams =====
await client.xAdd('events', '*', { type: 'login', user: 'alex' });
const messages = await client.xRead([{ key: 'events', id: '0' }], { COUNT: 10 });

# ===== ioredis (alternative) =====
# npm install ioredis
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

await redis.set('foo', 'bar');
await redis.get('foo');

# ioredis is widely used; supports cluster, sentinel, pipelining.

# ===== Cache pattern =====
async function getUser(id) {
  const cached = await client.get(\`user:${id}\`);
  if (cached) return JSON.parse(cached);
  const user = await db.users.findById(id);
  await client.set(\`user:${id}\`, JSON.stringify(user), { EX: 600 });
  return user;
}

# Cache invalidation:
async function updateUser(id, data) {
  await db.users.update(id, data);
  await client.del(\`user:${id}\`);
}

# ===== Rate limit =====
async function tryRequest(ip) {
  const key = \`rl:${ip}\`;
  const count = await client.incr(key);
  if (count === 1) await client.expire(key, 60);
  if (count > 100) throw new Error('rate limit');
}

# ===== BullMQ for job queues =====
# npm install bullmq
import { Queue, Worker } from 'bullmq';
const queue = new Queue('emails', { connection: { host: 'localhost', port: 6379 } });
await queue.add('send', { to: 'a@x.io', subject: 'hi' });

const worker = new Worker('emails', async (job) => {
  await sendEmail(job.data);
}, { connection: { host: 'localhost', port: 6379 } });

# ===== Patterns =====
# - One client per process; duplicate for sub
# - TTL on every cache key
# - JSON.stringify around objects (Redis strings)
# - BullMQ over custom queue logic

# ===== Pitfalls =====
# - Forgetting reconnectStrategy -> hard failures on transient blips
# - Storing big objects (>1MB) per key
# - Pub/Sub on a cluster (subscriptions are per-node)
# - Missing TTL -> memory growth

Why it matters

Node + Redis: official client or ioredis, one connection per process, TTL on caches, BullMQ for queues, pub/sub via duplicated client. The patterns are small; the discipline is set-TTL and avoid storing big values.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
import { createClient } from 'redis';
const redis = await createClient().connect();
await redis.set('hits', 1);
await redis.incr('hits');
Try it Yourself »

Discussion

Loading…