Version: 1.0.0
Category: API Patterns
Purpose: Guide for AI agents on implementing effective response caching strategies
Caching API responses reduces server load, improves performance, and helps stay within rate limits. This pattern explains when and how to cache API responses effectively while maintaining data freshness.
Caching stores API responses locally to avoid redundant requests:
Good Candidates for Caching:
Don’t Cache:
Approach: Cache responses for a fixed time period
Implementation:
{
"strategy": "time-based-ttl",
"cache": {
"ttl": 300,
"unit": "seconds",
"key": "player-{id}",
"invalidate": "after-ttl"
}
}
Example TTL Values:
Use When:
Approach: Invalidate cache when related events occur
Implementation:
{
"strategy": "event-based-invalidation",
"cache": {
"key": "player-{id}",
"invalidateOn": [
"structs.player.{id}",
"structs.planet.{id}",
"structs.guild.{id}"
]
}
}
Example:
structs.player.{id} events via GRASS/NATSUse When:
Approach: Cache based on data version/ETag
Implementation:
{
"strategy": "version-based",
"cache": {
"key": "player-{id}",
"version": "response.headers.ETag",
"check": "If-None-Match header",
"invalidate": "when-version-changes"
}
}
Use When:
Approach: Combine multiple strategies
Implementation:
{
"strategy": "hybrid",
"cache": {
"ttl": 300,
"eventInvalidation": true,
"versionCheck": true,
"fallback": "ttl"
}
}
Use When:
Pattern: {entity}-{id}
Examples:
{
"keys": {
"player": "player-{id}",
"planet": "planet-{id}",
"guild": "guild-{id}",
"struct": "struct-{id}"
}
}
Pattern: {endpoint}-{params}
Examples:
{
"keys": {
"playerPlanets": "player-{id}-planets",
"guildMembers": "guild-{id}-members",
"planetStructs": "planet-{id}-structs"
}
}
Pattern: {entity}-{id}-{language}-{guildId}
Examples:
{
"keys": {
"structTypeWithCosmetics": "struct-type-{id}-{language}-{guildId}",
"playerWithGuild": "player-{id}-guild-{guildId}"
}
}
{
"implementation": "simple-ttl-cache",
"code": {
"javascript": "class SimpleCache {\n constructor(ttl = 300) {\n this.cache = new Map();\n this.ttl = ttl * 1000; // Convert to ms\n }\n \n get(key) {\n const item = this.cache.get(key);\n if (!item) return null;\n \n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return null;\n }\n \n return item.value;\n }\n \n set(key, value) {\n this.cache.set(key, {\n value,\n expires: Date.now() + this.ttl\n });\n }\n}"
},
"usage": {
"example": "const cache = new SimpleCache(300); // 5 min TTL\n\nasync function getPlayer(id) {\n const cached = cache.get(`player-${id}`);\n if (cached) return cached;\n \n const response = await fetch(`/api/player/${id}`);\n const data = await response.json();\n cache.set(`player-${id}`, data);\n return data;\n}"
}
}
{
"implementation": "event-based-cache",
"code": {
"javascript": "class EventBasedCache {\n constructor() {\n this.cache = new Map();\n this.nats = null;\n }\n \n async connectNATS() {\n // Connect to GRASS/NATS\n this.nats = await connect({ servers: ['nats://localhost:4222'] });\n \n // Subscribe to invalidation events\n this.nats.subscribe('structs.player.*', (msg) => {\n const data = JSON.parse(msg.data.toString());\n this.invalidate(`player-${data.id}`);\n });\n }\n \n invalidate(key) {\n this.cache.delete(key);\n }\n \n get(key) {\n return this.cache.get(key);\n }\n \n set(key, value) {\n this.cache.set(key, value);\n }\n}"
},
"usage": {
"example": "const cache = new EventBasedCache();\nawait cache.connectNATS();\n\n// Cache will auto-invalidate on player updates"
}
}
{
"implementation": "hybrid-cache",
"code": {
"javascript": "class HybridCache {\n constructor(ttl = 300) {\n this.cache = new Map();\n this.ttl = ttl * 1000;\n this.nats = null;\n }\n \n get(key) {\n const item = this.cache.get(key);\n if (!item) return null;\n \n // Check TTL\n if (Date.now() > item.expires) {\n this.cache.delete(key);\n return null;\n }\n \n return item.value;\n }\n \n set(key, value) {\n this.cache.set(key, {\n value,\n expires: Date.now() + this.ttl\n });\n }\n \n invalidate(key) {\n this.cache.delete(key);\n }\n \n // Subscribe to events for invalidation\n async setupEventInvalidation() {\n this.nats = await connect({ servers: ['nats://localhost:4222'] });\n \n this.nats.subscribe('structs.player.*', (msg) => {\n const data = JSON.parse(msg.data.toString());\n this.invalidate(`player-${data.id}`);\n });\n }\n}"
}
}
When: Cache entry expires after TTL
Implementation:
{
"invalidation": "ttl-expiration",
"check": "on-get",
"action": "delete-if-expired"
}
When: Related event received via GRASS/NATS
Implementation:
{
"invalidation": "event-driven",
"events": {
"structs.player.{id}": "invalidate-player-{id}",
"structs.planet.{id}": "invalidate-planet-{id}",
"structs.guild.{id}": "invalidate-guild-{id}"
}
}
When: Explicitly invalidate cache
Implementation:
{
"invalidation": "manual",
"methods": [
"cache.invalidate(key)",
"cache.clear()",
"cache.invalidatePattern(pattern)"
]
}
When: Related entity changes
Implementation:
{
"invalidation": "dependency-based",
"dependencies": {
"player-{id}": [
"player-{id}-planets",
"player-{id}-guild",
"guild-{guildId}-members"
]
}
}
Examples: Struct types, game constants
TTL: 3600 seconds (1 hour) or longer
{
"staticData": {
"structTypes": 3600,
"gameConstants": 7200,
"structTypeCosmetics": 1800
}
}
Examples: Player info, guild info
TTL: 300 seconds (5 minutes)
{
"slowlyChanging": {
"playerInfo": 300,
"guildInfo": 300,
"planetInfo": 180
}
}
Examples: Planet resources, guild stats
TTL: 60 seconds (1 minute)
{
"moderatelyChanging": {
"planetResources": 60,
"guildStats": 60,
"playerStats": 120
}
}
Examples: Planet shield, raid status
TTL: 10-30 seconds
{
"frequentlyChanging": {
"planetShield": 10,
"raidStatus": 15,
"fleetStatus": 30
}
}
Do:
Don’t:
Do:
{
"cacheWarming": {
"onStartup": [
"Load frequently accessed data",
"Pre-fetch critical entities",
"Subscribe to invalidation events"
]
}
}
Benefits:
Metrics to Track:
Example:
{
"monitoring": {
"metrics": {
"hitRate": 0.85,
"missRate": 0.15,
"averageResponseTime": 50,
"cacheSize": 1000
}
}
}
Do:
{
"errorHandling": {
"cacheMiss": "fetch-from-api",
"cacheError": "fetch-from-api",
"cacheCorruption": "clear-and-refetch"
}
}
Don’t:
Do:
{
"realTimeData": {
"use": "GRASS/NATS streaming",
"benefit": "No caching needed",
"example": "Planet shield updates, raid status"
}
}
Benefits:
Benefits:
Example:
{
"scenario": "100 requests/minute limit",
"withoutCache": {
"requests": 100,
"remaining": 0
},
"withCache": {
"requests": 30,
"cacheHits": 70,
"remaining": 70
}
}
Strategy:
Use When:
Example: JavaScript Map, Python dict
Use When:
Example: Redis, SQLite, file system
Use When:
Example: Redis cluster, Memcached
patterns/rate-limiting.md - Rate limit patternsprotocols/streaming.md - GRASS/NATS streaming (alternative to caching)guides/api-usage-guide.md - API usage best practicesprotocols/error-handling.md - Error handling patterns{entity}-{id}{endpoint}-{params}{entity}-{id}-{language}-{guildId}Pattern Version: 1.0.0 - December 7, 2025