GraphQL in Headless Frontends: Magento with React, Vue or Next.js
AI generated
{ }
type
GraphQL · Headless · React · Vue · Next.js · Magento
GraphQL in Headless Frontends:
Magento with React, Vue or Next.js

Headless commerce with Magento GraphQL promises flexibility. In practice, Apollo Client configuration, type-safe code generation, SSR/SSG caching and fragment design decide whether the frontend stays fast and maintainable or turns into a complexity trap.

20 min read Apollo Client · Code Gen · SSR · SSG · Fragments Magento GraphQL · React · Vue · Next.js

1. Headless commerce: what the architectural promise means

Headless commerce means the frontend is completely decoupled from the commerce backend. Magento serves as the data and business logic layer, while the frontend is a standalone application that communicates over APIs. GraphQL is the preferred interface here because it lets the frontend request exactly the data a given page needs, without having to coordinate multiple REST endpoints. The architectural promise is: better frontend performance, freedom to choose whichever frontend technology fits, and the ability to evolve the frontend independently of Magento's upgrade cycle.

In practice, this promise only holds up when several prerequisites are met. The GraphQL schema must be stable and well documented so the frontend team can work independently. Caching needs to be thought through at multiple levels: the Apollo in-memory cache, an HTTP cache layer and the CDN edge cache. And the customer context (cart, login state, price group) must be transported securely and performantly through session tokens. Anyone who only swaps out the frontend without addressing these points simply trades well-known Luma problems for new headless problems.

2. Apollo Client vs. URQL vs. fetch: making the right choice

Apollo Client is the most comprehensive solution: a normalized in-memory cache, React hooks (useQuery, useMutation), optimistic updates, pagination utilities and a large ecosystem of libraries. For complex commerce frontends with a cart, product listings, search and customer context, Apollo Client is the strongest choice, even though the initial configuration is more involved. The normalized cache is especially valuable when the same products are loaded through different queries; Apollo deduplicates them automatically.

URQL is lighter, more modular and easier to configure. For projects that don't need every Apollo feature, URQL is a good alternative. Plain fetch with a custom wrapper function is sufficient for SSR/SSG contexts in Next.js, where queries only run once at page-generation time and no client-side cache is needed. The right choice depends on how complex the frontend is: the more client-side state and update logic, the more Apollo Client pays off.


# Fragment-based product query, collocated with the ProductCard component
# This query is generated and typed by GraphQL Code Generator

fragment ProductCardFragment on ProductInterface {
  __typename
  sku
  name
  url_key
  small_image {
    url
    label
  }
  price_range {
    minimum_price {
      final_price {
        value
        currency
      }
      discount {
        percent_off
      }
    }
  }
}

query GetCategoryProducts(
  $categoryId: String!
  $pageSize: Int = 24
  $currentPage: Int = 1
) {
  products(
    filter: { category_id: { eq: $categoryId } }
    pageSize: $pageSize
    currentPage: $currentPage
    sort: { position: ASC }
  ) {
    total_count
    page_info {
      current_page
      page_size
      total_pages
    }
    items {
      ...ProductCardFragment
    }
  }
}

3. Code generation: type-safe queries in TypeScript

GraphQL Code Generator analyzes the Magento schema together with the queries and fragments defined in the frontend, and generates fully type-safe TypeScript types and React hooks from them. The result: instead of manually maintaining interface definitions for every query response, types are derived automatically from the schema. If Magento renames a field or changes a type in the next release, code generation fails immediately, which means the breaking change becomes visible in the frontend before it causes problems in production.

Configuring GraphQL Code Generator for Magento requires downloading the schema. The Magento GraphQL schema can be obtained through the introspection endpoint (POST /graphql with the introspection query) or provided as an SDL file. For teams, an automatic schema download in the CI pipeline that runs on every Magento version update is recommended. That way generated types stay in sync with the actual Magento schema, and discrepancies are caught in CI before a developer spends an hour debugging a type error.

4. Fragment colocation: queries close to the component

Fragment colocation is one of the most important principles for maintainable GraphQL frontends. The idea: every React component defines its own fragment describing the data it needs. The parent page component combines these fragments into a single query. When a component needs new data, it adds fields to its own fragment, and the query updates automatically. This eliminates the common situation where a central query file has to be maintained for the entire page's data needs, with developers hunting for which fields belong to which components.

Combined with the near-operation-file preset in GraphQL Code Generator, the generated hook file ends up right next to the query file. The entire data pattern, meaning component, fragment, generated hook and types, lives in a single directory. During a code review it's immediately obvious what a component renders and which data it requests. This pattern also scales well in larger teams: frontend teams can work on different page sections in parallel without creating query conflicts.


