In your Hydrogen app server entry (typically server.ts) call createStorefrontClient with storeDomain, publicStorefrontToken (for client-side use), and privateStorefrontToken (for server-side use); these are set as Oxygen environment variables
The client returns a storefront object with storefront.query and storefront.mutate methods; pass the storefront through the Remix app context so loaders can access it
In a Remix loader use const { storefront } = context to get the client, then call const { product } = await storefront.query(PRODUCT_QUERY, { variables: { handle } }) where PRODUCT_QUERY is a tagged gql template string
Define PRODUCT_QUERY with the fields you need; the privateStorefrontToken is used automatically on the server so you don't need to pass an Authorization header manually — the client handles it
Return the data from the loader as json(data) and consume it in the route component with useLoaderData()
For caching annotate the query call with cache: storefront.CacheLong() or storefront.CacheShort() to control how Oxygen's edge cache stores the response
Known gotchas
publicStorefrontToken is safe to expose to browsers; privateStorefrontToken must never be sent to the client — only use it in server-side loader or action contexts
createStorefrontClient from @shopify/hydrogen is designed for Oxygen (Cloudflare Workers runtime); if you run it locally via Miniflare or a standard Node server the fetch behavior differs slightly — test in both environments
The storefront client does not automatically retry on rate limit (429) responses; implement retry-with-backoff logic in production loaders that issue many parallel queries
Give your agent this knowledge — and 200+ more routes
One MCP install gives any agent live access to the full route map, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp