Frequently Asked Questions
General
What is @express-route-cache?
@express-route-cache is a production-grade caching middleware for Express.js. It adds three critical features that most Express caching solutions lack:
- O(1) Invalidation via Epoch Versioning — no Redis
SCAN, noKEYScommand - Stale-While-Revalidate (SWR) — serve instantly, refresh in background
- Stampede Protection — request coalescing prevents thundering herds
It supports Redis (ioredis), Memcached (memjs), and in-memory storage adapters.
How is it different from apicache or express-cache-controller?
| Feature | @express-route-cache | apicache | express-cache-controller |
|---|---|---|---|
| O(1) Invalidation | ✅ | ❌ (SCAN) | ❌ |
| SWR Background Refresh | ✅ | ❌ | ❌ |
| Stampede Protection | ✅ | ❌ | ❌ |
| Redis Support | ✅ | ✅ | ❌ |
| Memcached Support | ✅ | ❌ | ❌ |
| Binary Data (images) | ✅ | ❌ | ❌ |
| TypeScript-first | ✅ | ❌ | ❌ |
See the full comparison →
Does it work with TypeScript?
Yes. The library is written entirely in TypeScript and ships with full type definitions. No @types/ packages needed.
Is it production-ready?
Yes. It is designed for high-throughput distributed environments:
- Scales to millions of keys — O(1) invalidation never degrades
- Multi-instance safe — Redis adapter shares cache across all pods
- Memory-safe —
maxBodySizeprevents OOM from large responses - Resilient — Stampede protection means no thundering herd on deploys
Configuration
What is the difference between staleTime and gcTime?
This mirrors TanStack Query's naming convention:
staleTime— How long a response is considered "fresh". During this period, all requests are served from cache without any background work.gcTime— How long a stale response stays in the cache. When SWR is enabled, responses in thestaleTime → gcTimewindow are served instantly and trigger a background refresh.
createCache({
staleTime: 60, // Fresh for 60s — zero DB calls
gcTime: 3600, // Stays cached for 1 hour (stale)
swr: true, // Serve stale, refresh in background
});Can I disable caching for specific routes?
Yes, use enabled: false per-route:
app.get('/health', cache.route({ enabled: false }), handler);Or toggle globally:
const cache = createCache({ enabled: process.env.NODE_ENV !== 'test' });How do I cache user-specific data?
Use the vary option to namespace the cache per user, or a custom key function:
// Vary by Authorization header (one cache entry per token)
cache.route({ vary: ['Authorization'] });
// Or use a fully custom key
cache.route({ key: (req) => `user:${req.user.id}:profile` });Invalidation
How does O(1) cache invalidation work?
Each route pattern (e.g., /api/posts) has an integer "epoch" counter stored alongside the cache. When you call cache.invalidate('/api/posts'):
- The epoch for
/api/postsis incremented (a singleINCRcommand in Redis). - All future requests look for cache keys containing the new epoch.
- Old entries still exist in Redis but are never queried — they expire naturally via TTL.
No SCAN, no KEYS, no blocking operations. O(1) regardless of how many cached entries exist.
Does invalidation work across multiple server instances?
Yes, when using the Redis adapter. All instances share the same Redis epoch counters and cache store, so invalidating on one instance immediately affects all others.
What triggers autoInvalidate?
When autoInvalidate: true is set, the library automatically increments the epoch for the matching route pattern after any successful POST, PUT, PATCH, or DELETE request (i.e., 2xx response).
Performance
Does caching affect my event loop?
No. All cache reads and writes are async. The in-memory adapter uses a simple Map with zero blocking calls. The Redis and Memcached adapters use async network calls that do not block the Node.js event loop.
What happens if Redis goes down?
If the Redis adapter fails to connect or throws an error, the request falls through to your Express handler as a cache MISS — the cache is skipped gracefully and your application continues to function normally.
Licensing
Is @express-route-cache free?
Yes. It is 100% free and open source under the MIT License. View on GitHub →