# Authentication: customer token flow in Magento GraphQL
# Step 1: generate token with credentials
mutation GenerateCustomerToken($email: String!, $password: String!) {
  generateCustomerToken(email: $email, password: $password) {
    token
  }
}

# Step 2: use token in Authorization header for subsequent requests
# Authorization: Bearer <token>

# Step 3: fetch customer data with active session
query GetCustomerData {
  customer {
    firstname
    lastname
    email
    addresses {
      id
      firstname
      lastname
      street
      city
      postcode
      country_code
      default_shipping
      default_billing
    }
    orders(pageSize: 5) {
      items {
        number
        order_date
        status
        total {
          grand_total { value currency }
        }
      }
    }
  }
}

# Step 4: revoke token on logout
mutation RevokeCustomerToken {
  revokeCustomerToken {
    result
  }
}

5. Caching in the headless frontend: Apollo, SSR and CDN

The Apollo in-memory cache normalizes all GraphQL responses by type and ID. In practice that means: if a product with SKU DEMO-001 is loaded in a category listing and later requested again on the product detail page, Apollo returns the cached version without another network request. This normalization works automatically for every type that has an id field. For Magento types that don't use a standard ID (such as product types that use sku instead of id), the Apollo cache needs to be adjusted with a custom keyFields configuration.

At the HTTP level, Magento GraphQL queries are fundamentally POST requests and aren't cached by HTTP caches. Persisted queries convert frequently used queries into GET requests with a hash as a parameter. These GET requests can be cached by CDNs and reverse proxies. Magento supports persisted queries natively for catalog data; queries without customer context can then be served from the CDN with very high cache hit rates. That reduces the load on Magento and significantly improves time to first byte.

6. SSR and SSG with Next.js and Magento GraphQL

Next.js offers three rendering strategies, each requiring a different GraphQL caching approach. Static Site Generation (SSG) with getStaticProps is a good fit for product detail pages and category pages without customer context: the GraphQL query runs at build time, and the page is delivered as static HTML. Incremental Static Regeneration (ISR) extends SSG with automatic refreshes after a configurable interval, which is ideal for category pages that don't change on every request but still need to stay current. Server-Side Rendering (SSR) is necessary whenever customer context affects the rendered data, for example personalized prices or store credit information.

One important caveat: Apollo Client is primarily designed for the browser. In Next.js SSR contexts, it's advisable to either use a server-only fetch pattern without Apollo, or use Apollo with @apollo/client/react/ssr and the renderToStringWithData function, which resolves all Apollo queries during server rendering. For most commerce pages, a simple fetch inside getStaticProps combined with a separate Apollo Client only in the browser is the clearer and more maintainable solution.

7. React vs. Vue vs. Next.js: what fits which project

The choice of frontend framework shapes the GraphQL client choice and the caching architecture. React with Apollo Client is the best-documented combination with Magento GraphQL and has the largest ecosystem support. Next.js builds on top of that and adds SSR/SSG and file-system routing, making it the natural choice for commerce projects where SEO and initial-load performance are critical. Vue 3 with URQL or the Vue Apollo wrapper is a solid choice for teams with existing Vue experience; the GraphQL patterns are the same, only the component API differs.

Criterion React + Apollo Next.js Vue 3 + URQL
SEO & SSR Extra effort required Natively supported Requires Nuxt.js
Caching Normalized cache ISR + CDN Simple cache
Magento docs Extensive Extensive Fewer examples
Code gen support Full Full Full
Learning curve Moderate complexity Moderate complexity Easy start

For new headless Magento projects, Next.js is currently the default recommendation: the combination of SSG for catalog pages, SSR for personalized pages and native image optimization support makes it the strongest commerce framework in the React ecosystem. Vue teams typically settle on Nuxt.js and URQL, applying the same GraphQL principles.

8. Common mistakes in headless operation

The most common mistake: reloading all data from Magento on every request without using any caching layers. Category pages that load the entire product list fresh from Magento on every request create unnecessary load and poor TTFB numbers. The fix is a clear separation by data type: static catalog data gets cached (ISR, CDN), while data tied to the customer session is never cached.

The second common mistake is loading too many fields in a query. GraphQL queries that pull in hundreds of fields because the frontend might theoretically need them place unnecessary load on Magento resolvers. Fragment colocation solves this structurally: every component only requests the fields it actually renders. The third mistake concerns handling customer tokens: customer tokens stored in localStorage are vulnerable to XSS. For commerce applications, HttpOnly cookie storage is the safer alternative, though it requires CORS configuration on the Magento server.

