Redis Caching in Node

Introduction

Redis is an in-memory data store used for caching, session storage, rate limiting, and lightweight queues. Node apps use clients like ioredis or redis to get/set keys with TTL (time-to-live). This chapter complements databases and message queues.

Prerequisites

When to Cache

Cache expensive or slow results:

  • Heavy database aggregations
  • Third-party API responses allowed to be slightly stale
  • Rendered HTML fragments

Do not cache personalized secrets without careful key design and TTL.

Connect to Redis

bash
npm install ioredis
javascript
// redis-client.mjs
import Redis from "ioredis";
 
export const redis = new Redis(process.env.REDIS_URL ?? "redis://127.0.0.1:6379");
 
redis.on("error", (err) => console.error("Redis error", err.message));

Run Redis locally via Docker:

bash
docker run -d -p 6379:6379 redis:7-alpine

Set and Get with TTL

javascript
// Cache JSON for 60 seconds
const key = "user:42:profile";
 
await redis.set(key, JSON.stringify({ id: 42, name: "Ada" }), "EX", 60);
 
const cached = await redis.get(key);
if (cached) {
  console.log("hit", JSON.parse(cached));
} else {
  console.log("miss");
}

EX 60 expires the key automatically.

Cache-Aside Pattern

javascript
async function getUserProfile(userId) {
  const key = `user:${userId}:profile`;
  const hit = await redis.get(key);
  if (hit) return JSON.parse(hit);
 
  const profile = await loadFromDatabase(userId);
  await redis.set(key, JSON.stringify(profile), "EX", 300);
  return profile;
}
 
async function loadFromDatabase(userId) {
  return { id: userId, name: "From DB" };
}
 
console.log(await getUserProfile(1));
console.log(await getUserProfile(1));

On update, invalidate cache: await redis.del(key).

Rate Limiting (Simple Counter)

javascript
async function allowRequest(ip) {
  const key = `rl:${ip}`;
  const count = await redis.incr(key);
  if (count === 1) await redis.expire(key, 60);
  return count <= 100;
}
 
console.log(await allowRequest("203.0.113.10"));

##Hashes and Sets (Brief)

javascript
// Hash — object-like
await redis.hset("session:abc", "userId", "7", "role", "admin");
const role = await redis.hget("session:abc", "role");
 
// Set — unique members
await redis.sadd("tags:post:1", "js", "web");
const tags = await redis.smembers("tags:post:1");

Mini Example: Memoize API Fetch

javascript
async function fetchPostsCached(userId) {
  const key = `posts:${userId}`;
  const cached = await redis.get(key);
  if (cached) return JSON.parse(cached);
 
  const res = await fetch(
    `https://jsonplaceholder.typicode.com/posts?userId=${userId}`
  );
  const posts = await res.json();
  await redis.set(key, JSON.stringify(posts), "EX", 120);
  return posts;
}

FAQ

Redis vs database?

Redis is fast volatile memory—primary data still lives in PostgreSQL/Mongo with Redis as cache layer.

Cache stampede?

Many misses at once—use locking, early expiry jitter, or prewarm.

Production hosting?

Managed Redis (ElastiCache, Upstash, Redis Cloud) with TLS and auth.

What comes next?

typeof and type checks.