Headless WooCommerce

Unstoppable Headless WooCommerce: How I Joyfully Catapulted My Client’s 6-Figure Store to Netlify and Slashed Costs by a Whopping 87 %

I’ve spent the last seven years elbow-deep in WordPress—fortune-500 landing pages, SaaS dashboards, you name it. Last quarter a long-time client (let’s call them “Bloom Coffee Co.”) slid into my inbox with a familiar panic:

“Black-Friday traffic is coming. Our checkout is already wheezing and our AWS bill just eclipsed our bean budget.”

Challenge accepted.
I promised:

  1. Keep the WooCommerce dashboard their ops team loves.
  2. Slash hosting costs before November.
  3. Keep Core Web Vitals green when 3,000 caffeine-starved shoppers hit “Buy Now.”

Here’s the exact playbook I followed


1. Why Headless WooCommerce Feels Like Cheating

Traditional WooCommerce = PHP + MySQL on every request = expensive autoscaling.
Headless WooCommerce = WordPress builds JSON once, Netlify’s edge CDN serves static pages and serverless functions handle the cart/checkout.
Bloom’s traffic spikes to 30 k sessions on launch days—Netlify’s free tier still fits because static assets don’t spin up servers.


1. Why Headless WooCommerce Feels Like Cheating

LayerToolCost / mo
WordPress back-endKinsta “Application” plan\$7 (yes, really)
Front-end frameworkNext.js 14 (App Router)\$0
Theme / UIAstra Pro + Elementor Pro\$59 + \$59 / yr (affiliate)
Product imagesCloudinary free tier\$0
PaymentsStripe Checkout (serverless)2.9 % per txn (same as before)

(Affiliate honesty: Astra & Elementor links pay me ~30-50 % if you upgrade—at no extra cost to you.)


3. Step 1 – Bulletproof the Back-End (but Keep It Tiny)

3.1 Lock It Down in 5 Minutes

  • Install WP GraphQL + WooGraphQL (free).
  • Disable REST API endpoints we don’t need (wp-json/wc/v3/) via functions.php.
  • Add Sucuri WAF (free tier) to block brute-force noise. (Affiliate link: Sucuri Pro earns me 20 % recurring.)

3.2 Strip the Front-End Fat

  • Dequeue every theme stylesheet—Next.js will style everything.
  • Use Kinsta’s Edge Cache (built-in) so GraphQL responses are 200 ms, not 2 s.

4. Step 2 – Scaffold the Static Storefront

npx create-next-app@latest bloom-headless --typescript --tailwind --eslint --app --src-dir
cd bloom-headless
npm install @apollo/client graphql stripe

Create .env.local:

WP_GRAPHQL_ENDPOINT=https://admin.bloomcoffee.co/graphql
NEXT_PUBLIC_STRIPE_PK=pk_live_xxxxxxxx
STRIPE_SK=sk_live_xxxxxxxx

lib/woocommerce.ts

import { ApolloClient, InMemoryCache } from '@apollo/client';

export const wooClient = new ApolloClient({
  uri: process.env.WP_GRAPHQL_ENDPOINT,
  cache: new InMemoryCache({
    typePolicies: {
      Product: { keyFields: ['id'] },
    },
  }),
});

5. Step 3 – Build Product & Cart Pages (Copy-Paste Ready)

app/products/[slug]/page.tsx

import { wooClient } from '@/lib/woocommerce';
import { gql } from '@apollo/client';
import AddToCart from '@/components/AddToCart';

async function getProduct(slug: string) {
  const { data } = await wooClient.query({
    query: gql`
      query GetProduct($slug: ID!) {
        product(id: $slug, idType: SLUG) {
          id
          name
          description
          price(format: RAW)
          images {
            nodes {
              sourceUrl
            }
          }
        }
      }
    `,
    variables: { slug },
  });
  return data.product;
}

export default async function ProductPage({ params }: { params: { slug: string } }) {
  const product = await getProduct(params.slug);
  return (
    <main className="max-w-6xl mx-auto p-8">
      <h1 className="text-4xl font-bold">{product.name}</h1>
      <img src={product.images.nodes[0]?.sourceUrl} className="w-1/3 rounded" />
      <div dangerouslySetInnerHTML={{ __html: product.description }} />
      <p className="text-2xl font-semibold mt-4">${product.price}</p>
      <AddToCart productId={product.id} />
    </main>
  );
}

components/AddToCart.tsx

'use client';
import { loadStripe } from '@stripe/stripe-js';

const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_PK!);

export default function AddToCart({ productId }: { productId: string }) {
  const handleClick = async () => {
    const stripe = await stripePromise;
    const { sessionId } = await fetch('/api/checkout', {
      method: 'POST',
      body: JSON.stringify({ productId }),
    }).then((r) => r.json());
    stripe?.redirectToCheckout({ sessionId });
  };

  return (
    <button
      onClick={handleClick}
      className="mt-4 bg-amber-600 text-white px-6 py-3 rounded hover:bg-amber-700"
    >
      Buy Now
    </button>
  );
}

Serverless checkout API route lives inside app/api/checkout/route.ts.


6. Step 4 – Styling That Converts

  • Import Astra Pro starter “Coffee Shop” → rip out CSS variables → feed into Tailwind.
  • Elementor Pro for mega-menu & footer blocks exported as JSON → instant consistency.
    (Links again: Astra Pro 30 % off, Elementor Pro 50 % commission.)

7. Step 5 – Deploy to Netlify (One Command)

git init && git remote add origin git@github.com:bloomco/bloom-headless
netlify deploy --prod --dir=.next

Netlify settings:

  • Build: npm run build
  • Publish: .next
  • Environment variables pasted from .env.local.

8. Step 6 – Incremental Builds for Inventory Updates

Bloom adds new roasts daily. We don’t want to rebuild the whole site every time.


9. Real Numbers After 30 Days

MetricOld AWS EC2New Stack
Hosting\$320/mo\$7 Kinsta + \$0 Netlify
Average TTFB1.9 s76 ms
Lighthouse6899
Cart Abandonment73 %58 %
Build time (2,000 products)N/A90 s

10. When You Outgrow the Free Tier

HostPlanWhy You’d Upgrade
KinstaApplication \$14/moDockerized Node, autoscale workers
WP EngineHeadless \$20/moAtlas CDN, built-in preview URLs

Affiliate links:



11. The “I Need This Yesterday” Escape Hatch

Headless WooCommerce looks simple on paper, but Stripe webhooks, stock sync, and preview modes can eat weekends.
If you’d rather sell coffee than wrestle GraphQL:

📧 Email arnab.hstu@gmail.com with the subject “HEADLESS ECOMMERCE” and I’ll send a fixed-price quote or hop on a 15-min call.


12. TL;DR Cheatsheet

  • WooCommerce back-end on Kinsta $7/mo
  • Static storefront on Netlify $0
  • Astra Pro + Elementor Pro for styling (affiliate)
  • Stripe serverless checkout
  • Auto-rebuild on every inventory change

Now go forth and caffeinate the internet—without caffeinating your AWS bill.

Leave a Reply

Your email address will not be published. Required fields are marked *