Chat on WhatsApp
Magento glossary

What is Magento GraphQL ?

Magento GraphQL is the headless API endpoint introduced in Magento 2.3 (Nov 2018) at POST /graphql, exposing the catalog, customer, cart, checkout and order data via GraphQL queries and mutations. Built for PWA Studio, Hyvä-headless, custom React/Vue SPAs, mobile apps and BFF (backend-for-frontend) patterns. One round-trip, client-specified field selection, schema-introspectable.

How it works

Five things Magento GraphQL actually does under the hood

GraphQL is not magic — it is a strongly-typed schema, a resolver layer over Magento's service contracts, and HTTP cache integration via Varnish. Here is what that means concretely.

  1. 01

    Single endpoint, client-specified field selection

    Magento exposes one URL — POST /graphql — that accepts every query and mutation. The client sends a query body declaring exactly which fields it wants (name, price, image.url, nested variants { sku }) and Magento returns only those fields. There is no field over-fetching the way REST endpoints return entire entity payloads, and no field under-fetching that forces follow-up requests — one round-trip pulls the precise tree the storefront needs.

  2. 02

    Schema declared per-module via schema.graphqls files

    Each Magento module ships an etc/schema.graphqls file written in GraphQL SDL (Schema Definition Language). The core Magento_CatalogGraphQl module defines products, categories, category queries; Magento_QuoteGraphQl defines cart, addProductsToCart mutations; Magento_CustomerGraphQl defines customer, createCustomer. Custom modules extend the schema by declaring new types and resolvers in their own schema.graphqls. The aggregated schema is compiled at setup:di:compile time.

  3. 03

    Resolvers map GraphQL types to PHP service contracts

    Behind every GraphQL field is a Resolver class implementing Magento\Framework\GraphQl\Query\ResolverInterface. Resolvers re-use the same service contracts (Repository interfaces, Search APIs) the REST layer uses — they are a thin transport adapter, not a parallel data layer. This means GraphQL and REST stay in sync as the catalog, customer, and cart logic evolve. Custom resolvers register against schema fields via etc/graphql/di.xml using the resolver argument.

  4. 04

    Cacheable queries cached via Varnish + x-magento-vary

    Read queries (catalog browsing, category listing, product detail) are HTTP-cacheable when the resolver returns a CacheableInterface result with cache tags. Magento sets X-Magento-Cache-Id and X-Magento-Vary headers; Varnish (or Fastly on Adobe Commerce Cloud) keys the cache on those headers and serves repeat queries from edge memory in under 5 ms. Customer-specific data (cart, customer, wishlist) is uncacheable and falls through to PHP every time.

  5. 05

    Mutations bypass cache and run synchronously

    Mutations — addProductsToCart, placeOrder, createCustomer, updateCustomerAddress — bypass HTTP cache by design and execute synchronously through the same service contracts the REST API uses. Each mutation runs inside a Magento DB transaction; failure rolls back state. The response shape mirrors the input mutation, so the client can update its local state from the mutation result without a follow-up read query.

When to use

Four situations where GraphQL is the right Magento API

GraphQL is not universally correct — REST is still better for batch / admin work. These four scenarios are unambiguous GraphQL wins.

  • Building PWA Studio, Hyvä-headless or custom SPA

    If the storefront is a separate frontend application — PWA Studio (React + Apollo), Hyvä-headless (Hyvä theme talking to Magento purely via GraphQL), Vue Storefront, or a bespoke React/Next.js / Vue/Nuxt SPA — GraphQL is the supported transport. The Adobe-recommended PWA Studio talks GraphQL by default, and every Magento 2.3+ release ships GraphQL coverage for new features the moment they land. Building a headless storefront on REST in 2026 is fighting the platform.

  • Multi-channel app from a single Magento backend

    When one Magento instance has to feed a web storefront plus an iOS app plus an Android app plus a kiosk — or any combination — GraphQL lets each client pull only the fields it needs in one request. The mobile app fetches small thumbnails and short descriptions, the web app fetches full image sets and rich HTML, the kiosk fetches inventory + price only. Same backend, same schema, four optimised payloads. REST forces every channel to consume the same fixed entity shape.

  • Deep nested data in one round-trip (kills N+1)

    A typical product detail page needs the product, its 8 image variants, its 3 configurable child SKUs, its category breadcrumb, its 12 reviews and the related-products carousel. On REST that is 5 to 7 sequential calls; on a 200 ms RTT mobile network that is over a second of waterfall latency. One GraphQL query pulls the entire nested tree in a single request — everything resolved server-side, returned as one JSON document. Lighthouse-critical Time to First Byte metrics improve by 30 to 60% on data-heavy pages.

  • Avoid for: bulk imports, simple admin tasks

    GraphQL is request-per-item by design — great for storefront reads, terrible for batch operations. Importing 10,000 products one mutation at a time will take hours and saturate PHP-FPM. Use the Magento Admin REST API or the bulk async REST endpoints for ingestion, or write directly to the database via InstallData / DataPatchInterface for one-time loads. Keep GraphQL for what it is built for: customer-facing storefront reads and individual cart / checkout / order mutations.

Common mistakes

Four traps that turn a GraphQL build into a performance disaster

