Database queries are the bottleneck of most web applications. A query that takes 50ms under light load takes 500ms when traffic spikes. Redis eliminates this by storing frequently accessed data in memory, serving it in sub-millisecond time. It’s not just a cache though. Redis handles session storage, rate limiting, real-time pub/sub messaging, and job queues, making it the Swiss army knife of backend infrastructure for Node.js and TypeScript applications.
Caching database queries
The most immediate use of Redis is caching expensive or repetitive database calls. A product page that hits PostgreSQL on every request can serve the same data from Redis in under a millisecond after the first fetch.
import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
async function getCached<T>(key: string, ttl: number, fetcher: () => Promise<T>): Promise<T> {
const cached = await redis.get(key);
if (cached) return JSON.parse(cached);
const data = await fetcher();
await redis.set(key, JSON.stringify(data), 'EX', ttl);
return data;
}
router.get('/projects/:slug', async (req, res) => {
const project = await getCached(`project:${req.params.slug}`, 3600, () =>
db.project.findUnique({ where: { slug: req.params.slug } })
);
res.json(project);
});
The getCached helper abstracts the read-through pattern. First call hits the database and writes to Redis with a TTL. Every subsequent call within the TTL window skips the database entirely. On a Next.js application serving hundreds of concurrent users, this alone can cut response times by an order of magnitude.
Session storage
Storing sessions in Redis instead of memory or a SQL database solves two problems: it survives server restarts, and it works across multiple Node.js instances behind a load balancer.
import session from 'express-session';
import RedisStore from 'connect-redis';
app.use(
session({
store: new RedisStore({ client: redis }),
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: { secure: true, maxAge: 86400000 },
})
);
Redis stores the session as a key-value pair with automatic expiration. No cleanup jobs, no stale sessions accumulating in PostgreSQL, no sticky sessions needed at the load balancer level.
Rate limiting
Protecting API endpoints from abuse requires counting requests per client within a time window. Redis makes this atomic and fast with the INCR command and key expiration.
async function rateLimit(req: Request, res: Response, next: NextFunction) {
const key = `rate:${req.ip}`;
const current = await redis.incr(key);
if (current === 1) {
await redis.expire(key, 60);
}
if (current > 100) {
return res.status(429).json({ error: 'Too many requests' });
}
next();
}
Each IP gets a counter that resets every 60 seconds. The INCR and EXPIRE combination is atomic in Redis, so there are no race conditions even under heavy concurrent traffic. This pattern works for API rate limiting, login attempt throttling, and form submission protection.
Pub/Sub for real-time features
Redis pub/sub enables real-time communication between Node.js processes without WebSocket libraries managing state. One process publishes an event, every subscriber receives it instantly.
const publisher = new Redis(process.env.REDIS_URL!);
const subscriber = new Redis(process.env.REDIS_URL!);
subscriber.subscribe('notifications');
subscriber.on('message', (channel, message) => {
const payload = JSON.parse(message);
broadcastToClients(payload);
});
async function notify(event: string, data: unknown) {
await publisher.publish('notifications', JSON.stringify({ event, data }));
}
This pattern powers live dashboards, chat applications, and collaborative editing features. Each Node.js instance subscribes to the same Redis channel, so horizontal scaling doesn’t break real-time functionality.
Conclusion
Redis sits between the application and the database, absorbing the load that would otherwise slow everything down. Caching, sessions, rate limiting, and pub/sub cover the vast majority of use cases for Node.js backends. It’s a single dependency that eliminates entire categories of performance and scaling problems.