Caching strategy
Evolve applies caching at multiple layers: the Next.js frontend, the GraphQL gateway, and the backend domain services. Each layer has a different caching strategy suited to its role.
Next.js frontend
Section titled “Next.js frontend”HTTP caching
Section titled “HTTP caching”The frontend sets Cache-Control headers for unauthenticated requests:
s-maxage=900; stale-while-revalidate=2592000This caches pages on the CDN for 15 minutes and serves stale responses for up to 30 days while revalidating in the background. Authenticated requests (users with a session token) bypass this cache entirely.
Data fetching
Section titled “Data fetching”Server-side GraphQL requests use Next.js Incremental Static Regeneration (ISR) with a 15-minute revalidation interval:
const data = await serverClient(GetProductDetailPage, variables, { cache: authHeaders ? "no-cache" : "force-cache", next: { revalidate: 900, tags: ["product_detail_page", slug], },});- Unauthenticated: responses are cached with
force-cacheand revalidated every 15 minutes - Authenticated: responses use
no-cacheto ensure customer- specific data is always fresh - Draft mode: all caching is disabled for content preview
React’s cache() function deduplicates identical requests within a
single server render, preventing multiple fetches for the same data
during one page load.
On-demand revalidation
Section titled “On-demand revalidation”Cached pages can be revalidated immediately using Next.js cache tags. This is useful for content updates that should be visible before the 15-minute TTL expires (e.g., a CMS publish event triggering revalidation of affected pages).
GraphQL gateway
Section titled “GraphQL gateway”The gateway is a Rust-based Hive Router and keeps no shared response cache: it does not use Redis, and there is no Apollo-style response or APQ cache. The router caches query plans in memory within its own process. In production, clients send persisted document IDs instead of full query text, which serves the same bandwidth-saving purpose APQ did.
Backend services
Section titled “Backend services”Redis caching
Section titled “Redis caching”Backend services use the framework’s cache module, which provides a Redis-backed cache with automatic fallback to a no-op store when Redis is unavailable:
import { cache } from "@evolve-framework/core/cache";
await cache.configure(config.REDIS_URL);
const result = await cache.wrapFn({ key: `product:${storeContext.cacheKeyPrefix}:${slug}`, fn: () => fetchProduct(slug), ttl: 300_000, // 5 minutes});The wrapFn method handles cache lookup, miss execution, and storage
in a single call with built-in tracing.
DataLoader caching
Section titled “DataLoader caching”DataLoaders that fetch frequently accessed data (categories, breadcrumbs, content snippets) are configured with Redis-backed caching. Cache keys include the store context (store key, locale, currency) to prevent data leaking between stores:
new DataLoaderCache(fetchFn, { cache: { ttl: 5 * MINUTES, storeFn: () => cache.getStore("cms:dataloaders"), }, cacheKeyFn: (key) => `snippet:${storeContext.cacheKeyPrefix}:${key}`,});Cache namespaces
Section titled “Cache namespaces”Services use namespaced cache stores to keep cached data separated:
| Namespace | Service | Purpose |
|---|---|---|
cms:page-cache |
CMS | Page content cache |
cms:dataloaders |
CMS | DataLoader result cache |
cms:cache |
CMS | CMS version tracking |
Guidelines
Section titled “Guidelines”- Default to no caching: add caching explicitly where it provides measurable benefit. Premature caching creates staleness bugs that are hard to debug.
- Include the store context in cache keys: use the
storeContext.cacheKeyPrefix(which combines store key, locale, and currency) to prevent cross-store cache pollution. - Use short TTLs for commerce data: product availability and pricing change frequently. 5 minutes is a reasonable default for catalog data.
- Use longer TTLs for CMS content: content changes less frequently. 15 to 60 minutes works well for page content and navigation.
- Skip caching for customer data: carts, orders, and account information should never be cached in shared caches.

