In-Memory Caching
Overview
If a user visits your homepage, and the backend runs a complex SQL query that takes 200ms to calculate the 'Top 10 Trending Products', that is fine. But if 10,000 users visit your homepage simultaneously, running that identical heavy SQL query 10,000 times will melt the database. Caching solves this. You run the query exactly ONCE, and store the resulting JSON directly in lightning-fast RAM (usually using Redis or Valkey). The next 9,999 users bypass the database entirely and fetch the JSON directly from RAM in 1 millisecond.
Syntax
// Utilizing Redis (or Valkey) via the 'redis' npm package
const redis = require('redis');
const client = redis.createClient({ url: process.env.REDIS_URL });
await client.connect();
app.get('/api/trending', async (req, res) => {
// 1. CHECK THE CACHE FIRST!
const cachedData = await client.get('trending_products');
// 2. CACHE HIT! The data was in RAM. Return it instantly.
if (cachedData) {
return res.json(JSON.parse(cachedData));
}
// 3. CACHE MISS! The data is not in RAM. We must hit the heavy Database.
const products = await db.query('SELECT ... HEAVY QUERY ...');
// 4. POPULATE THE CACHE! Save the result in Redis for the next user.
// 'EX 60' means Expire in 60 seconds (Cache Invalidation)
await client.set('trending_products', JSON.stringify(products), { EX: 60 });
res.json(products);
});Common Pitfalls
- Cache Invalidation (The hardest problem in Computer Science). If you cache a user's Profile Data forever, and they update their profile picture in the database, the API will continue serving the old picture from the cache. You MUST implement strict TTLs (Time-To-Live, e.g., expire after 5 mins) or explicitly run
client.del('profile_42')every time they run anUPDATEquery. - Caching sensitive user data globally. If you cache a query like
SELECT * FROM user_bank_accountsunder a generic key likecache:accounts, User B might hit the cache and receive User A's bank details. Always append unique identifiers to cache keys for sensitive data (e.g.,cache:accounts:user_42).
Interview Questions
const cache = {})?If you scale your app to 5 Node.js servers behind a Load Balancer, local memory is completely isolated. User A hits Server 1 (populates the local cache). User A refreshes, the Load Balancer routes them to Server 2 (Cache Miss!). Redis is a centralized, external RAM store that ALL 5 servers share perfectly.
Real-World Example
Using Redis to implement a distributed, high-performance API Rate Limiter.
// Instead of keeping counts in local Node.js variables,
// Redis guarantees atomic incrementation across all servers!
const currentRequests = await redis.incr(`rate_limit:${req.ip}`);
if (currentRequests === 1) {
// If it's their first request, set the key to expire in 1 minute!
await redis.expire(`rate_limit:${req.ip}`, 60);
}
if (currentRequests > 100) {
return res.status(429).send("Too Many Requests");
}Check Your Knowledge
Test your understanding of In-Memory Caching with these quick questions.