Caching Shopify GraphQL: A Practical Guide for Developers

작성자

카테고리:

← 피드로
DEV Community · Muhammad Masad Ashraf · 2026-06-20 개발(SW)

TL;DR: GraphQL can’t be cached by URL like REST. Cache by query + variables, layer your caches (client → edge → app → persisted queries), match TTLs to data volatility, and invalidate via webhooks. Never cache carts or customer-specific pricing.

The Core Problem

REST caching is URL-based. One endpoint = one cache entry. Easy.

GraphQL uses a single endpoint for everything. The query body defines the response, so two requests to the same URL can return totally different data. URL-based caching is useless here.

Your cache key has to include the query + variables + user context.

// Naive (broken) approach
const key = endpoint; // same key for every query — wrong

// Correct approach
const key = hash(JSON.stringify({
  query: normalizedQuery,
  variables: sortedVariables,
  locale,
  buyerSegment
})); // correct

Enter fullscreen mode Exit fullscreen mode

Factor REST GraphQL Endpoints Many One Cache key URL Query + variables Granularity Coarse Field-level possible Invalidation Simpler More complex

The 4 Cache Layers

Don’t think “a cache.” Think layers, each catching a different request type.

  1. Client cache (Apollo / urql) — session reuse
  2. Edge / CDN cache — public storefront pages
  3. App cache (Redis / Memcached) — shared, semi-static data
  4. Persisted queries — stable hash-based keys

All of these sit in front of the Shopify GraphQL API.

Layer Location Best for Typical TTL Client Browser/app Single session Session length Edge/CDN Network edge Public data Minutes to hours App Your server Shared data Seconds to hours Persisted query Server Stable identity Long-lived

What to Cache (and What Will Burn You)

  • Cache hard: product details, collections, shop settings
  • Cache short: pricing, availability (a few seconds)
  • Never cache: carts, checkout, customer-specific pricing
Data Volatility Approach Product details Low Cache hours, invalidate on update Collections Low Cache hours Inventory High Cache seconds or skip Pricing Med-high Short TTL + invalidation Cart/checkout Very high Don’t cache Customer data High + private Scope per user or skip

I once cached inventory too long and oversold during a launch. Learn from my pain.

Cache Invalidation: 3 Strategies

1. TTL (time-based) — simplest, but you’re guessing the window.

await redis.set(key, payload, 'EX', 60); // expire in 60s

Enter fullscreen mode Exit fullscreen mode

2. Event-based (webhooks) — most accurate. Product updates fire a webhook, you purge the entry.

// products/update webhook handler
app.post('/webhooks/products/update', verifyHmac, async (req, res) => {
  const productId = req.body.id;
  await redis.del(`product:${productId}:*`);
  res.sendStatus(200);
});

Enter fullscreen mode Exit fullscreen mode

A dropped webhook means stale cache. Make your consumers reliable (retries, dead-letter queues).

3. Stale-while-revalidate — serve stale instantly, refresh in background.

Cache-Control: max-age=60, stale-while-revalidate=300

Enter fullscreen mode Exit fullscreen mode

Method Freshness Complexity Best for TTL Medium Low Predictable data Event-based High Med-high Inventory, pricing SWR High Medium Public pages

Smart Cache Keys (don’t leak data!)

For B2B stores with tiered pricing, the buyer’s company must be in the key or you’ll serve Customer A’s contract price to Customer B.

function buildKey({ query, variables, context }) {
  return hash(JSON.stringify({
    q: normalize(query),
    v: sortObjectKeys(variables), // sort for consistency
    locale: context.locale,
    currency: context.currency,
    buyer: context.companyId ?? 'anonymous'
  }));
}

Enter fullscreen mode Exit fullscreen mode

Handling Personalized Data

Field-level splitting is the cleanest pattern:

  • Catalog data: cache once, globally
  • Cart + pricing: fetch fresh, per user, no cache

Keep your hit rate high where it counts, fetch fresh where it matters.

Measure It

  • Hit ratio = hits / (hits + misses) — maximize
  • Latency delta = before vs after — should drop
  • API calls avoided = cache hits — cost savings
  • Stale incidents = wrong price/stock reports — target zero

Common Mistakes

  • Caching personal data in a shared key
  • TTLs so long prices go stale
  • Ignoring webhook reliability
  • Caching mutation results

Wrap-Up

Layer your caches. Match TTLs to volatility. Invalidate via webhooks. Build keys that respect personalization. Measure, then tune.

Done right, caching turns a throttled, sluggish app into a fast, resilient one.

I wrote a longer, more detailed version with extra comparison tables and architecture notes here:
Caching Strategies for Shopify GraphQL

What’s your go-to invalidation strategy? Drop it in the comments.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다