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.