Skip to main content

Command Palette

Search for a command to run...

🧠 Redis in Backend System Design (Node.js Edition)

Published
8 min readView as Markdown

🔍 The Problem Redis Solves

Modern applications demand speed, scalability, and resilience. But databases like PostgreSQL or MongoDB are disk-based and not optimized for high-frequency access patterns, like:

  • Reading the same user profile 100,000 times a day

  • Throttling requests from millions of clients

  • Handling bursty job queues

  • Broadcasting chat messages in real-time

Here’s where Redis fits in.

Redis is not a replacement for your database. It’s a performance-enhancing layer, sitting beside your DB and application, handling use-cases where memory, atomicity, and instant access matter.


🧱 Redis as a Strategic Component

Let’s go use-case by use-case, and ask WHY Redis? WHEN Redis?


1. ✅ Caching

Scenario:

You're building a product API. Each product page shows availability, price, rating, and stock — data stored in your SQL DB.

Now, 10,000 users hit the same product page. Do you want 10,000 DB calls?

Why Redis:

  • Redis is in-memory, making reads orders of magnitude faster than disk.

  • You can cache the result for a product ID.

  • Redis supports TTL, meaning data can expire automatically.

When to Use:

  • High-read, low-write objects (e.g., product catalogs, user profiles)

  • Expensive computations or joins

  • Search results, landing page content

Strategic Insight:

Redis becomes your first responder; the DB is your source of truth.


2. 🔐 Session Management

Scenario:

You’ve built an Express or NestJS-based authentication flow. Users log in, and a session or token must persist between requests.

Why Redis:

  • Session data needs to be fast to access and shared across instances (in a multi-server setup).

  • Redis provides a central, fast key-value store.

  • Most session middlewares (like express-session) have a Redis adapter.

When to Use:

  • Stateless authentication with short-lived tokens

  • Stateful session management (e.g., for internal tools)

  • Login-based SaaS apps with frequent auth lookups

Strategic Insight:

Redis is shared state across nodes — your user’s login persists even if your server restarts or scales horizontally.


3. 📈 API Rate Limiting

Scenario:

Your public API is getting abused — some users send 1000 requests per second.

You want to throttle access: no more than 100 requests/minute per user.

Why Redis:

  • Redis can atomically increment counters per key (per user).

  • It supports expiring counters, which reset automatically after a time window.

  • It's blazing fast and can handle thousands of increments per second.

When to Use:

  • Public-facing APIs

  • Login attempt limits (prevent brute force)

  • Per-user pricing tiers (basic vs premium)

Strategic Insight:

This is infrastructure-level control. Without Redis or similar, your backend has no memory of per-user usage. Redis gives it a short-term memory.


4. 🎯 Job Queues & Background Workers

Scenario:

You don’t want to send emails or process images during a user request. Instead, you want to offload those jobs.

Why Redis:

  • Redis supports queues using Lists (LPUSH, BRPOP).

  • It’s extremely fast, reliable, and works well with background workers (e.g., Bull, Bee-Queue).

  • Many job queue libraries in Node.js are Redis-backed.

When to Use:

  • Email notifications

  • Invoice generation

  • Image processing

  • Any async task where response time matters

Strategic Insight:

Your API stays fast, your jobs run reliably — all because Redis acts as a message broker for decoupled execution.


5. 🥇 Real-Time Leaderboards

Scenario:

You run a game or coding competition platform (like LeetCode). You need a leaderboard sorted by score, updated live.

Why Redis:

  • Redis’s Sorted Sets (ZADD, ZREVRANGE) are perfect for ranking users by score.

  • You can increment a user’s score atomically.

  • You can query top-N in milliseconds.

When to Use:

  • Gaming apps

  • Learning platforms (e.g., XP, levels)

  • Competitive platforms (speed, accuracy, score)

Strategic Insight:

Databases can calculate rankings — but not fast enough to feel “live.” Redis gives live ranking at memory speed.


6. 📣 Real-Time Pub/Sub

Scenario:

You’re building a chat app, live notifications, or a system that needs event broadcasting (e.g., “User X commented on your post”).

Why Redis:

  • Redis has native pub/sub channels.

  • Messages are instantly broadcast to all subscribed services.

  • Great for event propagation across microservices.

When to Use:

  • Live chat

  • Notification systems

  • Microservice communication

Strategic Insight:

You’re not just sending data — you’re building a reactive backend. Redis acts like your internal WebSocket server for service-to-service comms.


🧠 When NOT to Use Redis

Redis is amazing, but not for everything:

CaseAvoid Redis When...
Durable dataRedis is volatile by default (though AOF/RDB persistence helps). Use Postgres/MySQL/Mongo for critical records.
Huge datasetsRedis runs in-memory. Large datasets can exhaust RAM quickly and become expensive.
Complex queryingRedis doesn't support joins, aggregations, or advanced querying. Use traditional DBs for those.

🛡 Production Considerations

If you’re serious about using Redis in production:

  • Enable persistence if you can't afford data loss

  • ✅ Use Redis Sentinel for HA (failover)

  • ✅ Use Redis Cluster for horizontal scaling

  • ✅ Apply key naming conventions (e.g., user:123:session)

  • ✅ Monitor memory usage — Redis is RAM-limited

  • ✅ Use TTL wisely to avoid stale or bloated keys


