Skip to main content

Magento 2 Headless Commerce Architecture Guide

9 min read
Plan my headless Magento

We design the decoupled frontend, GraphQL layer and services around your Magento backend.

Architect a headless Magento 2 storefront with a decoupled frontend, GraphQL API layer, and microservices for scalable modern commerce experiences.

Why Go Headless

Traditional Magento 2 architecture couples the frontend theme layer directly to the backend PHP application. Every page request passes through the Magento rendering pipeline - layout XML, blocks, templates, and KnockoutJS components. This architecture served well for years, but modern commerce demands faster storefronts, omnichannel experiences, and independent frontend deployment cycles.

Headless commerce separates the presentation layer from the commerce engine. The Magento 2 backend becomes a pure API server handling catalog, cart, checkout, and order management, while a dedicated frontend framework delivers the customer experience.

Architecture Overview

A production headless Magento 2 setup consists of several layers:

Frontend Layer: A Next.js or React application deployed on edge infrastructure (Vercel, CloudFront, or a Kubernetes cluster). This handles all rendering - either server-side (SSR) or static (SSG) - and communicates with the backend exclusively through APIs.

API Gateway: An NGINX or AWS API Gateway layer that routes requests, handles rate limiting, and manages authentication tokens. This sits between the frontend and the Magento backend.

Commerce Engine: Magento 2 running in API-only mode with the storefront theme disabled. GraphQL and REST endpoints serve product data, cart operations, and checkout flows.

Services Layer: Microservices for search (Elasticsearch/OpenSearch), payments (Stripe/Adyen), and content (headless CMS like Contentful or Strapi).

Setting Up the GraphQL API

Magento 2 ships with a comprehensive GraphQL API. Enable it and configure the schema for headless use:

// app/etc/env.php
'system' => [
    'default' => [
        'web' => [
            'graphql' => [
                'cors_allowed_origins' => 'https://store.example.com',
                'cors_allowed_methods' => 'GET,POST,OPTIONS',
                'cors_max_age' => 86400,
            ],
        ],
    ],
],

Product Query Example

query GetProductPage($urlKey: String!) {
  products(filter: { url_key: { eq: $urlKey } }) {
    items {
      sku
      name
      price_range {
        minimum_price {
          final_price { value currency }
          discount { percent_off }
        }
      }
      media_gallery {
        url
        label
      }
      description { html }
      related_products {
        sku
        name
        thumbnail { url }
      }
    }
  }
}

Frontend Architecture with Next.js

Build the storefront with Next.js for optimal performance through server-side rendering and static generation:

// lib/magento-client.ts
const MAGENTO_GRAPHQL_URL = process.env.MAGENTO_GRAPHQL_URL!;

export async function magentoFetch<T>(
  query: string,
  variables?: Record<string, unknown>
): Promise<T> {
  const response = await fetch(MAGENTO_GRAPHQL_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Store: "default",
    },
    body: JSON.stringify({ query, variables }),
    next: { revalidate: 60 },
  });
  const json = await response.json();
  if (json.errors) throw new Error(json.errors[0].message);
  return json.data;
}

Static Generation for Category Pages

export async function generateStaticParams() {
  const data = await magentoFetch<CategoryData>(
    `{ categories(filters: {}) { items { url_path children { url_path } } } }`
  );
  return data.categories.items.flatMap((cat) =>
    [cat, ...cat.children].map((c) => ({ slug: c.url_path.split("/") }))
  );
}

Caching Strategy

Implement multi-layer caching for performance:

CDN Layer: Cache product listing pages and category pages at the CDN edge with 60-second TTL. Use stale-while-revalidate headers to serve cached content while fetching fresh data.

Application Layer: Use Next.js ISR (Incremental Static Regeneration) with on-demand revalidation triggered by Magento webhooks when products or prices change.

API Layer: Deploy Varnish or Redis in front of the Magento GraphQL endpoint. Cache GET-equivalent POST requests using query hashing.

Handling Cart and Checkout

Cart operations are stateful and require careful session management in a headless context:

