Caching Best Practices
Define invalidation before caching hot paths. Redis is fast; wrong cache design is slow to debug.
How to Use This List
- Require cache design note in PRs that add new key families
- Run load tests with Redis disabled to verify degrade path
- Pair with Caching Basics for patterns
- Revisit TTLs after schema or API shape changes
A - Key Design
- Namespaced keys:
env:service:version:entity:id. - Invalidation owner named in code comment or ADR per key family.
- TTL on every key unless explicit eternal config with change stream invalidation.
- Bounded key cardinality - no unbounded user-generated strings in keys.
- Version prefix bump when serialized shape changes.
B - Consistency
- Cache-aside with DB as source of truth for most reads.
- Delete or update cache on write in same use case as DB mutation.
- Stampede protection on hottest keys (lock, jitter, early refresh).
- Short TTL for negative caching (miss results) to protect DB.
- No caching of strongly consistent financial balances without explicit product sign-off.
C - Operations
- Singleton ioredis client per process with error handler.
- Memory alerts on Redis instance (maxmemory, evictions).
- Hit/miss metrics per endpoint or key prefix in staging before prod.
- Separate Redis logical DB or instance for BullMQ vs user-facing cache when load is high.
- TLS and AUTH enabled on managed Redis in production.
D - Security
- No secrets in cache values unless encrypted at application level.
- Session cookies hold ids only - see Session Stores.
- Multi-tenant isolation in every key prefix.
- Redis not exposed to public internet - VPC/private link only.
- ACL users with least privilege on Redis 6+.
E - Application Integration
- Graceful degrade when Redis timeout - log and fall back to origin.
- JSON payload size reviewed - cache DTOs not full object graphs.
- Pipeline multi-get for batch reads instead of N sequential gets.
-
redis.quit()on shutdown alongside HTTP and DB pool close. - Distributed locks held < few seconds - see Distributed Locks.
F - When Not to Cache
- Skip cache when hit ratio < 30% after measurement period.
- Skip per-user mutable data with low repeat read within TTL window.
- Prefer CDN for static public assets before Redis.
- Prefer DB index before Redis if query is slow due to missing index.
- Queue heavy recomputation instead of giant cached blobs.
FAQs
Cache every GET endpoint?
No. Profile first. Cache proven hot reads with clear invalidation.
What eviction policy?
allkeys-lru on dedicated cache nodes. Avoid OOM crashes without eviction policy.
Redis down - return 500?
Read endpoints should fall back to DB when business allows. Rate limits may fail closed.
How long should product TTL be?
5-15 minutes common for catalog. Shorter when merchandising demands fresher data.
Cache warming?
After bulk deploy or import, warm from worker or lazy on first requests with stampede guard.
Invalidate across services?
Pub/sub channel, short TTL, or event bus message - pick one pattern per entity type.
Redis Cluster ops?
Use hash tags for multi-key ops. Test failover drills on managed provider.
Biggest smell?
Mystery keys with no TTL and no owner - KEYS * discovery in incident.
NestJS CacheModule enough?
Yes if same TTL/invalidation rules enforced. Wrapper does not remove design duty.
Serverless Redis?
Upstash and similar - watch per-request pricing and latency vs VPC ElastiCache.
Related
- Caching Basics - cache-aside examples
- ioredis - client patterns
- Session Stores - cookie sessions
- Performance Basics - latency
- Resilience Basics - degrade modes
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.