Every cached key can have a time-to-live (TTL): a timer after which the cache deletes it automatically. TTLs keep data fresh and stop memory from filling up forever.
How to use it
await cache.set(
"popular_posts",
JSON.stringify(posts),
{ EX: 60 * 5 } // expires in 5 minutes
);
await cache.set("otp:user-123", "482910", { EX: 60 }); // gone after a minute Freshness vs. staleness
A cached value is a copy, so it can go stale when the real data changes. Two ways to handle it:
- Short TTL — accept that data may be a little out of date, and let it refresh on its own
- Invalidate on write — delete the cache key whenever the underlying data changes
Even without a TTL, a cache has limited memory. When it's full, it evicts keys to make room (commonly the least recently used). So treat every cached value as something that can disappear at any time.
Don't confuse the three "expirations"
By now you've seen three different things that "expire," and they're easy to mix up. The TTL here is the one that lives in memory:
| Expiration | What goes away | Used for |
|---|---|---|
| Cache TTL | A value in memory | Freshness |
| Object expiration | The file itself, after N days | Cost, compliance, temp files |
| Signed URL expiry | The link; the file stays | Time-limited access |
Exercise
Redis challenge · runs in your browser
Key expiration — TTL, EXPIRE, PERSIST
Commands run against an in-browser Redis mock that implements the full ioredis API. Your code works identically against a real Redis instance — just swap in new Redis() from ioredis.