// Create guest cart
const { createEmptyCart } = await magentoFetch<{ createEmptyCart: string }>(
  `mutation { createEmptyCart }`
);
// Store cart ID in HTTP-only cookie
cookies().set("cart_id", createEmptyCart, {
  httpOnly: true,
  secure: true,
  sameSite: "strict",
  maxAge: 60 * 60 * 24 * 30,
});

Performance Benchmarks

A well-implemented headless Magento 2 storefront typically achieves:

  • Time to First Byte: under 200ms (vs. 800-1200ms traditional)
  • Largest Contentful Paint: under 1.5s (vs. 3-5s traditional)
  • Core Web Vitals: all green across mobile and desktop
  • API response times: under 100ms for cached queries

Deployment Architecture

Deploy the frontend independently from Magento using separate CI/CD pipelines. The frontend deploys in seconds via edge platforms, while Magento backend deployments follow the standard zero-downtime process with maintenance mode and cache warming.

For Magento 2 performance optimization and headless architecture consulting, our team brings deep experience across dozens of headless implementations for B2B and B2C merchants.

Frequently asked questions

Why would a Magento 2 store move to a headless architecture?

In a traditional Magento 2 setup every page request passes through the PHP rendering pipeline of layout XML, blocks, templates and KnockoutJS components. Going headless turns Magento into an API server for catalog, cart, checkout and order management, while a separate frontend framework renders the storefront. Merchants get faster storefronts, omnichannel experiences and frontend deployment cycles that run independently of the backend.

What are the layers of a headless Magento 2 architecture?

A production headless Magento 2 setup has four layers. A Next.js or React frontend runs on Vercel, CloudFront or Kubernetes and talks to the backend only through APIs. An NGINX or AWS API Gateway layer handles routing, rate limiting and authentication tokens. Magento 2 runs in API-only mode with the storefront theme disabled, and a services layer covers search (Elasticsearch or OpenSearch), payments (Stripe or Adyen) and content from a headless CMS such as Contentful or Strapi.

How do I configure CORS for the Magento 2 GraphQL API in a headless setup?

Add a graphql block under system, default and web in app/etc/env.php with cors_allowed_origins set to the storefront domain, cors_allowed_methods set to GET,POST,OPTIONS and cors_max_age set to 86400. The Next.js storefront then posts its queries to the GraphQL URL as JSON, with a Store header set to default.

How do I query a product by URL key with Magento 2 GraphQL?

Use the products query with a filter on url_key using eq, and pass the URL key in as a variable. Request only what the product page renders, for example sku, name, price_range with the minimum final_price and discount percent_off, media_gallery, description html and related_products with their thumbnails. A single query then returns everything a product page needs.

How should a headless Magento storefront store the guest cart ID?

Create the guest cart with the createEmptyCart GraphQL mutation and keep the returned cart ID in an HTTP-only cookie. Mark the cookie secure with sameSite strict and a maxAge of 60 * 60 * 24 * 30, which keeps the cart for 30 days. Cart operations are stateful, so session handling needs more care in a headless build than catalog pages do.

How do I cache a headless Magento 2 storefront?

Caching works at three layers. At the CDN edge, product listing and category pages get a 60-second TTL with stale-while-revalidate headers. In Next.js, Incremental Static Regeneration uses on-demand revalidation triggered by Magento webhooks when products or prices change. In front of the Magento GraphQL endpoint, Varnish or Redis caches GET-equivalent POST requests by hashing the query.

How fast is a headless Magento 2 storefront compared with a traditional theme?

A well-implemented headless Magento 2 storefront typically reaches a Time to First Byte under 200ms, against 800 to 1200ms for a traditional storefront. Largest Contentful Paint drops under 1.5s from 3 to 5s. Core Web Vitals come out green on mobile and desktop, and cached API queries answer in under 100ms.

Can the frontend of a headless Magento store be deployed separately from Magento?

Yes. Give the frontend and the Magento backend separate CI/CD pipelines. The frontend deploys in seconds through an edge platform, while Magento backend deployments keep the standard zero-downtime process with maintenance mode and cache warming.

Read how we handle it in DevOps for e-commerce.