🧩 Redis in the Bigger Picture

In a Node.js microservices environment, Redis may sit at multiple architectural levels:

  • As a cache in front of your DB

  • As a queue between your API and worker pool

  • As a pub/sub layer between microservices

  • As a session/token store between users and auth service

  • As a coordination layer for distributed systems (e.g., locks)

Redis isn’t just a cache. It's a multi-role utility that, when used wisely, can optimize performance, scalability, and responsiveness across your system.


🎓 Final Thoughts

Using Redis well isn’t about knowing commands like SET, GET, or LPUSH. It’s about knowing where Redis makes architectural sense — where it helps you:

  • Minimize latency

  • Maximize throughput

  • Offload compute

  • Decouple services

Master Redis not just as a tool — but as a design pattern for building systems that scale.


Perfect — let’s now dive into the Node.js + Redis code part, based on the real-world system design use cases we just discussed. These snippets are written in a clean, modular style, ideal for production backend apps (e.g., Express.js, NestJS, or any Node-based architecture).


🧩 Redis in Node.js: Code Snippets for Real Scenarios


✅ 1. Redis Setup

npm install redis
// redisClient.js
const { createClient } = require('redis');

const redisClient = createClient({
  url: 'redis://localhost:6379',
});

redisClient.on('error', (err) => console.error('Redis error:', err));
redisClient.connect();

module.exports = redisClient;

⚡ 2. Caching API Responses

// productService.js
const redis = require('./redisClient');
const db = require('./fakeDB'); // assume DB function

async function getProduct(productId) {
  const cacheKey = `product:${productId}`;
  const cached = await redis.get(cacheKey);

  if (cached) {
    return JSON.parse(cached); // cache hit
  }

  const product = await db.getProductById(productId); // cache miss
  await redis.setEx(cacheKey, 3600, JSON.stringify(product)); // cache for 1 hour
  return product;
}

🔐 3. Session Management with Redis

npm install express-session connect-redis
// session.js
const session = require('express-session');
const RedisStore = require('connect-redis').default;
const redisClient = require('./redisClient');

const store = new RedisStore({ client: redisClient });

module.exports = session({
  store,
  secret: 's3cr3t!',
  resave: false,
  saveUninitialized: false,
  cookie: { maxAge: 1000 * 60 * 15 }, // 15 minutes
});
// app.js
const express = require('express');
const sessionMiddleware = require('./session');

const app = express();
app.use(sessionMiddleware);

app.get('/', (req, res) => {
  req.session.views = (req.session.views || 0) + 1;
  res.send(`Session views: ${req.session.views}`);
});

🚦 4. Rate Limiting (Per IP)

// rateLimiter.js
const redis = require('./redisClient');

async function rateLimiter(req, res, next) {
  const ip = req.ip;
  const key = `rate:${ip}`;

  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, 60); // 1 minute window

  if (count > 100) {
    return res.status(429).json({ message: 'Rate limit exceeded' });
  }

  next();
}

module.exports = rateLimiter;
// app.js
const rateLimiter = require('./rateLimiter');
app.use(rateLimiter);

📬 5. Redis Queue (Job Producer/Worker)

Producer (enqueue job):

// emailQueue.js
const redis = require('./redisClient');

async function enqueueEmail(to) {
  const job = JSON.stringify({ task: 'sendEmail', to });
  await redis.lPush('emailQueue', job);
}

Worker (process job):

// worker.js
const redis = require('./redisClient');

async function processQueue() {
  while (true) {
    const job = await redis.brPop('emailQueue', 0);
    const data = JSON.parse(job.element);
    console.log(`Sending email to: ${data.to}`);
    // simulate processing...
  }
}

processQueue();

🥇 6. Leaderboard with Sorted Sets

// leaderboard.js
const redis = require('./redisClient');

async function updateScore(userId, score) {
  await redis.zIncrBy('leaderboard', score, userId);
}

async function getTopUsers(limit = 10) {
  return await redis.zRevRangeWithScores('leaderboard', 0, limit - 1);
}

📣 7. Pub/Sub for Notifications or Chat

Publisher:

// publisher.js
const redis = require('./redisClient');

async function sendNotification(message) {
  await redis.publish('notifications', message);
}

Subscriber:

// subscriber.js
const { createClient } = require('redis');
const subscriber = createClient();
await subscriber.connect();

await subscriber.subscribe('notifications', (msg) => {
  console.log('Notification received:', msg);
});

🧪 Redis Testing (Optional)

Try these manually with Redis CLI:

redis-cli
> GET product:123
> ZREVRANGE leaderboard 0 4 WITHSCORES
> LLEN emailQueue

📌 Best Practices Summary in Code

TaskRedis Commands
CachingSETEX, GET
Sessionvia connect-redis
Rate LimitINCR, EXPIRE
Queue/JobsLPUSH, BRPOP
LeaderboardZINCRBY, ZREVRANGE
Pub/SubPUBLISH, SUBSCRIBE