Build headless storefronts against the Shopify Storefront API (GraphQL). Use when working with Shopify cart mutations (create, add/update/remove lines, discount codes, gift cards), checkout flow (hosted checkout via checkoutUrl), and customer account operations (registration, login/logout via access tokens, profile management, addresses, password recovery, order history). Covers the stateful/write side of the Storefront API — not product data fetching (use Frontic for that).
Procedural guide for building headless storefronts against the Shopify Storefront API (GraphQL). Covers cart, checkout, and customer account operations. For product data, collections, and pages use the Frontic skill instead.
Frontic Client → Product data, collections, pages, search (read)
Storefront API → Cart, checkout, customer accounts (write/session)
The Storefront API is a GraphQL API. All operations are mutations or queries sent to a single endpoint.
POST https://{store}.myshopify.com/api/{version}/graphql.json
| Header | When | Purpose |
|---|
X-Shopify-Storefront-Access-Token | Client-side | Public access token (safe to expose in browser) |
Shopify-Storefront-Private-Token | Server-side | Private token (never expose publicly) |
Shopify-Storefront-Buyer-IP | Server-side with private token | Buyer IP for rate limiting context |
Content-Type | Always | application/json |
API versions follow YYYY-MM format (e.g., 2025-01). New versions release quarterly.
1. cartCreate → Create cart (optionally with lines)
2. cartLinesAdd / cartLinesUpdate → Manage items
3. cartDiscountCodesUpdate → Apply discount codes
4. cartBuyerIdentityUpdate → Associate logged-in customer
5. Query cart { checkoutUrl } → Get hosted checkout URL
6. Redirect to checkoutUrl → Shopify handles payment & order
1. customerAccessTokenCreate → Login, get accessToken
2. cartBuyerIdentityUpdate → Attach customerAccessToken to cart
3. Query cart { checkoutUrl } → Checkout pre-fills address & payment
Shopify uses hosted checkout only. There is no custom checkout API — the old Checkout mutations are deprecated (removed April 2025). The flow:
cartBuyerIdentityUpdatecart.checkoutUrlasync function storefrontQuery<T>(
query: string,
variables?: Record<string, unknown>
): Promise<T> {
const response = await fetch(
`https://${SHOP_DOMAIN}/api/${API_VERSION}/graphql.json`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': STOREFRONT_TOKEN,
},
body: JSON.stringify({ query, variables }),
}
)
const { data, errors } = await response.json()
if (errors?.length) throw new StorefrontError(errors)
return data
}
All mutations return userErrors (or customerUserErrors):
mutation {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart { id }
userErrors { field message code }
}
}
Always check userErrors — the mutation may "succeed" (HTTP 200) but contain business logic errors.
All prices use MoneyV2:
{
amount # String — decimal like "49.95"
currencyCode # CurrencyCode enum — "USD", "EUR", etc.
}
Cart IDs contain a secret key component (e.g., gid://shopify/Cart/Z2NwLX...?key=abc123). Store the full ID in a cookie or server-side session.
The Storefront API uses calculated query costs. Default: 50 points/second, 1000-point bucket. Keep queries lean by selecting only needed fields.