Skip to main content

Shopify routes

The handleShopifyRoutes function serves Shopify's storefront endpoints, such as checkout permalinks and the Storefront API proxy, and your custom route handlers. Use createShopifyRouteHandler to add your own endpoints that receive the request context, the session, and the Storefront API client.

Import handleShopifyRoutes and createShopifyRouteHandler from @shopify/hydrogen.


Serves Shopify's storefront endpoints and your custom route handlers from your app. Call the function at the start of request handling, before your framework's router.

When a route matches, the function returns a promise for the response. Return that response as is, because the response already carries the request context's headers. When no route matches, the function returns null synchronously, and your framework's router handles the request. The function throws when requestContext differs from the Storefront API client's request context. When a route handler throws, the returned promise rejects. Inside a try block, use return await on the result.

The function checks these routes in order and returns the first match: the Shopify API proxy at /__shopify, the Storefront API proxy at /api/{version}/graphql.json, Liquid-style ?variant= product URLs, registered handler groups, UCP buy permalinks at /buy/..., checkout and cart permalinks, allowlisted .well-known resources, the UCP MCP proxy at /api/ucp/mcp, the MCP proxy at /api/mcp, and AJAX cart URLs.

Some routes answer unsupported methods with a 405 response. A registered handler path returns 405 for any method that no handler on that path registers. Checkout and cart permalinks accept only GET and HEAD. Buy permalinks accept only GET, and the UCP MCP proxy accepts only POST.

When your bundler resolves the package's development export condition, the function also serves the GraphiQL explorer at /graphiql for GET requests that match no other route, and accepts a graphiql option. Set graphiql.customerAccount to an object with apiUrl, accessToken, and schemaUrl to add a Customer Account API tab. The explorer skips the tab when it can't fetch the schema.

Anchor to options
options
required

The request, its context and session, the Storefront API client, your route templates, and your handler groups.

null | Promise<Response>

A promise for the matched route's response, or null when no route matches.

Examples

TypeScript

import {
createShopifyRequestContext,
createStorefrontClient,
handleShopifyRoutes,
type ShopifyRouteHandlerContext,
type ShopifyRouteHandlerGroup,
type ShopifyRouteTemplates,
type StorefrontClient,
} from "@shopify/hydrogen";

export async function fetch(
request: Request,
appRouter: (request: Request, storefrontClient: StorefrontClient) => Promise<Response>,
sessionManager: ShopifyRouteHandlerContext["sessionManager"],
routeTemplates: ShopifyRouteTemplates,
cartHandlers: ShopifyRouteHandlerGroup,
) {
const requestContext = createShopifyRequestContext({
request,
i18n: { country: "US", language: "EN" },
});
const storefrontClient = createStorefrontClient({
type: "public",
requestContext,
config: { storeDomain: process.env.PUBLIC_STORE_DOMAIN! },
});

const shopifyRoute = handleShopifyRoutes({
request,
requestContext,
sessionManager,
storefrontClient,
routeTemplates,
handlers: [cartHandlers],
});
if (shopifyRoute) return await shopifyRoute;

const routedResponse = await appRouter(request, storefrontClient);
const response = new Response(routedResponse.body, routedResponse);
requestContext.applyResponseHeaders(response.headers);
return response;
}

Creates a custom endpoint in your app for one pathname and HTTP method. Collect handlers in an object, and pass an array of handler objects to handleShopifyRoutes in its handlers option.

Use paths other than /api/cart, which the cart server handlers use. Return a JSON, redirect, or error result from the handler. Hydrogen sends a JSON result with status 200, an error result with status 400 by default, and a redirect result with status 303 by default. When a redirect result uses a status other than 301, 302, 303, 307, or 308, the promise from handleShopifyRoutes rejects.

Anchor to pathname
pathname
TPathname extends string
required

The exact URL pathname that the handler matches. Hydrogen doesn't match path parameters or prefixes.

Anchor to method
method
TMethod extends string
required

The HTTP method that the handler matches, in uppercase, such as POST.

Anchor to handler
handler
(context: ) => Promise<>
required

The function that receives the request, its context, the session, and the Storefront API client, and returns a JSON, redirect, or error result.

<const TPathname extends string, const TMethod extends string>

The handler to add to a handler object.

The code and message that an error result sends in the response body's error field.

string
required

A machine-readable error code.

Anchor to message
message
string
required

A human-readable error message.

Examples

TypeScript

import {
createShopifyRouteHandler,
gql,
type ShopifyRouteHandlerContext,
type ShopifyRouteHandlerResult,
} from "@shopify/hydrogen";

const SHOP_QUERY = gql(`query { shop { name } }`);

async function handleShopGet({
storefrontClient,
}: ShopifyRouteHandlerContext): Promise<ShopifyRouteHandlerResult> {
const result = await storefrontClient.graphql(SHOP_QUERY);
if (result.errors) {
return {
type: "error",
status: 502,
error: { code: "storefront_error", message: result.errors[0]?.message ?? "Request failed" },
};
}

return { type: "json", data: { name: result.data.shop.name } };
}

export const shopHandlers = {
get: createShopifyRouteHandler("/api/shop", "GET", handleShopGet),
};

Was this page helpful?