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.
- Introduced Magento 2.3 (Nov 2018)
- Endpoint POST /graphql
- Spec GraphQL 2018 + webonyx/graphql-php
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.
01Single 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.
02Schema 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.
03Resolvers 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.
04Cacheable 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.
05Mutations 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.
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.
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.
Where Magento GraphQL sits in the wider headless landscape
Four neighbour concepts most readers want to look at next. Click through for the full deep-dive.
- Magento headless / PWAFull pillar on going headless with Magento, GraphQL transport plus PWA Studio, Hyvä-headless or custom SPA. Architecture, cost, timeline.
- Hyvä vs PWA StudioServer-rendered Hyvä or React headless via PWA Studio? Stack-by-stack breakdown, cost-of-ownership maths, decision matrix.
- What is PWA StudioAdobe's React-based reference headless storefront for Magento. Apollo client, Venia theme, build tooling and where it fits.
- What is Hyvä ThemesThe Tailwind + Alpine Magento frontend rebuild. Server-rendered alternative or headless GraphQL client, both supported.
Magento GraphQL: frequently asked questions
GraphQL vs REST in Magento: which should I use?
Magento GraphQL performance: is it fast?
Which Magento versions support GraphQL?
Can I add custom GraphQL schemas?
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, customerCart, customerOrders, customerDownloadableProducts) require a customer access token sent in the Authorization: Bearer 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?
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.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.