Implementing Caching Strategies
1. Understanding Cache Levels
| Level | Where |
|---|---|
| Browser | Apollo InMemoryCache, urql |
| CDN/Edge | Cloudflare, Fastly via GET + APQ |
| App-level | Redis response cache |
| Resolver | Per-field memoization |
| Database | Query plan cache, materialized views |
2. Using Response Caching
Example: Yoga response cache
import { useResponseCache } from "@graphql-yoga/plugin-response-cache";
createYoga({
schema,
plugins: [useResponseCache({
session: (req) => req.headers.get("authorization"),
ttl: 60_000,
invalidateViaMutation: true
})]
});
3. Implementing Cache Control
| Directive Arg | Detail |
|---|---|
| maxAge | Seconds to cache |
| scope | PUBLIC | PRIVATE |
| inheritMaxAge | Use parent's maxAge |
4. Setting Cache Hints
Example: Apollo cacheControl
products: (_, args, { cacheControl }) => {
cacheControl.setCacheHint({ maxAge: 300, scope: "PUBLIC" });
return db.product.findMany();
}
5. Using ETags
| Aspect | Detail |
|---|---|
| Generation | Hash response body |
| Validation | Client sends If-None-Match → 304 on hit |
| Persisted Queries | Hash already exists; ideal pairing |
6. Implementing Field-level Cache
| Pattern | Detail |
|---|---|
| Per-field memo | Cache resolver result by (id, args) |
| Read-through | Check cache, fall back to DB, populate |
| TTL | Short for hot fields, long for static |
7. Using Redis Cache
Example: Redis-backed loader
const loader = new DataLoader(async (ids) => {
const cached = await redis.mget(ids.map(k => `user:${k}`));
const missing = ids.filter((_, i) => !cached[i]);
if (missing.length) {
const fresh = await db.user.findMany({ where: { id: { in: missing } } });
await Promise.all(fresh.map(u => redis.set(`user:${u.id}`, JSON.stringify(u), "EX", 300)));
}
return ids.map((id, i) => cached[i] ? JSON.parse(cached[i]) : fresh.find(u => u.id === id));
});
8. Implementing Cache Invalidation
| Strategy | Detail |
|---|---|
| Time-based (TTL) | Simple; tolerate staleness |
| Event-based | Invalidate on mutation publish |
| Tag-based | Tag entries with entity ids; purge by tag |
| Versioned keys | Bump version on write |
9. Using Cache Keys
| Component | Example |
|---|---|
| Query hash | SHA256 of operation |
| Variables | Stable JSON serialization |
| Session | User id (for PRIVATE) or anon |
| Schema version | Invalidate on schema change |
10. Configuring Cache TTL
| Content | TTL |
|---|---|
| Reference data (countries) | 1 day+ |
| Catalog (product info) | 5–30 min |
| User profile | 1–5 min |
| Live counts | 10–60 s |
| Personal feeds | 0 (no cache) |