WPGraphQL + WooCommerce: Complete Setup and Query Guide
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
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
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-woocommerceActivate 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
{ 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
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
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
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
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 handlingUse 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