@by-association-only/shopify-graphql-client
v1.4.0
Published
Readme
@by-association-only/shopify-graphql-client
Typed GraphQL clients for the Shopify Admin and Storefront APIs, built on @shopify/graphql-client. Consuming apps need no @shopify/*-api-client packages.
Install
bun add @by-association-only/shopify-graphql-clientAdmin API
import { createShopifyClient } from '@by-association-only/shopify-graphql-client'
const client = createShopifyClient({
apiVersion: '2026-07',
accessToken: session.accessToken,
shop: 'example.myshopify.com',
})
const result = await client.request(ShopQuery)Endpoint: https://{shop}/admin/api/{version}/graphql.json with X-Shopify-Access-Token auth.
Storefront API
Endpoint: https://{shop}/api/{version}/graphql.json. Two token classes, per Shopify's auth docs:
import { createStorefrontClient } from '@by-association-only/shopify-graphql-client'
// Public token (client-side safe) — sent as X-Shopify-Storefront-Access-Token
const client = createStorefrontClient({
apiVersion: '2026-01',
publicAccessToken: token,
shop: 'example.myshopify.com',
})
// Private token (server-side only) — sent as Shopify-Storefront-Private-Token.
// buyerIp forwards the buyer's IP (Shopify-Storefront-Buyer-IP) on requests
// made on behalf of buyer traffic; it is only accepted with a private token.
const serverClient = createStorefrontClient({
apiVersion: '2026-01',
buyerIp: request.headers.get('cf-connecting-ip') ?? undefined,
privateAccessToken: env.STOREFRONT_PRIVATE_TOKEN,
shop: 'example.myshopify.com',
})The auth options are a union: publicAccessToken or privateAccessToken (with optional buyerIp), never both.
Typed operations
Generate operation types with @by-association-only/graphql-app-config. The generated storefront.generated.ts augments this package's StorefrontQueries/StorefrontMutations, so client.request is typed per operation — including @inContext:
export const ProductsQuery = `#graphql
query Products($country: CountryCode!, $language: LanguageCode!, $first: Int)
@inContext(country: $country, language: $language) {
products(first: $first) {
edges { node { id title } }
}
}
`
import { CountryCode, LanguageCode } from './types/storefront.types'
import './types/storefront.generated'
const response = await client.request(ProductsQuery, {
variables: { country: CountryCode.Gb, first: 3, language: LanguageCode.En },
})
// response.data?.products.edges[0]?.node.title is string | undefinedAdmin works the same way through AdminQueries/AdminMutations (see the graphql-app-config README for the wiring).
Helpers
gidToNumber('gid://shopify/Product/123') // -> 123
// Unwrap Shopify's { <operation>: { <result>, userErrors } } mutation envelope
const createThing = createShopifyMutation(rawCreateThing, 'thingCreate', 'thing')
const result = await createThing(client, input) // ApiResult<Thing>
if (!result.success) formApi.setErrorMap(getFieldErrors(result.userErrors))createShopifyMutation accepts mutations written against either client.
