80% off for waitlist membersJoin now and lock in Launch from $39.80 or Lifetime from $49.80 

← Back to Guides

WPGraphQL + WooCommerce: Complete Setup and Query Guide

WPBundle Team··13 min read
wpgraphql woocommerce setupwpgraphql woocommercewoocommerce graphqlwpgraphql tutorial

WPGraphQL + WooCommerce is the combination most developers reach for when building a headless WooCommerce frontend. It lets you query products, categories, variations, and cart data with a single request — no over-fetching, no chaining REST calls. But the setup has sharp edges. This guide walks through installation, schema exploration, real queries, cart mutations, and the gotchas that trip up even experienced WordPress developers.

TL;DR

Install WPGraphQL and WPGraphQL WooCommerce (WooGraphQL) on your WordPress site. Use the built-in GraphiQL IDE to explore the schema. Query products with nested images, categories, and variations in a single request. Use mutations for cart operations (add, update, remove). Mutations require session tokens. Caching is harder than REST — plan for it early.

What you need before starting

This guide assumes you have a working WordPress + WooCommerce installation. You do not need a headless frontend set up yet — you can explore everything through the GraphiQL IDE in wp-admin. If you're still deciding between GraphQL and REST for your project, read our WooCommerce REST API vs WPGraphQL comparison first.

  • WordPress 6.0 or later
  • WooCommerce 7.0+ (8.x recommended)
  • PHP 7.4 minimum (8.1+ recommended)
  • WPGraphQL plugin (latest stable)
  • WPGraphQL WooCommerce (WooGraphQL) extension
  • At least a few test products with images and categories

Version compatibility matters

WooGraphQL version must match your WPGraphQL version. Check the WooGraphQL GitHub repository compatibility matrix before installing. Mismatched versions are the single most common cause of schema errors.

Step 1: Install the plugins

You need two plugins. WPGraphQL adds the GraphQL server to WordPress. WPGraphQL WooCommerce (also called WooGraphQL) extends that server with WooCommerce types — products, product variations, product categories, cart, orders, and more.

Option A: Install from wp-admin

Go to Plugins → Add New and search for "WPGraphQL". Install and activate it. For WooGraphQL, download the latest release ZIP from the GitHub releases page and upload it via Plugins → Add New → Upload Plugin. WooGraphQL is not yet available on the WordPress.org plugin directory.

Option B: Install via Composer

If your WordPress project uses Composer (Bedrock, Sage, or similar), add both packages:

composer require wp-graphql/wp-graphql
composer require wp-graphql/wp-graphql-woocommerce

Activate both plugins after installation. Once activated, you'll see a new GraphQL menu item in wp-admin with links to the GraphiQL IDE and settings.

Step 2: Explore the schema with GraphiQL

Navigate to GraphQL → GraphiQL IDE in wp-admin. This is your most valuable tool during development. The IDE gives you autocompletion, inline documentation, and a schema explorer — use it before writing a single line of frontend code.

Click the Docs panel on the right side. You'll see root query types including products, productCategories, productTags, and cart. Each type expands to show available fields, nested types, and argument filters. This schema is your API contract — if a field exists here, you can query it.

Schema exploration tip

