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.
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.
-
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, nestedvariants { 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. -
02
Schema declared per-module via schema.graphqls files
Each module that adds to the API ships an
etc/schema.graphqlsfile in GraphQL SDL.Magento_CatalogGraphQldefines theproducts,categoriesandcategoryqueries;Magento_QuoteGraphQldefinescartandaddProductsToCart;Magento_CustomerGraphQldefinescustomerandcreateCustomer. 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. -
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 inschema.graphqls. -
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-Idheader, 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. -
05
Mutations bypass cache and run synchronously
Mutations such as
addProductsToCart,placeOrder,createCustomerandupdateCustomerAddressskip 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.
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_ImportExportCSV import, or aDataPatchInterfacepatch for one-time data.
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 fullProductInterfacefield 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
IdentityInterfaceon the resolver result and return cache tags (cat_p_,cat_c_) so Varnish can key and invalidate the response. Addcache: { tags: [...], cacheable: true }declarations inetc/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
addProductsToCart500 times to seed test orders, orcreateProduct10,000 times to import a CSV catalog, will burn hours of PHP-FPM time, blow pastmax_execution_timedefaults, 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,customerOrdersqueries are customer-scoped and require anAuthorization: Bearer <token>header. Without it Magento returns a "The current customer isn't authorized" error rather than the customer's data. Issue tokens via thegenerateCustomerTokenmutation, 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.
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. Createetc/schema.graphqlsin your custom module declaring new types, queries, or mutations in GraphQL SDL syntax. Implement a Resolver class that implementsResolverInterface. Wire the resolver to the schema field viaetc/graphql/di.xml. The aggregated schema is compiled atbin/magento setup:di:compiletime, so re-run that after schema changes. For cacheable resolvers, also implementIdentityInterfaceand 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 theAuthorization: Bearer <token>HTTP header. Tokens are issued by thegenerateCustomerToken(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 implementIdentityInterfaceand return cache tags (cat_p_123for product 123,cat_c_45for category 45). Magento setsX-Magento-Cache-IdandX-Magento-Varyresponse 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.