Caching Strategies with Redis for Next.js and FastAPI: When and What to Cache
Decision framework and working patterns for when and what to cache in a Redis-backed stack. Avoid costly staleness bugs and database fires with principled caching.
Caching Strategies with Redis for Next.js and FastAPI: When and What to Cache
Caching is where "I know what it is" and "I know when to use it" diverge—often expensively. Most teams either cache too eagerly (introducing staleness bugs) or too late (setting their database on fire). This guide gives you a decision framework and working patterns for a Redis-backed Next.js + FastAPI stack.
The Core Question: Should This Even Be Cached?
Before touching Redis, answer three questions:
- Is this data read significantly more than it's written? Roughly equal read/write ratios don't justify caching complexity.
- Is regeneration costly? Expensive DB joins, third-party API calls, and heavy computation are cache candidates. A single indexed primary-key lookup is not.
- Can you tolerate stale data, and for how long? This defines your TTL. A product catalog tolerates 60 seconds; a user's account balance does not.
If all three are yes, proceed. Otherwise, a well-indexed Postgres query is usually sufficient.
Caching in FastAPI
Your FastAPI backend is where most caching decisions live. Here's a working pattern using redis.asyncio:
This cache-aside pattern is explicit and easy to reason about. Structured cache keys (e.g., products:{category}) matter for later invalidation.
Cache-worthy in FastAPI:
- Aggregated query results (dashboards, counts)
- Third-party API responses (exchange rates, geolocation)
- Deterministic computed results
- User-scoped data with short TTLs (
user:{id}:profile, 30–120s)
Avoid caching:
- Write-heavy records
- Strict-consistency data (transactions, reservations)
- Auth tokens (use Redis for session storage instead, with different expiry logic)
Handling Failure and Cache Stampede
Redis failures shouldn't cascade to your database. Wrap cache operations in a timeout and fallback:
To mitigate cache stampede (thundering herd when a hot key expires), use probabilistic early expiration:
This spreads refresh load across the window instead of hammering the DB when TTL hits zero.
Cache Invalidation in FastAPI
TTL expiry handles most cases, but immediate invalidation is needed after writes:
For broader invalidation, use SCAN with a match pattern—it's safer than KEYS * and won't block the event loop:
Caching in Next.js
Route Handler and Server Component caching: Next.js has its own fetch cache. Control it explicitly:
This works for public, semi-static content without Redis.
When Redis matters on the Next.js side: Use it for server-side caching that persists across deployments, scales horizontally, or is shared across multiple Next.js instances (common in containerized setups).
TTL Reference
| Data type | TTL | Notes |
|---|---|---|
| Product catalog | 60–300s | Invalidate on write |
| Search results | 30–60s | Key on query hash |
| User profile | 30–120s | Key on user ID |
| Leaderboard | 15–30s | Acceptable lag |
| Third-party API | Match source TTL | Honor upstream headers |
| Session data | 24h–7d | Sliding expiry on access |
Docker Compose Setup
Set maxmemory explicitly; unbounded Redis consumption breaks shared hosts. The allkeys-lru eviction policy discards least-recently-used keys when full, preventing errors and avoiding stale data accumulation. Pair this with intentional TTLs—let expiration handle correctness, and let LRU handle overflow as a safety valve only.
The Principle That Matters
Cache the boundary between expensive and cheap. In a Next.js + FastAPI stack, that boundary usually sits at the FastAPI response layer, just above the database. Get that right, and nearly everything else defers until you actually need it.
Damian Hodgkiss
Senior Staff Engineer at Sumo Group, leading development of AppSumo marketplace. Technical solopreneur with 25+ years of experience building SaaS products.