Chat on WhatsApp
Magento glossary

What is Magento GraphQL ?

Magento GraphQL is the storefront API that arrived in Magento 2.3 (November 2018). It lives at a single URL, POST /graphql, and serves catalog, customer, cart, checkout and order data through queries and mutations. PWA Studio runs on it, and so do most custom React or Vue frontends and mobile apps I connect to Magento. The client asks for exactly the fields it needs and gets them back in one round-trip.

How it works

Five things Magento GraphQL actually does under the hood

Under the hood there are three parts: a typed schema, resolver classes that call Magento's service contracts, and HTTP caching through Varnish. This is how they fit together.

  1. 01

    Single endpoint, client-specified field selection

    Magento has one URL, POST /graphql, for every query and mutation. The client lists the fields it wants (name, price, image.url, nested variants { sku }) and Magento returns only those. With REST you often get the whole entity when you needed three fields, or you need a second call for related data. With GraphQL one request returns the exact tree the page needs.

  2. 02

    Schema declared per-module via schema.graphqls files

    Each module that adds to the API ships an etc/schema.graphqls file in GraphQL SDL. Magento_CatalogGraphQl defines the products, categories and category queries; Magento_QuoteGraphQl defines cart and addProductsToCart; Magento_CustomerGraphQl defines customer and createCustomer. In my own modules I add types and fields in the same kind of file. Magento merges every module’s file into one schema and caches the result.

  3. 03

    Resolvers map GraphQL types to PHP service contracts

    Each field that needs data has a resolver class implementing Magento\Framework\GraphQl\Query\ResolverInterface. Resolvers call the same service contracts (repositories, search APIs) the REST layer uses, so they are a thin transport layer, not a second data layer. That keeps GraphQL and REST in step as the catalog, customer and cart code changes. A custom field points to its resolver with the @resolver(class: "...") directive in schema.graphqls.

  4. 04

    Cacheable queries cached via Varnish + x-magento-vary

    Read queries for catalog pages, category lists and product detail can be cached at the HTTP layer when the query is sent as GET and its resolvers declare cache tags. Magento sets the X-Magento-Cache-Id header, and Varnish (or Fastly on Adobe Commerce Cloud) uses it to serve repeat queries without touching PHP. Customer data such as cart, account and wishlist is never cached and goes to PHP on every request.

  5. 05

    Mutations bypass cache and run synchronously

    Mutations such as addProductsToCart, placeOrder, createCustomer and updateCustomerAddress skip the HTTP cache and run straight through the same service contracts the REST API uses. Most mutations return the changed object, for example the updated cart, so the frontend can refresh its state from the response without a second query.

When to use

Four situations where GraphQL is the right Magento API

I don't use GraphQL for everything. For batch and admin work REST is still the better fit. These are the cases where GraphQL is the clear choice, plus the one where it isn't.

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

    If the storefront is a separate frontend app (PWA Studio with React and Apollo, Vue Storefront, or a custom Next.js or Nuxt build), GraphQL is the supported way to talk to Magento. PWA Studio uses it by default, and new storefront features in each 2.4.x release usually arrive in GraphQL first. Building a new headless frontend on REST means working against the platform.

  • Multi-channel app from a single Magento backend

    When one Magento backend feeds a website, an iOS app, an Android app and maybe an in-store kiosk, each client can ask for only the fields it uses. The app pulls small thumbnails and short descriptions, the website pulls full galleries and rich HTML, the kiosk pulls price and stock. Same backend, same schema, a different payload per channel. With REST every channel gets the same fixed entity shape.

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

    A product page usually needs the product, its images, its configurable children, the category breadcrumb, reviews and related products. Over REST that is several calls, often one after another, and on a slow mobile connection the waits add up. One GraphQL query returns the whole nested tree, resolved on the server, as a single JSON response.

  • Avoid for: bulk imports, simple admin tasks

    GraphQL is built for storefront reads and single cart, checkout and order actions. It is a poor fit for batch work. Importing thousands of products one mutation at a time takes hours and ties up PHP-FPM. For imports I use the bulk async REST endpoints, the Magento_ImportExport CSV import, or a DataPatchInterface patch for one-time data.

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.