Template
Target SFAPI 2026-07, rename the token var, work around a hydrogen CORS bug
Three changes needed to run against a real storefront rather than mock.shop. Storefront API version 2025-07 -> 2026-07. Verified before switching: the API validates the version segment (a bogus one returns NOT_FOUND), and the fields this app selects all still resolve at 2026-07 — including the two deprecated ones, ProductOption.values and CartCost.totalTaxAmount, and the classic customer account mutations. BREAKING (config): NEXT_PUBLIC_SHOPIFY_STOREFRONT_ACCESS_TOKEN is now NEXT_PUBLIC_SHOPIFY_PUBLIC_ACCESS_TOKEN. Deploy environments must set the new name *before* the next build — NEXT_PUBLIC_* values are inlined at build time, so a stale name silently degrades to a tokenless client and real-store queries start failing. Works around a bug in this preview build of @shopify/hydrogen: the client tags every request with X-Hydrogen-Version, which the Storefront API does not list in its CORS access-control-allow-headers. Browsers reject the preflight, fetch throws, and hydrogen reports the generic "SFAPI request failed". Confirmed by isolating the single header in the browser against a real store: without it the request reaches the server (401 on a dummy token), with it fetch throws TypeError: Failed to fetch. This never showed up on mock.shop, which answers access-control-allow-headers: *. The header is stripped in the browser only, inside the fetch wrapper the client already owns; server-side requests are not subject to CORS and keep sending it. Remove once the API allows the header, or once these queries move server-side — which is the better fix, and what hydrogen's own guidance recommends for browser UI. Verified against a real store: product grid, search (519 results), product detail with variants, and cartCreate all succeed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JaxgqPbFxLsSuLPZom2kdC
This commit is contained in:
co-authored by
Claude Opus 5
parent
fe78293312
commit
1c690ca593
@@ -12,8 +12,8 @@ import {
|
||||
} from '@shopify/hydrogen';
|
||||
import {
|
||||
SHOPIFY_API_VERSION,
|
||||
SHOPIFY_PUBLIC_ACCESS_TOKEN,
|
||||
SHOPIFY_STORE_DOMAIN,
|
||||
SHOPIFY_STOREFRONT_ACCESS_TOKEN,
|
||||
} from '@/services/shopify/config';
|
||||
|
||||
// No incoming request and no buyer context: these clients are module-scoped and
|
||||
@@ -24,16 +24,38 @@ const requestContext = createShopifyRequestContext({
|
||||
i18n: { country: 'US', language: 'EN' },
|
||||
});
|
||||
|
||||
/**
|
||||
* Workaround for a bug in this preview build of `@shopify/hydrogen`.
|
||||
*
|
||||
* The client tags every request with `X-Hydrogen-Version`, but the Storefront
|
||||
* API does not list that header in its CORS `access-control-allow-headers`.
|
||||
* Browsers therefore reject the preflight and `fetch` throws, which hydrogen
|
||||
* reports as the generic "SFAPI request failed". It only bites against real
|
||||
* stores — `mock.shop` answers `access-control-allow-headers: *`.
|
||||
*
|
||||
* Stripped in the browser only: server-side requests are not subject to CORS,
|
||||
* so they keep sending the header. Remove this once the API allows it (or once
|
||||
* these queries move server-side, which is the better long-term fix).
|
||||
*/
|
||||
const CORS_BLOCKED_HEADERS = ['X-Hydrogen-Version'];
|
||||
|
||||
// Hydrogen calls `fetch(url, init, cacheOptions)`; Next's caching hints ride
|
||||
// along on `init`, which is how the two ways of caching get to coexist.
|
||||
const fetchWith = (overrides: RequestInit): typeof globalThis.fetch =>
|
||||
((url, init) =>
|
||||
globalThis.fetch(url, { ...init, ...overrides })) as typeof globalThis.fetch;
|
||||
((url, init) => {
|
||||
const headers = new Headers(init?.headers);
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
for (const header of CORS_BLOCKED_HEADERS) headers.delete(header);
|
||||
}
|
||||
|
||||
return globalThis.fetch(url, { ...init, ...overrides, headers });
|
||||
}) as typeof globalThis.fetch;
|
||||
|
||||
const config = {
|
||||
storeDomain: SHOPIFY_STORE_DOMAIN!,
|
||||
apiVersion: SHOPIFY_API_VERSION,
|
||||
publicStorefrontToken: SHOPIFY_STOREFRONT_ACCESS_TOKEN,
|
||||
publicStorefrontToken: SHOPIFY_PUBLIC_ACCESS_TOKEN,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user