Caching
Per-method TTL caching with pluggable adapters
Default Behavior
Whisper includes an in-memory cache that is enabled by default. GET requests are cached based on route, path, and query parameters. The default TTL is 300 seconds (5 minutes).
import { createClient } from '@wardbox/whisper/core';
// Caching is active by default (in-memory, 300s TTL)
const client = createClient({
apiKey: 'RGAPI-your-key-here',
});Cache keys include a hash of your API key to prevent cross-key cache pollution when rotating keys.
TTL Configuration
Configure TTLs per API path pattern using the cacheTtl option. Rather than a separate byMethod map, patterns are top-level keys on the config object. Pattern matching checks if the request path contains the pattern string:
import { createClient } from '@wardbox/whisper/core';
const client = createClient({
apiKey: 'RGAPI-your-key-here',
cacheTtl: {
default: 60, // 60 seconds for everything else
'/lol/summoner/v4': 300, // 5 minutes for summoner data
'/lol/match/v5': 30, // 30 seconds for match data
'/lol/spectator/v5': 0, // Never cache live game data
'/lol/league/v4': 120, // 2 minutes for league data
},
});A TTL of 0 means "do not cache" -- the request always hits the API. This is the correct default for live game data like spectator endpoints, where stale data is actively harmful.
Custom Cache Adapter
Replace the built-in memory cache with any storage backend by implementing the CacheAdapter interface:
import type { CacheAdapter } from '@wardbox/whisper/core';
const redisCache: CacheAdapter = {
async get<T>(key: string): Promise<T | undefined> {
const data = await redis.get(key);
return data ? JSON.parse(data) : undefined;
},
async set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {
await redis.set(key, JSON.stringify(value), 'EX', ttlSeconds);
},
async delete(key: string): Promise<void> {
await redis.del(key);
},
async has(key: string): Promise<boolean> {
return (await redis.exists(key)) === 1;
},
};Pass it to the client:
import { createClient } from '@wardbox/whisper/core';
const client = createClient({
apiKey: 'RGAPI-your-key-here',
cache: redisCache,
});Disabling the Cache
To skip caching entirely:
const client = createClient({
apiKey: 'RGAPI-your-key-here',
cache: false,
});API-Key-Aware Cache Keys
Cache keys include a short hash derived from your API key. This prevents a subtle bug: if you rotate API keys and the old key's cached responses are served to the new key, Riot may flag the mismatch.
The key itself is never stored in the cache -- only a djb2 hash prefix. This works in all runtimes (no crypto module needed) and is deterministic across requests.
Cache key format: {key-hash}:{route}:{path}?{sorted-params}
Example: k8f2a:na1:/lol/summoner/v4/summoners/by-puuid/xyzCache Behavior
- Only GET requests are cached. POST, PUT, and DELETE always hit the API.
- The cache is checked before the middleware pipeline runs -- cached responses skip middleware entirely for performance.
- Expired entries are lazily evicted on access, not on a timer.