Jamie Barton
27 AUG 2020

Manage Shopping Carts with GraphQL

I built CartQL to scratch my own itch.

I wanted a shopping cart API that I could use anywhere. On personal projects, side hustles, landing pages — without needing to bring my own inventory system. I wanted to sell things I sourced from other APIs, markdown files, CMSs, existing commerce platforms, even spreadsheets. And I didn’t want to rewire cart logic every time I built something new.

So I built CartQL, a flexible GraphQL shopping cart API that plugs into any stack, doesn’t assume where your products come from, and handles carts, items, and checkout in a predictable way.


Here’s how it works:

  • Built for the Jamstack — Use fetch, Apollo, URQL, or whatever GraphQL client you already have.
  • No replatforming — Works with your existing frontend, backend, and data sources.
  • Flexible cart items — Use the metadata field to store custom item data like variants, notes, or shipping info.
  • Bring your own inventory — Use files, APIs, CMS, or anything else as your product source.
  • Composable — Stitch it into other GraphQL APIs, or just call it directly.

Manage cart items, checkout and pay for orders with a simple declarative GraphQL API. The API is completely open and free to use.

Getting Started

Use this endpoint: https://api.cartql.com

Fetch a new or existing Cart

query {
  cart(id: "ck5r8d5b500003f5o2aif0v2b", currency: { code: GBP }) {
    ...CartWithItems
  }
}

fragment CartWithItems on Cart {
  ...CartInfo
  items {
    ...ItemInfo
  }
}

fragment CartInfo on Cart {
  id
  email
  isEmpty
  abandoned
  totalItems
  totalUniqueItems
  currency {
    code
    symbol
  }
  subTotal {
    amount
    formatted
  }
  shippingTotal {
    amount
    formatted
  }
  taxTotal {
    amount
    formatted
  }
  grandTotal {
    amount
    formatted
  }
  metadata
  notes
  createdAt
  updatedAt
}

fragment ItemInfo on CartItem {
  id
  name
  description
  images
  quantity
  metadata
  unitTotal {
    amount
    formatted
  }
  lineTotal {
    amount
    formatted
  }
  createdAt
  updatedAt
}

Add to Cart

mutation {
  addItem(
    input: {
      cartId: "ck5r8d5b500003f5o2aif0v2b"
      id: "5e3293a3462051"
      name: "Full Logo Tee"
      description: "Purple Triblend / L"
      images: ["full-logo-tee.png"]
      price: 2000
    }
  ) {
    id
    totalItems
    subTotal {
      formatted
    }
  }
}

Update Cart Item

mutation {
  updateItem(
    input: {
      cartId: "ck5r8d5b500003f5o2aif0v2b"
      id: "5e3293a3462051"
      price: 2500
      quantity: 2
    }
  ) {
    id
    totalItems
    subTotal {
      formatted
    }
  }
}

Remove Cart Item

mutation {
  removeItem(
    input: { cartId: "ck5r8d5b500003f5o2aif0v2b", id: "5e3293a3462051" }
  ) {
    id
    totalItems
    subTotal {
      formatted
    }
  }
}

Custom metadata

mutation {
  updateItem(
    input: {
      cartId: "ck5r8d5b500003f5o2aif0v2b",
      id: "5e3293a3462051",
      metadata: {
        "engraving": "Jamie"
      }
    }
  ) {
    id
    metadata
  }
}

Currency Formatting

mutation {
  updateCart(
    input: { id: "ck5r8d5b500003f5o2aif0v2b", currency: { code: GBP } }
  ) {
    id
    currency {
      code
      symbol
      thousandsSeparator
      decimalSeparator
      decimalDigits
    }
  }
}

Checkout

mutation {
  checkout(
    input: {
      cartId: "ck5r8d5b500003f5o2aif0v2b"
      email: "jamie@cartql.com"
      shipping: {
        name: "Jamie Barton"
        line1: "123 Cart Lane"
        city: "Newcastle upon Tyne"
        postalCode: "NE14 CQL"
        country: "England"
      }
    }
  ) {
    id
    grandTotal {
      formatted
    }
  }
}

Checkout with Stripe

const Stripe = require("stripe");
const { request, gql } = require("graphql-request");

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

const query = gql`
  query getCart($cartId: ID!) {
    cart(id: $cartId) {
      items {
        name
        description
        unitTotal {
          amount
          currency {
            code
          }
        }
        quantity
      }
    }
  }
`;

async function handler(event) {
  const { cartId } = JSON.parse(event.body);
  const { cart } = await request("https://api.cartql.com", query, {
    cartId,
  });

  const session = await stripe.checkout.sessions.create({
    mode: "payment",
    success_url: `${process.env.URL}/thankyou`,
    cancel_url: `${process.env.URL}/cart`,
    line_items: cart.items.map(
      ({ name, description, unitTotal, quantity }) => ({
        price_data: {
          currency: unitTotal.currency.code,
          unit_amount: unitTotal.amount,
          product_data: {
            name,
            description,
          },
        },
        quantity,
      }),
    ),
  });

  return {
    statusCode: 201,
    body: JSON.stringify(session),
  };
}

There are even more mutations:

  • setItems
  • incrementItemQuantity
  • decrementItemQuantity
  • emptyCart
  • deleteCart

Check out the gatsby-cartql-starter repo to see it in action.

If you ever wanted to sell something online without needing a whole commerce platform — CartQL might scratch that itch too.