Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-agents-proxy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/hydrogen': patch
---

Storefront Agent requests now route through the generic `/__shopify/*` Shopify API proxy. Unprefixed `/agent/buyer-claims` and `/agent/handoff` requests are no longer intercepted and fall through to app routing, where they may return a 404 or catch-all HTML response.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Request
-> framework 404 page
```

`handleShopifyRoutes` owns Hydrogen routes the framework should never see: SFAPI proxy URLs, `/checkout`, cart permalinks like `/cart/{variantId}:{quantity}`, AJAX cart URLs like `/cart.js` and `/cart/add.js`, `/api/mcp`, `/agent/*`, `/graphiql` in development, Liquid-style `?variant=<numeric id>` product URLs, and app-registered handler groups such as `createCartServerHandlers()` or `createCustomerAccountServerHandlers()`.
`handleShopifyRoutes` owns Hydrogen routes the framework should never see: SFAPI proxy URLs, the generic `/__shopify/*` API proxy, `/checkout`, cart permalinks like `/cart/{variantId}:{quantity}`, AJAX cart URLs like `/cart.js` and `/cart/add.js`, `/api/mcp`, `/graphiql` in development, Liquid-style `?variant=<numeric id>` product URLs, and app-registered handler groups such as `createCartServerHandlers()` or `createCustomerAccountServerHandlers()`.

## Variant Id Redirects

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Preserve the app's existing route shape when present. When there is no establish
- `/products/{handle}` for product detail. Use plural `products`, not `/product`.
- `/cart` for the full cart page.

Hydrogen-owned handlers are not page routes: `/api/cart`, `/api/{api-version}/graphql.json`, `/checkout`, cart permalinks like `/cart/{variantId}:{quantity}`, AJAX cart URLs like `/cart.js` and `/cart/add.js`, `/api/mcp`, `/agent/*`, `/graphiql` in development, Liquid-style `?variant=<numeric id>` product redirects, `/admin` redirects, and Storefront URL redirects belong in the `hydrogen-request-handlers` wiring.
Hydrogen-owned handlers are not page routes: `/api/cart`, `/api/{api-version}/graphql.json`, the generic `/__shopify/*` API proxy, `/checkout`, cart permalinks like `/cart/{variantId}:{quantity}`, AJAX cart URLs like `/cart.js` and `/cart/add.js`, `/api/mcp`, `/graphiql` in development, Liquid-style `?variant=<numeric id>` product redirects, `/admin` redirects, and Storefront URL redirects belong in the `hydrogen-request-handlers` wiring.

Invoke the `hydrogen-routing` skill and create the shared route template manifest for Shopify resources such as products, collections, pages, blogs, or articles.

Expand Down
4 changes: 0 additions & 4 deletions packages/hydrogen/src/core/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ export const REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
export const SDK_VARIANT_HEADER = "X-SDK-Variant";
export const SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
export const SDK_VERSION_HEADER = "X-SDK-Version";
export const SHOPIFY_CHAT_FRAME_ORIGIN_HEADER = "Sec-Shopify-Chat-Frame-Origin";
export const SHOPIFY_STOREFRONT_ORIGIN_HEADER = "Sec-Shopify-Storefront-Origin";
export const STOREFRONT_ID_HEADER = "Shopify-Storefront-Id";
export const SHOPIFY_STOREFRONT_S_HEADER = "Shopify-Storefront-S";
Expand Down Expand Up @@ -51,7 +50,6 @@ export type ShopifyHeaderName =
| typeof SDK_VARIANT_HEADER
| typeof SDK_VARIANT_SOURCE_HEADER
| typeof SDK_VERSION_HEADER
| typeof SHOPIFY_CHAT_FRAME_ORIGIN_HEADER
| typeof SHOPIFY_STOREFRONT_ORIGIN_HEADER
| typeof SHOPIFY_STOREFRONT_S_HEADER
| typeof SHOPIFY_STOREFRONT_Y_HEADER
Expand Down Expand Up @@ -118,8 +116,6 @@ export const SFAPI_REQUEST_HEADER_ALLOWLIST = defineHeaderList(

export const MCP_REQUEST_HEADER_ALLOWLIST = defineHeaderList(...COMMON_PROXY_HEADER_ALLOWLIST);

export const AGENT_REQUEST_HEADER_ALLOWLIST = defineHeaderList(...COMMON_PROXY_HEADER_ALLOWLIST);

export const AJAX_API_REQUEST_HEADER_ALLOWLIST = defineHeaderList(
...COMMON_PROXY_HEADER_ALLOWLIST,
"content-length",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { handleProductVariantId } from "../product/accept-variant-id";
import { handleAgentProxy } from "./interceptors/agent-proxy";
import { handleAjaxApi } from "./interceptors/ajax-api";
import { handleShopifyApiProxy } from "./interceptors/api-proxy";
import { handleCheckoutRedirect } from "./interceptors/checkout";
Expand All @@ -18,7 +17,6 @@ const SHOPIFY_ROUTE_INTERCEPTORS = [
handleCheckoutRedirect,
handleWellKnownProxy,
handleMcpProxy,
handleAgentProxy,
handleAjaxApi,
] satisfies readonly HydrogenRouteInterceptor[];

Expand Down

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ describe("handleShopifyApiProxy", () => {

it("removes the prefix and forwards the path and search params to SFR", async () => {
await handleShopifyApiProxy(
new Request("https://my-app.com/__shopify/apps/inbox/config.json?locale=en"),
new Request("https://my-app.com/__shopify/agent/buyer-claims?locale=en"),
);

const call = mockFetch.mock.calls[0];
assert(call, "expected fetch to be called");
expect(call[0].href).toBe("https://test-store.myshopify.com/apps/inbox/config.json?locale=en");
expect(call[0].href).toBe("https://test-store.myshopify.com/agent/buyer-claims?locale=en");
});

it("forwards the prefix root to the SFR root", async () => {
Expand Down
2 changes: 0 additions & 2 deletions packages/hydrogen/src/core/url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ export function isHydrogenServerHandoffPath(pathname: string): boolean {
);
}

export const AGENT_BUYER_CLAIMS_RE =
/^(?:\/[a-z]{2}(?:-[a-z]{2})?)?\/agent\/(?:handoff|buyer-claims)(?:\.[^/.]+)?\/?$/i;
export const WELL_KNOWN_RE = /^\/\.well-known\/(?:apple-developer-merchantid-domain-association)$/;
export const AJAX_CART_RE =
/^(?:\/[a-z]{2}(?:-[a-z]{2})?)?\/cart(?:\.(?:js|json)|\/(?:add|update|change|clear)(?:\.(?:js|json))?)$/i;
Expand Down
Loading