Cutting Over‑Fetch in Magento 2: A Practical Guide to GraphQL for Headless Front‑Ends
Learn how Magento 2’s GraphQL API lets you fetch only the product fields you need, reducing over‑fetching for headless stores. See a concrete query example, trade‑offs, and a step‑by‑step checklist to enable caching and depth limits.
25 May 2026, 09:22 UTC

Problem: Over‑Fetching in Headless Magento Stores
When building a headless storefront on top of Magento 2, developers often hit the same performance wall: the client pulls more data than it actually needs. A REST call to /V1/products returns a fixed payload that includes thousands of fields, even if the page only displays the product name, price, and image. The extra data increases payload size, slows the first paint, and forces the client to filter out unwanted fields.
How can the front‑end request only the fields it needs without rewriting the entire API layer?
Thesis: GraphQL Gives You Field‑Level Precision
Magento 2’s GraphQL API lets a client specify exactly which product attributes to retrieve. A single request can replace many REST calls, and pagination is built into the schema. The result is leaner payloads, faster page loads, and a cleaner client code base.
1. Why GraphQL in Magento?
- Selective Data Retrieval: Clients declare the shape of the data they need.
- Single Endpoint: All data lives under
/graphql, eliminating the need for multiple REST URLs. - Pagination & Filtering: The schema exposes
pageSizeandcurrentPagearguments, plus filters for categories, attributes, and price ranges. - Cache‑Friendly: Magento’s Varnish or Redis layers can cache GraphQL responses if you enable the
X-GraphQL-Cacheheader.
2. Core Schema Overview
Magento’s GraphQL schema is generated from the app/code/Magento/GraphQl/etc/schema.xml files. Key entry points for product data include:
products– a connection type that returns a list ofProductobjects.product– fetch a single product byskuorid.categoryList– retrieve categories and their child categories.- Custom attributes defined in
app/code///etc/graphqlare automatically added to theProducttype.
3. A Concrete Query Example
Below is a typical query that a React or Vue headless client might send. It fetches the first 10 products in a category, pulling only the fields the UI needs and a custom attribute called color.
query GetCategoryProducts($categoryId: Int!, $pageSize: Int = 10, $currentPage: Int = 1) {
categoryList(filters: {category_id: {eq: $categoryId}}) {
items {
id
name
url_key
url_suffix
products(
pageSize: $pageSize
currentPage: $currentPage
) {
items {
sku
name
price {
regularPrice {
amount {
currency
value
}
}
}
media_gallery {
url
label
}
custom_attributes {
attribute_code
value
}
}
page_info {
total_pages
current_page
}
}
}
}
}
**Where to run**: POST to /graphql on the Magento instance. Permissions: the client must be authenticated if the store uses customer‑only product visibility. The request body contains the query string and a variables object:
{
"query": "...",
"variables": {
"categoryId": 42
}
}
**Expected checks**:
- The response JSON should contain a
data.categoryList.items[0].products.itemsarray with the requested fields. - Verify that
custom_attributesincludes an entry withattribute_code": "color"if the attribute exists. - If the query fails with
max depth exceeded, adjustmax_query_depthinapp/etc/di.xml.
4. Trade‑offs & Limitations
- Depth Limits
Magento enforces a default maximum query depth of 12. Deeply nested queries (e.g., product → price → regularPrice → amount) can hit this limit. Adjust the setting in
app/etc/di.xml:<type name="Magento\GraphQl\Model\Resolver\Query\Config"> <arguments> <argument name="max_query_depth" xsi:type="int">20</argument> </arguments> </type>After changing, run
bin/magento setup:di:compileand clear caches. - Cache Invalidation
Unlike REST, GraphQL does not automatically tie into Magento’s full‑page cache. To enable caching, set the
X-GraphQL-Cacheheader to1and configure Varnish to respect it. Be cautious: a cached GraphQL response may become stale if product data changes frequently. - Custom Schema Changes
Adding a new custom attribute to the GraphQL schema requires placing a
graphql.xmlfile inapp/code///etcand redeploying static content. Failure to do so means the attribute will not appear in queries. - Server Load
Complex queries with many nested fields can increase CPU usage. Monitor
var/log/debug.logfor query execution times and adjust caching or query complexity accordingly.
5. Actionable Checklist
- Define the exact product fields your UI needs.
- Create a GraphQL query that requests only those fields.
- Test the query in the GraphiQL playground (
/graphql?debug=1) and verify the response shape. - Check
max_query_depthand adjust if the query fails. - Enable
X-GraphQL-Cacheheader and configure Varnish to cache responses. - For custom attributes, add
graphql.xml, runbin/magento setup:static-content:deploy, and reindex. - Monitor performance with
var/log/debug.logandvar/log/varnish.logto ensure cache hits. - Document the query and its variables in your front‑end codebase.
By following this flow, a headless Magento store can deliver only the data it needs, reduce payload sizes, and keep the client logic simple.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.