Writing Shopify Queries
Step-by-step guide to adding a new Storefront API query or mutation.
This guide walks through adding a new Shopify Storefront API operation from scratch. We'll fetch a shop's privacy policy as a concrete example.
If you're using a coding agent, run /vercel-shop:shopify-graphql-reference in Claude Code or ask the agent to use the shopify-graphql-reference skill first. The skill is the reference source for Shopify GraphQL work and uses shopify-ai-toolkit for live schema checks while keeping the agent anchored to the template guardrails.
1. Check the schema
Use shopify-ai-toolkit, Shopify's GraphQL docs, or your preferred GraphQL explorer to confirm the field exists before writing the query. For our example, the QueryRoot type has a shop field with a privacyPolicy property that returns a ShopPolicy:
Never guess field names. The live Shopify schema is the source of truth.
2. Write the query
Create the GraphQL query as a template string. Interpolate any shared fragments from lib/shopify/fragments.ts at the top. Include @inContext if the data is locale-sensitive:
The operation name (getPrivacyPolicy) must match the operation field you pass to shopifyFetch.
3. Define the response type
Type the shape of data that the GraphQL response will return:
This is a local type scoped to your operation file - it maps directly to the GraphQL response shape.
4. Write the operation function
Create a new file in lib/shopify/operations/ or add to an existing one. The function calls shopifyFetch, applies caching, and returns the result:
Key points:
"use cache: remote"enables Next.js caching for the functioncacheLife("max")caches until you manually invalidatecacheTag("policies")lets you invalidate withrevalidateTag("policies")later- Always pass
localeand extractcountry/languagefrom it
5. Add a transform (if needed)
If the Shopify response shape differs from how your components want to consume the data, add a transform in lib/shopify/transforms/. This keeps Shopify-specific types out of your UI:
Then call it in your operation before returning:
For simple responses where the shape already matches your needs, you can skip the transform.
6. Use it in a page
Call the operation from a Server Component:
Writing a mutation
Mutations follow the same pattern but without caching directives. If the mutation modifies cart state, you must call invalidateCartCache() afterward:
Expose mutations to the client through server actions in components/cart/actions.ts using "use server".
Checklist
- Verified field names against the live Storefront API schema
- Operation name in the query string matches the
operationparameter - Variables passed as an object, never interpolated into the query
-
@inContextdirective included if data is locale-sensitive -
"use cache: remote",cacheLife, andcacheTagset for read operations - Transform added if the Shopify response shape needs mapping
- Components import domain types from
lib/types, not Shopify response types - Cart mutations call
invalidateCartCache()