Type { in the query editor and press Ctrl+Space to trigger autocompletion. It's faster than browsing the docs panel for discovering available root queries and their arguments.

Step 3: Query products

Product queries are where WPGraphQL shines compared to the REST API. Instead of fetching a product, then its images, then its categories in separate calls, you get everything in one request. Here are the queries you'll use most often.

Basic product listing

query GetProducts {
  products(first: 12) {
    nodes {
      id
      databaseId
      name
      slug
      type
      ... on SimpleProduct {
        price
        regularPrice
        salePrice
      }
      ... on VariableProduct {
        price
        regularPrice
      }
    }
  }
}

Note the inline fragments (... on SimpleProduct). WooCommerce has multiple product types (Simple, Variable, Grouped, External) and each type has different pricing fields. Without inline fragments, you cannot access type-specific fields — this trips up every developer the first time.

Products with images and categories

query GetProductsWithMedia {
  products(first: 12) {
    nodes {
      name
      slug
      image {
        sourceUrl
        altText
      }
      galleryImages {
        nodes {
          sourceUrl
          altText
        }
      }
      productCategories {
        nodes {
          name
          slug
        }
      }
      ... on SimpleProduct {
        price
        stockStatus
      }
    }
  }
}

This single query replaces what would be three or four REST API calls. The response contains exactly the fields you requested — no extra data bloating the payload.

Filtering products

Use the where argument to filter products by category, tag, status, or search term. For example: products(first: 12, where: { category: "clothing" }) returns only products in the "clothing" category. Check the schema explorer for all available filter arguments.

Querying variable products and variations

Variable products are the most complex data type in WooCommerce, and they're the most common source of confusion in WPGraphQL. A variable product has attributes (like Size, Colour) and variations (specific combinations with their own price, stock, and image).

query GetVariableProduct($slug: ID!) {
  product(id: $slug, idType: SLUG) {
    name
    ... on VariableProduct {
      price
      regularPrice
      attributes {
        nodes {
          name
          options
        }
      }
      variations {
        nodes {
          databaseId
          name
          price
          regularPrice
          stockStatus
          attributes {
            nodes {
              name
              value
            }
          }
          image {
            sourceUrl
            altText
          }
        }
      }
    }
  }
}

The variations connection returns all child variations with their specific attribute values. On your frontend, use this data to build variant selectors — when a customer picks "Large" and "Blue", match those attribute values to find the correct variation's price and stock status.

1

Request for full product data

~50ms

Typical GraphQL response time

60-80%

Less data transferred vs REST

Cart mutations: add, update, and remove items

Cart operations use mutations rather than queries. This is where the WPGraphQL WooCommerce setup gets more involved, because mutations require session management. WooCommerce tracks cart state via server-side sessions, and your headless frontend needs to maintain that session across requests.

Adding an item to the cart

mutation AddToCart($productId: Int!, $quantity: Int) {
  addToCart(input: {
    productId: $productId
    quantity: $quantity
  }) {
    cartItem {
      key
      product {
        node {
          name
        }
      }
      quantity
      total
    }
  }
}

Viewing the cart

query GetCart {
  cart {
    contents {
      nodes {
        key
        product {
          node {
            name
            slug
          }
        }
        quantity
        total
      }
    }
    total
    subtotal
    totalTax
  }
}

Removing an item

mutation RemoveFromCart($keys: [ID]) {
  removeItemsFromCart(input: {
    keys: $keys
  }) {
    cart {
      contents {
        nodes {
          key
          quantity
        }
      }
      total
    }
  }
}

Session tokens are mandatory

After the first addToCart mutation, the response headers include a woocommerce-session token. You must send this token back in the headers of every subsequent request: woocommerce-session: Session <token>. Without it, each request creates a new empty cart. This is the number one mistake developers make when building headless WooCommerce carts.

Authentication for mutations

Product queries work without authentication — they return public data. But mutations and customer-specific queries (orders, account details) require authentication. You have several options depending on your architecture.

  • Application Passwords — built into WordPress 5.6+, simplest to set up
  • JWT Authentication — use the WPGraphQL JWT Authentication plugin for token-based auth
  • WooCommerce session tokens — sufficient for cart operations without user login
  • OAuth — more complex but suitable for multi-site or third-party integrations

For most headless WordPress projects, JWT authentication is the best balance of security and developer experience. Install the WPGraphQL JWT Authentication plugin, define a secret key in wp-config.php, and use the login mutation to exchange credentials for access and refresh tokens.

For anonymous cart operations (where users haven't logged in), the WooCommerce session token is sufficient. Store it in an HTTP-only cookie or in-memory on the client. Do not store session tokens inlocalStorage — it's vulnerable to XSS attacks.

GraphQL vs REST for WooCommerce

If you've read our REST API vs WPGraphQL comparison, you know both have legitimate use cases. Here's how the trade-offs look specifically for a WooCommerce integration.

Pros

  • Fetch products, images, categories, and variations in a single request
  • No over-fetching — request only the fields your frontend renders
  • Strongly typed schema acts as living documentation
  • GraphiQL IDE makes development and debugging significantly faster
  • Excellent fit for React/Next.js frontends with Apollo or urql

Cons

  • Checkout and payment flows are still more reliable via REST
  • Caching is harder — no native HTTP caching like REST endpoints
  • Plugin conflicts: some WooCommerce extensions break the GraphQL schema
  • Smaller community than REST — fewer Stack Overflow answers and tutorials
  • WooGraphQL is community-maintained, not officially supported by Automattic

The pragmatic approach used by most production headless WooCommerce stores: use WPGraphQL for product catalogue queries (listings, single product pages, category pages) and the WooCommerce REST API for checkout and order processing. This gives you the best of both protocols.

Caching challenges with GraphQL

REST APIs get HTTP caching almost for free. Each endpoint has a unique URL, so CDNs, browser caches, and reverse proxies can cache responses with standard Cache-Control headers. GraphQL sends all requests to a single /graphql endpoint via POST — standard HTTP caches cannot distinguish between different queries.

This matters for WooCommerce stores because product pages are often the highest-traffic pages. Without caching, every product page view hits your WordPress database. Here are the strategies that work.

  • Persisted queries — hash queries and use GET requests so CDNs can cache them
  • Application-level caching — cache query results in Redis or Memcached on the server
  • Client-side caching — Apollo Client and urql both have normalised caches
  • WPGraphQL Smart Cache plugin — provides automatic cache invalidation based on post/product changes
  • Static generation — use Next.js ISR or SSG to pre-render product pages at build time
  • Edge caching — services like Cloudflare Workers can cache GraphQL responses at the edge

If you're using Next.js, Incremental Static Regeneration (ISR) is often the simplest solution. Product pages are generated at build time and revalidated periodically. This eliminates GraphQL query latency for the majority of page views. Our guide to using WordPress as a headless CMS covers this pattern in detail.

Common gotchas and how to fix them

After helping teams set up WPGraphQL with WooCommerce across dozens of projects, these are the issues that come up repeatedly. Save yourself hours of debugging.

Schema conflicts from other plugins

Some WordPress plugins register custom post types or taxonomies that conflict with WPGraphQL's type system. If you see errors like "Type name must be unique" or the GraphiQL IDE fails to load, deactivate plugins one by one to find the culprit. Common offenders include SEO plugins that register duplicate types, custom field plugins with poorly namespaced types, and page builders that register content blocks as post types.

Variations not appearing in queries

If your variable product query returns an empty variations connection, check two things. First, ensure variations are actually published — draft or private variations are excluded from public queries. Second, verify that each variation has all required attributes set. WooCommerce allows "any" attribute values on variations, but WooGraphQL requires explicit values.

Debug with the query analyser

Enable GraphQL → Settings → Enable Query Logs in wp-admin to see the SQL queries generated by each GraphQL request. This is invaluable for diagnosing slow queries or missing data. Turn it off in production — it adds overhead and exposes internal details.

Cart session disappearing

The most frustrating bug for headless WooCommerce developers. Your cart works in the GraphiQL IDE but not from your frontend. The cause is almost always one of these: CORS headers blocking the session token, your frontend not forwarding the woocommerce-session header on subsequent requests, or your server's PHP session configuration expiring sessions too aggressively.

Pagination and the connection model

WPGraphQL uses Relay-style cursor pagination, not offset pagination. You cannot request "page 5" — you request "the next 12 items after cursor X". This is more efficient for large catalogues but requires a different mental model. Use the pageInfo field to get endCursor and hasNextPage values for implementing "load more" patterns.

Performance considerations

A well-configured WPGraphQL WooCommerce setup can outperform REST for read-heavy workloads, but performance is not automatic. Deep queries — requesting products with all variations, all images, all categories, and all attributes — can generate expensive database joins.

Limit query depth. If your product listing page only shows the product name, price, and featured image, only query those fields. Save the full product data (variations, gallery images, related products) for the single product page query. This keeps response sizes small and database load manageable.

Query complexity limits

WPGraphQL includes a query complexity analyser that rejects excessively deep or wide queries. The default limit is 1000 points. If your product query is rejected, simplify it — you're probably fetching more data than your frontend needs. You can adjust the limit via the graphql_max_query_amount filter, but increasing it is usually the wrong fix.

For stores with thousands of products, consider implementing a proper headless migration strategy that includes a dedicated data layer or search service (Algolia, Meilisearch) for catalogue browsing, with GraphQL reserved for individual product queries and cart operations.

Recommended project structure

Once your WPGraphQL WooCommerce setup is working, organise your frontend queries consistently. Most Next.js projects use a pattern like this:

/lib
  /graphql
    /queries
      products.ts      # Product listing queries
      product.ts       # Single product query
      categories.ts    # Category queries
    /mutations
      cart.ts           # addToCart, removeFromCart, updateCart
      checkout.ts       # Checkout mutation
    /fragments
      productFields.ts  # Reusable product field fragments
      imageFields.ts    # Reusable image fragments
    client.ts           # Apollo/urql client setup with session handling

Use GraphQL fragments to avoid duplicating field selections across queries. Define your product fields once and reference them in both the listing query and the single product query. This keeps queries maintainable as your data requirements evolve.

What comes next

With WPGraphQL and WooGraphQL installed, your schema explored, and your first queries running, you're ready to build your headless frontend. The typical next steps are:

  • Set up your Next.js project with Apollo Client or urql
  • Implement product listing pages with pagination
  • Build single product pages with variation selectors
  • Add cart functionality with session token management
  • Integrate checkout via WooCommerce REST API or Stripe
  • Configure ISR for performance and SEO

If you're planning a full migration to headless WooCommerce, read our migration guide for a complete checklist covering SEO redirects, payment gateway compatibility, and content migration. For a broader look at the architecture, our headless WordPress guide covers routing, previews, and deployment patterns.

WPBundle handles the infrastructure side of headless WooCommerce — optimised WordPress hosting, pre-configured WPGraphQL, and a managed deployment pipeline — so you can focus on building your frontend rather than debugging server configuration.

Ready to go headless?

Join the WPBundle waitlist and get beta access completely free.

Join the Waitlist