Skip to content

GraphQL

Read the shop, products and collections in one request, with the schema, examples, errors and limits. Carts and checkout stay in the storefront API.

Reference5 min read

GraphQL reads the same shop, products and collections as the storefront API. Use it when one page needs several things at once, such as the shop's currencies, a product and a collection menu, in a single request.

Text
https://app.bifrostorbit.com/commerce/api/cdn/graphql

GraphQL only reads. Carts, checkout and orders stay in the storefront API.

Requests

GraphQL has one address for every shop, so it needs to know whose products to read. Either:

  • add ?organisation= and the shop address from Settings, such as ?organisation=northwilds. This needs no key.
  • or send an API key as Authorization: Bearer <key> or X-Api-Key: <key>. The key says which shop it belongs to, and a secret key (com_sk_) adds draft products.

With both, the key must belong to that shop.

To read as a signed-in customer, also send their token as X-Customer-Token: cst_…. Prices and stock are then theirs, and customer returns who they are. Only the shop's allowed websites, or your server, may send a customer token. See Customer accounts and personal prices.

POST a JSON body with query, and optionally variables (an object) and operationName:

Shell
curl 'https://app.bifrostorbit.com/commerce/api/cdn/graphql?organisation=northwilds' \  -H 'Content-Type: application/json' \  -d '{"query":"query ($slug: String!) { product(slug: $slug, locale: \"nb\", currency: \"NOK\") { title price { min currency } available } }","variables":{"slug":"tind-2-tent"}}'

GET works too, with query, variables (as JSON text) and operationName in the query string, URL-encoded:

Shell
curl -G 'https://app.bifrostorbit.com/commerce/api/cdn/graphql' \  --data-urlencode 'organisation=northwilds' \  --data-urlencode 'query={ products(tag: "winter", limit: 12) { slug title price { min currency } } }'

Answers are JSON with data, and errors when something went wrong. Any website may call it from a browser. A secret key works only from a server; from another website it gets 403. Answers aren't cached by us: cache them on your side.

Queries

store
Returns
Store!: name, logo, colour, currencies, languages, whether prices include VAT and the hosted shop address
customer
Returns
Customer: the signed-in customer, or null without a customer token
products
Arguments
ids, slugs, tag, collection, query, limit, locale, currency
Returns
[Product!]!: active products, as in the REST list
product
Arguments
slug: String! (address or id), locale, currency
Returns
Product, or null when there is no such active product
collections
Arguments
ids, locale
Returns
[Collection!]!: active collections
collection
Arguments
slug: String! (address or id), locale, currency, limit
Returns
Collection with its products, or null

locale and currency work as in REST. limit is 100 unless you say, at most 250 for products.

Types

Store
Fields
slug, name, logoUrl, colour, currency, currencies, locales, defaultLocale, pricesIncludeTax, shopUrl, termsUrl, returnsDays
Product
Fields
id, slug, title, summary, description (HTML), vendor, productType, tags, options { name values }, variants, images { url alt }, specs { label value }, price, available, status, seo { title description }, url, locale, updatedAt, stockSyncedAt, pricing { source account checkedAt }, metafields (JSON, by key), bundle { components { productId variantId quantity } }
Variant
Fields
id, title, options, sku, price, compareAtPrice, available, quantity (null when stock isn't tracked), listPrice, priceSource, branches { code name quantity yours }, requiresShipping, digital, plans { id name interval intervalCount price }, metafields (JSON)
Customer
Fields
email, name, company { name terms termsDays }, account { number status onStop creditLimit balance branch branchName }, priceList
PriceRange
Fields
min, max, currency, compareAt
Collection
Fields
id, slug, title, description, image { url alt }, productCount, url, products (only on collection)

Amounts are whole minor units, as in REST: 38900 with EUR is €389.00. price is null on a variant with no price in the chosen currency.

Examples

A product page

graphql
query ProductPage($slug: String!, $locale: String, $currency: String) {  store {    name    currencies    pricesIncludeTax  }  product(slug: $slug, locale: $locale, currency: $currency) {    title    description    images {      url      alt    }    options {      name      values    }    variants {      id      title      price      compareAtPrice      available      quantity    }    specs {      label      value    }  }}
JSON
{ "slug": "fjell-gtx-boot", "locale": "nb", "currency": "NOK" }

A collection with the menu

graphql
{  collection(slug: "tents", limit: 12) {    title    products {      slug      title      price {        min        currency      }      available    }  }  collections {    slug    title    productCount  }}

Errors

Errors come in errors, each with a message, a code under extensions, and the path and locations of the field when there is one.

400
When
No query, variables that aren't a JSON object, a query that doesn't parse or match the schema (bad_request), or one over the limits (query_too_deep, query_too_complex)
401
When
Neither a key nor ?organisation=, or a key that is wrong, revoked, expired or for another shop (unauthorized)
403
When
A secret key sent from a browser on another website (forbidden)
402
When
The shop is paused because its Commerce Orbit trial or plan ended (subscription_inactive)
404
When
No shop with that address (not_found)
413
When
A query longer than 20,000 characters (query_too_large)
429
When
Too many requests in a short time (rate_limited). Wait a minute.
500
When
Something failed on our side (server_error). Try again.

product and collection answer null when nothing matches, without an error.

Limits

  • A query may be at most 20,000 characters long, nest at most 10 levels and ask for at most 300 fields. Each alias and each use of a fragment counts.
  • Requests share the catalogue limit of the storefront API: 120 a minute per IP address and shop.

Was this page helpful?

Read next