/api-caching-strategies
Application-level caching strategies, HTTP caching, cache invalidation, and stampede prevention
$ npx -y skills add agents-inc/skills --skill api-caching-strategies --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/api-caching-strategies
Context preview
The summary Claude sees to decide when to auto-load this skill.
Application-level caching strategies, HTTP caching, cache invalidation, and stampede prevention
SKILL.md
api-caching-strategies.SKILL.mdname: api-caching-strategies
description: Application-level caching strategies, HTTP caching, cache invalidation, and stampede prevention
Caching Strategies
> **Quick Guide:** Choose the right caching strategy for your use case: cache-aside for read-heavy data, write-through for consistency, write-behind for write-heavy workloads. Always set TTL to prevent stale data and memory exhaustion. Use HTTP caching headers (Cache-Control, ETag, Last-Modified) for API responses. Prevent cache stampedes with locking or request coalescing. Measure cache hit rates before and after -- caching without metrics is guessing.
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST set TTL on ALL cached data -- cache without TTL leads to memory exhaustion and infinitely stale data)**
**(You MUST use namespaced cache keys with a consistent prefix -- generic keys cause collisions across data types)**
**(You MUST invalidate or update cache entries on writes -- serving stale data after mutation breaks user trust)**
**(You MUST implement stampede prevention (locking or coalescing) for high-traffic cache keys -- concurrent misses can overwhelm your data source)**
</critical_requirements>
---
**Auto-detection:** caching, cache-aside, write-through, write-behind, cache invalidation, TTL, Cache-Control, ETag, Last-Modified, stale-while-revalidate, s-maxage, cache stampede, thundering herd, in-memory cache, LRU cache, distributed cache, cache key, cache miss, cache hit, CDN caching, HTTP caching, conditional request, 304 Not Modified
**When to use:**
- Read-heavy endpoints fetching the same data repeatedly (cache-aside)
- Data that must stay consistent between cache and database after writes (write-through)
- API responses that benefit from HTTP caching headers (Cache-Control, ETag)
- High-traffic cache keys that risk stampede on expiration
- Reducing database load by caching expensive query results
**When NOT to use:**
- Data that must always be real-time fresh (caching adds staleness by definition)
- Simple CRUD with low traffic (caching complexity outweighs benefit)
- Development/debugging (caching obscures issues -- disable in dev)
- Premature optimization without measuring actual bottlenecks first
**Key patterns covered:**
- Cache-aside (lazy loading) with TTL
- Write-through for read-after-write consistency
- Write-behind (write-back) for write-heavy workloads
- HTTP caching: Cache-Control, ETag, Last-Modified, conditional requests
- CDN caching with s-maxage and stale-while-revalidate
- In-memory LRU caching for single-process hot data
- Cache key strategies and namespacing
- Stampede prevention (locking, request coalescing, early recomputation)
- Tag-based and pattern-based invalidation
---
<philosophy>
Philosophy
Caching trades freshness for speed. Every caching decision is a **consistency vs performance** trade-off -- understand where your use case falls on that spectrum before choosing a strategy.
**The three questions before adding caching:**
1. **Is this actually slow?** Measure first. If the uncached response is fast enough, caching adds complexity without benefit. 2. **Can I tolerate staleness?** If data must always be real-time, caching is the wrong tool. Use read replicas or materialized views instead. 3. **What is the read-to-write ratio?** Caching shines when reads vastly outnumber writes. For write-heavy workloads, consider write-behind or skip caching entirely.
**Caching is a layered system.** HTTP caching (browser and CDN) reduces requests to your server. Application-level caching (in-memory or distributed) reduces requests to your database. Apply caching at the right layer for the problem.
**When to use caching:**
- Response times exceed acceptable thresholds and the data source is the bottleneck
- The same data is fetched repeatedly across requests
- Data freshness requirements allow some staleness (even 60 seconds)
- Traffic is high enough that database load is a concern
**When NOT to use caching:**
- Data changes frequently and must always be current
- Every request returns unique data (no cache reuse)
- You have not measured the actual bottleneck yet (premature optimization)
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Cache-Aside (Lazy Loading)
The most common application-level caching pattern. The application checks the cache first, fetches from the data source on miss, and stores the result with a TTL.
const CACHE_TTL_SECONDS = 300;
const CACHE_PREFIX = "app:user";
async function getUserById(userId: string): Promise<User | null> {
const cacheKey = `${CACHE_PREFIX}:${userId}`;
const cached = await cacheStore.get(cacheKey);
if (cached) return JSON.parse(cached) as User;
const user = await db.query.users.findFirst({ where: eq(users.id, userId) });
if (!user) return null;
await cacheStore.set(cacheKey, JSON.stringify(user), { ttl: CACHE_TTL_SECONDS });
return user;
}**Why good:** TTL prevents stale data, namespaced keys prevent collisions, early return on cache hit, cache is populated lazily (only data that is actually requested gets cached)
// BAD: No TTL, generic key
async function getUser(id: string) {
const cached = await cacheStore.get(id); // No prefix -- collides with other data types
if (cached) return JSON.parse(cached);
const user = await db.query.users.findFirst({ where: eq(users.id, id) });
await cacheStore.set(id, JSON.stringify(user)); // No TTL -- never expires
return user;
}**Why bad:** No TTL means infinite staleness and eventual memory exhaustion, generic key collides with other entity types using the same ID format
**When to use:** Read-heavy endpoints where data changes infrequently relative to reads.
See [examples/core.md](examples/core.md) for generic cache wrapper and error handli
Read more
name: api-caching-strategies description: Application-level caching strategies, HTTP caching, cache invalidation, and stampede prevention
Caching Strategies
> **Quick Guide:** Choose the right caching strategy for your use case: cache-aside for read-heavy data, write-through for consistency, write-behind for write-heavy workloads. Always set TTL to prevent stale data and memory exhaustion. Use HTTP caching headers (Cache-Control, ETag, Last-Modified) for API responses. Prevent cache stampedes with locking or request coalescing. Measure cache hit rates before and after -- caching without metrics is guessing.
---
<critical_requirements>
CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST set TTL on ALL cached data -- cache without TTL leads to memory exhaustion and infinitely stale data)**
**(You MUST use namespaced cache keys with a consistent prefix -- generic keys cause collisions across data types)**
**(You MUST invalidate or update cache entries on writes -- serving stale data after mutation breaks user trust)**
**(You MUST implement stampede prevention (locking or coalescing) for high-traffic cache keys -- concurrent misses can overwhelm your data source)**
</critical_requirements>
---
**Auto-detection:** caching, cache-aside, write-through, write-behind, cache invalidation, TTL, Cache-Control, ETag, Last-Modified, stale-while-revalidate, s-maxage, cache stampede, thundering herd, in-memory cache, LRU cache, distributed cache, cache key, cache miss, cache hit, CDN caching, HTTP caching, conditional request, 304 Not Modified
**When to use:**
- Read-heavy endpoints fetching the same data repeatedly (cache-aside)
- Data that must stay consistent between cache and database after writes (write-through)
- API responses that benefit from HTTP caching headers (Cache-Control, ETag)
- High-traffic cache keys that risk stampede on expiration
- Reducing database load by caching expensive query results
**When NOT to use:**
- Data that must always be real-time fresh (caching adds staleness by definition)
- Simple CRUD with low traffic (caching complexity outweighs benefit)
- Development/debugging (caching obscures issues -- disable in dev)
- Premature optimization without measuring actual bottlenecks first
**Key patterns covered:**
- Cache-aside (lazy loading) with TTL
- Write-through for read-after-write consistency
- Write-behind (write-back) for write-heavy workloads
- HTTP caching: Cache-Control, ETag, Last-Modified, conditional requests
- CDN caching with s-maxage and stale-while-revalidate
- In-memory LRU caching for single-process hot data
- Cache key strategies and namespacing
- Stampede prevention (locking, request coalescing, early recomputation)
- Tag-based and pattern-based invalidation
---
<philosophy>
Philosophy
Caching trades freshness for speed. Every caching decision is a **consistency vs performance** trade-off -- understand where your use case falls on that spectrum before choosing a strategy.
**The three questions before adding caching:**
1. **Is this actually slow?** Measure first. If the uncached response is fast enough, caching adds complexity without benefit. 2. **Can I tolerate staleness?** If data must always be real-time, caching is the wrong tool. Use read replicas or materialized views instead. 3. **What is the read-to-write ratio?** Caching shines when reads vastly outnumber writes. For write-heavy workloads, consider write-behind or skip caching entirely.
**Caching is a layered system.** HTTP caching (browser and CDN) reduces requests to your server. Application-level caching (in-memory or distributed) reduces requests to your database. Apply caching at the right layer for the problem.
**When to use caching:**
- Response times exceed acceptable thresholds and the data source is the bottleneck
- The same data is fetched repeatedly across requests
- Data freshness requirements allow some staleness (even 60 seconds)
- Traffic is high enough that database load is a concern
**When NOT to use caching:**
- Data changes frequently and must always be current
- Every request returns unique data (no cache reuse)
- You have not measured the actual bottleneck yet (premature optimization)
</philosophy>
---
<patterns>
Core Patterns
Pattern 1: Cache-Aside (Lazy Loading)
The most common application-level caching pattern. The application checks the cache first, fetches from the data source on miss, and stores the result with a TTL.
const CACHE_TTL_SECONDS = 300;
const CACHE_PREFIX = "app:user";
async function getUserById(userId: string): Promise<User | null> {
const cacheKey = `${CACHE_PREFIX}:${userId}`;
const cached = await cacheStore.get(cacheKey);
if (cached) return JSON.parse(cached) as User;
const user = await db.query.users.findFirst({ where: eq(users.id, userId) });
if (!user) return null;
await cacheStore.set(cacheKey, JSON.stringify(user), { ttl: CACHE_TTL_SECONDS });
return user;
}**Why good:** TTL prevents stale data, namespaced keys prevent collisions, early return on cache hit, cache is populated lazily (only data that is actually requested gets cached)
// BAD: No TTL, generic key
async function getUser(id: string) {
const cached = await cacheStore.get(id); // No prefix -- collides with other data types
if (cached) return JSON.parse(cached);
const user = await db.query.users.findFirst({ where: eq(users.id, id) });
await cacheStore.set(id, JSON.stringify(user)); // No TTL -- never expires
return user;
}**Why bad:** No TTL means infinite staleness and eventual memory exhaustion, generic key collides with other entity types using the same ID format
**When to use:** Read-heavy endpoints where data changes infrequently relative to reads.
See [examples/core.md](examples/core.md) for generic cache wrapper and error handli
Showing the first part of this file.
The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?
Repo: agents-inc/skills
Other skills on agents-inc-skills.
- /ai-infrastructure-huggingface-inference
Hugging Face Inference SDK patterns for TypeScript/Node.js — InferenceClient setup, chat completion, text generation, streaming, embeddings, image generation, audio transcription, translation, summarization, and Inference Endpoints
Open skill - /ai-infrastructure-litellm
LiteLLM proxy server setup, TypeScript client patterns via OpenAI SDK, model routing, fallbacks, load balancing, spend tracking, virtual keys, and production deployment
Open skill - /ai-infrastructure-modal
Serverless GPU compute platform for AI model deployment — web endpoints, GPU functions, model serving, and TypeScript client patterns
Open skill - /ai-infrastructure-ollama
Local LLM inference with the Ollama JavaScript client -- chat, streaming, tool calling, vision, embeddings, structured output, model management, and OpenAI-compatible endpoint
Open skill - /ai-infrastructure-replicate
Replicate SDK patterns for TypeScript/Node.js -- client setup, predictions, streaming, webhooks, file handling, model versioning, deployments, and training
Open skill - /ai-infrastructure-together-ai
Together AI SDK patterns for TypeScript — client setup, chat completions, streaming, structured output, function calling, embeddings, image generation, fine-tuning, and OpenAI-compatible endpoints
Open skill