9. Authentication and customer context in the headless frontend

Magento GraphQL uses bearer token authentication. A customer token is issued through the generateCustomerToken mutation and must be sent with every subsequent request as an Authorization: Bearer <token> header. Tokens have no built-in expiry in stock Magento, so validity has to be controlled through configuration or a custom module. When implementing this in the frontend, it must be guaranteed that queries containing customer data are never cached, neither in the Apollo in-memory cache for other users nor in the CDN.

The cart in a headless frontend is a particularly critical point. Magento distinguishes between guest carts (identified by a cart ID string) and customer carts (linked to the customer token). On login, the guest cart must be merged into the customer cart (mergeCarts mutation). This logic is error-prone and needs to be tested explicitly, especially in combination with browser tab switching and parallel sessions. A well-structured state management layer for the cart context matters more than the technical GraphQL implementation itself.


# Cart management in headless Magento: guest to customer merge
# Step 1: create guest cart
mutation CreateGuestCart {
  createEmptyCart
}
# Returns: "abc123" (store as cookie or localStorage)

# Step 2: add items to guest cart
mutation AddToGuestCart($cartId: String!, $sku: String!, $qty: Float!) {
  addSimpleProductsToCart(input: {
    cart_id: $cartId
    cart_items: [{ data: { sku: $sku, quantity: $qty } }]
  }) {
    cart {
      id
      totalQuantity
    }
  }
}

# Step 3: after login, merge guest cart into customer cart
mutation MergeCarts($guestCartId: String!, $customerCartId: String!) {
  mergeCarts(
    source_cart_id: $guestCartId
    destination_cart_id: $customerCartId
  ) {
    id
    totalQuantity
    itemsV2 {
      items {
        product { sku name }
        quantity
      }
    }
  }
}

10. Summary

Headless commerce with Magento GraphQL is a mature architecture that delivers real benefits when implemented carefully: frontend independence, precise data fetching and better performance through multi-tier caching. The critical success factors are: type-safe code generation with GraphQL Code Generator, fragment colocation for maintainable queries, clearly separated caching strategies for static and customer-related data, and robust cart-merge logic in the login flow.

Next.js is currently the strongest choice for new headless Magento projects because SSG, ISR and SSR are natively integrated and the caching architecture fits commerce requirements well. Apollo Client is the recommended GraphQL client library for complex commerce frontends. Simpler projects can start with URQL or plainer fetch patterns and migrate to Apollo later if needed.

GraphQL in Headless Frontends: the key takeaways at a glance

Framework choice

Next.js for SEO-critical commerce projects. React + Apollo for complex client-side state logic. Vue + Nuxt for Vue teams.

Code generation

GraphQL Code Generator with the Magento schema automates type-safe hooks and types. Breaking changes become visible in CI.

Caching

Static catalog data: ISR + CDN. Customer data: never cache. Configure the Apollo cache with keyFields for Magento types.

Cart & Auth

Store the guest cart ID in a cookie. Test mergeCarts on login explicitly. Customer token in an HttpOnly cookie, never in localStorage.

11. FAQ: GraphQL in Headless Frontends with Magento

1Which framework for headless Magento?
Next.js for SEO-critical commerce projects thanks to native SSG, ISR and SSR. For Vue teams: Nuxt.js.
2What does GraphQL Code Generator give you?
Automatically type-safe TypeScript types and hooks from the schema. Breaking changes become visible in CI before they reach production.
3What is fragment colocation?
Every component defines its own fragment. The page combines fragments into a query. Changes stay local to the component.
4CDN caching for GraphQL queries?
Persisted queries convert POST into GET and enable CDN caching. Only queries without customer context are suitable for this.
5How do you secure the customer token?
HttpOnly cookies instead of localStorage. XSS-resistant since JavaScript has no access. Requires CORS configuration on the Magento server.
6What is the cart-merge problem?
On login, the guest cart must be merged with the customer cart. Implement and test the mergeCarts mutation explicitly.
7Fetch instead of Apollo: when does it make sense?
For SSG/SSR in Next.js. Apollo pays off with client-side caching, optimistic updates and complex state logic.
8How do I avoid too many fields in queries?
Fragment colocation: every component only requests the fields it renders. Unused fields become visible with code-gen analysis.
9What is ISR in Next.js?
Incremental Static Regeneration: static pages are automatically regenerated after an interval. Ideal for Magento category pages.
10How do I test the headless GraphQL layer?
MSW for component unit tests, Playwright/Cypress for E2E against staging, contract tests for critical schema dependencies.