Every slow Magento headless storefront I have audited makes one or more of these four mistakes. Avoid them and the API runs at edge speed.

  • Querying entire schemas instead of needed fields

    A common starter mistake is requesting products(filter: {...}) { items { ... }} with the autogenerated full ProductInterface field set. That pulls every attribute Magento knows about each product — description, short_description, meta_keyword, swatch images, every custom attribute — and a 24-product PLP can return 800 KB of JSON. Specify only the fields the UI actually renders (name, sku, price_range, small_image { url }) and the same query drops below 30 KB. GraphQL's killer feature is field selection — use it.

  • Not declaring cache tags on cacheable queries

    Custom resolvers default to uncacheable, which means every storefront hit runs the resolver from scratch — no Varnish, no edge cache, full PHP execution per request. Implement IdentityInterface on the resolver result and return cache tags (cat_p_, cat_c_) so Varnish can key and invalidate the response. Add cache: { tags: [...], cacheable: true } declarations in etc/graphql/di.xml. Done correctly, custom storefront queries hit at 50 to 200x lower latency.

  • Using GraphQL for bulk imports / batch operations

    GraphQL mutations are one-at-a-time by design. Looping addProductsToCart 500 times to seed test orders, or createProduct 10,000 times to import a CSV catalog, will burn hours of PHP-FPM time, blow past max_execution_time defaults, and likely 502 the gateway. For batch workloads use the Magento Bulk Async REST API (POST /async/bulk/V1/products), direct DB seed via DataPatch, or the Magento_ImportExport CSV pipeline. GraphQL is not the right tool for batch.

  • Forgetting Authorization header for customer queries

    The customer, customerCart, customerOrders queries are customer-scoped and require an Authorization: Bearer <token> header. Without it Magento returns a "The current customer isn't authorized" error rather than the customer's data. Issue tokens via the generateCustomerToken mutation, store them client-side (HttpOnly cookie or secure local storage), and send them on every customer-scoped request. Anonymous queries (products, category, cart(cart_id) with a guest mask ID) need no auth header.

FAQ

Magento GraphQL — frequently asked questions

  • GraphQL vs REST in Magento — which should I use?
    Use GraphQL for customer-facing storefront work where the client controls the field selection: PWA Studio, Hyvä-headless, custom SPA, mobile apps. Use REST for admin / backend tasks: bulk imports, ERP integrations, third-party connectors, batch operations, anything that runs as a scheduled job. GraphQL's strengths (single round-trip, field selection, schema introspection) are storefront strengths; its weaknesses (one-mutation-per-call, no native batching, request payload overhead) are batch weaknesses. Most production Magento stores run both side by side.
  • Magento GraphQL performance — is it fast?
    Cacheable queries (catalog browse, category, product detail with no customer context) hit Varnish / Fastly and respond in 5 to 50 ms at the edge — comparable to REST or faster, since one GraphQL query replaces 5 to 7 REST round-trips on a typical product page. Uncacheable queries (cart, customer, checkout) execute per-request through PHP-FPM at 100 to 400 ms depending on database size and resolver complexity, similar to REST. The biggest perf wins come from killing the N+1 round-trip pattern that REST forces on storefront UIs, not from raw query speed. Always measure on your own data and your own infra.
  • Which Magento versions support GraphQL?
    GraphQL was introduced in Magento 2.3 (November 2018) and has shipped with every Magento Open Source and Adobe Commerce release since. Coverage has expanded steadily — early versions only supported catalog reads, while 2.4.x covers catalog, cart, checkout, customer, orders, wishlist, B2B (company accounts, requisition lists, negotiable quotes), Page Builder content, and Inventory (MSI). Magento 2.4.6 and later have feature parity with REST for storefront use cases. Magento 2.4.9 (current latest) is the recommended floor for production headless work; older versions may need custom resolvers to fill gaps.
  • Can I add custom GraphQL schemas?
    Yes — extending the GraphQL schema is a first-class pattern. Create etc/schema.graphqls in your custom module declaring new types, queries, or mutations in GraphQL SDL syntax. Implement a Resolver class that implements ResolverInterface. Wire the resolver to the schema field via etc/graphql/di.xml. The aggregated schema is compiled at bin/magento setup:di:compile time, so re-run that after schema changes. For cacheable resolvers, also implement IdentityInterface and return cache tags so Varnish / Fastly can key responses correctly.
  • How does authorization work for customer-specific queries?
    Customer-scoped queries (customer, customerCart, customerOrders, customerDownloadableProducts) require a customer access token sent in the Authorization: Bearer <token> HTTP header. Tokens are issued by the generateCustomerToken(email, password) mutation and have a configurable lifetime (default 1 hour, set in Stores > Configuration > Services > OAuth). Anonymous storefront queries (products, category, cart(cart_id) using a guest mask ID) need no token. Admin-only queries are not exposed via /graphql at all — admin access stays on /rest with an admin token.
  • Are GraphQL queries cached?
    Read queries are cacheable when the resolver opts in. The resolver result must implement IdentityInterface and return cache tags (cat_p_123 for product 123, cat_c_45 for category 45). Magento sets X-Magento-Cache-Id and X-Magento-Vary response headers; Varnish or Fastly then keys the cache on those headers and serves repeat queries from edge memory. When a tagged entity changes, Magento purges the cache by tag automatically. Mutations are uncacheable by definition. Customer-scoped queries and queries with a logged-in session are also uncacheable — they execute per-request through PHP-FPM every time.
Headless audit

Planning a Magento headless / GraphQL build?

Send your storefront URL or wireframes — I will run a GraphQL coverage check, sketch the resolver / cache architecture, and reply with a written quote, fixed-price scope and earliest start date. 24-business-hour turnaround.