Mastering Subrequest Profiling in Shopify Oxygen

Introduction

Hydrogen storefronts live and die by Shopify’s subrequest budget. Every API call, redirect, and edge fetch counts against Oxygen’s ~40 subrequest cap per route. Exceed the limit, and pages can fail silently.

This post dives deep into subrequest profiling: how to measure, optimize, and enforce budgets so your storefronts stay fast and stable.

What Is a Subrequest?

A subrequest is any network request your Oxygen worker makes during a single page render:

  • GraphQL Storefront API queries
  • REST Admin API calls
  • External API requests (CMS, analytics, personalization)
  • Redirects + fetches from loaders

👉 Budget = ~40 per route before Oxygen rejects the render.

Why Subrequests Matter

  • ⚡ TTFB impact → more subrequests = slower first byte.
  • 🛑 Quota failures → >40 subrequests = hard stops.
  • 🔍 Debugging complexity → excessive calls scatter logic.

Shopify Subrequest Profiler

How to Enable

  • Available in Oxygen deploy logs and dev tools.
  • Shows request count per route, broken down by origin.

Example Output

/products/sofa

- Storefront API: 12

- Contentful: 5

- Loyalty API: 3

- Total: 20 subrequests

 

Optimization Techniques

1. Batch GraphQL Queries

  • Replace multiple variant fetches with one consolidated query.

query ProductWithVariants($handle: String!) { product(handle: $handle) { title variants(first: 10) { edges { node { id title price { amount } } } } } }

2. Cache Tokenless Queries

  • Cache public catalog queries with CacheLong().
  • Reduces duplicate subrequests across requests.

3. Stream with defer()

  • Prioritize above-the-fold data.
  • Stream CMS/reviews later without blocking initial render.

4. Eliminate Redundant Calls

  • Remove duplicate API hits across nested loaders.
  • Share data via context instead of refetching.

Case Study: PDP Optimization

  • Initial build: 9 GraphQL calls, 5 CMS calls, 3 loyalty calls = 17 subrequests.
  • Refactored build: 3 batched GraphQL queries, CMS streamed with defer().
  • Final: 7 subrequests, 65% faster TTFB, stable under load.

Tooling Beyond Shopify Profiler

  • Datadog/Splunk → log subrequest counts by route.
  • CI/CD Budget Checks → fail deploy if >40 subrequests.
  • Custom Middleware → count fetch calls at runtime.

Developer Checklist

  • ✅ Profile subrequests per route before launch.
  • ✅ Batch GraphQL queries wherever possible.
  • ✅ Cache tokenless queries at the edge.
  • ✅ Stream non-critical data with defer().
  • ✅ Enforce budgets in CI/CD pipelines.

Conclusion

Subrequest profiling isn’t optional — it’s survival. Merchants who budget, batch, and cache requests build Hydrogen storefronts that scale smoothly on Oxygen.

Every millisecond matters. Every subrequest counts.