Skip to main content
Practice Problems

How to implement caching in Express.js for better performance?

Caching in Express.js

Caching stores frequently requested data in a fast-access layer to reduce response time and server load. It's one of the most impactful performance optimizations.


Types of Caching

1. In-Memory Caching (node-cache)

Best for single-server deployments:

js
const NodeCache = require('node-cache'); const cache = new NodeCache({ stdTTL: 300, checkperiod: 60 }); function cacheMiddleware(duration) { return (req, res, next) => { const key = req.originalUrl; const cached = cache.get(key); if (cached) { return res.json(cached); } const originalJson = res.json.bind(res); res.json = (body) => { cache.set(key, body, duration); originalJson(body); }; next(); }; } // Cache for 5 minutes app.get('/api/products', cacheMiddleware(300), async (req, res) => { const products = await db.query('SELECT * FROM products'); res.json(products); });

2. Redis Caching (Distributed)

Best for multi-server deployments:

js
const Redis = require('ioredis'); const redis = new Redis(process.env.REDIS_URL); function redisCacheMiddleware(ttl = 300) { return async (req, res, next) => { const key = `cache:${req.originalUrl}`; try { const cached = await redis.get(key); if (cached) { return res.json(JSON.parse(cached)); } } catch (err) { console.error('Redis error:', err); } const originalJson = res.json.bind(res); res.json = async (body) => { try { await redis.setex(key, ttl, JSON.stringify(body)); } catch (err) { console.error('Redis set error:', err); } originalJson(body); }; next(); }; } app.get('/api/products', redisCacheMiddleware(600), getProducts);

3. HTTP Cache Headers

Let browsers and CDNs cache responses:

js
// Cache-Control for public cacheable content app.get('/api/products', (req, res) => { res.set('Cache-Control', 'public, max-age=300'); // 5 min res.json(products); }); // No cache for private data app.get('/api/profile', auth, (req, res) => { res.set('Cache-Control', 'private, no-cache'); res.json(user); }); // ETag-based caching const etag = require('etag'); app.get('/api/data', (req, res) => { const data = getData(); const tag = etag(JSON.stringify(data)); if (req.headers['if-none-match'] === tag) { return res.status(304).end(); // Not Modified } res.set('ETag', tag); res.json(data); });

4. Cache Invalidation

js
// Invalidate on write operations app.post('/api/products', async (req, res) => { const product = await createProduct(req.body); // Invalidate related caches cache.del('/api/products'); await redis.del('cache:/api/products'); res.status(201).json(product); }); // Pattern-based invalidation with Redis async function invalidatePattern(pattern) { const keys = await redis.keys(pattern); if (keys.length > 0) { await redis.del(...keys); } } // Invalidate all product caches await invalidatePattern('cache:/api/products*');

Caching Strategy Comparison

StrategySpeedScalabilityPersistenceUse Case
In-memoryFastestSingle serverNoSmall apps, dev
RedisVery fastMulti-serverYesProduction APIs
HTTP headersClient-sideCDN-friendlyBrowserStatic content
CDNFastest for usersGlobalEdgeAssets, public APIs

What to Cache

CacheDon't Cache
Database query resultsUser-specific data (unless Redis)
External API responsesReal-time data (stock prices)
Computed/aggregated dataAuthentication tokens
Static contentWrite-heavy endpoints

Golden rule: Cache aggressively, invalidate carefully. Start with short TTLs (30-60s) and increase based on data change frequency. Always have a way to force-invalidate when data changes.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems