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:
metadata
field to store custom item data like variants, notes, or shipping info.Manage cart items, checkout and pay for orders with a simple declarative GraphQL API. The API is completely open and free to use.
Use this endpoint: https://api.cartql.com
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
}
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
}
}
}
mutation {
updateItem(
input: {
cartId: "ck5r8d5b500003f5o2aif0v2b"
id: "5e3293a3462051"
price: 2500
quantity: 2
}
) {
id
totalItems
subTotal {
formatted
}
}
}
mutation {
removeItem(
input: { cartId: "ck5r8d5b500003f5o2aif0v2b", id: "5e3293a3462051" }
) {
id
totalItems
subTotal {
formatted
}
}
}
metadata
mutation {
updateItem(
input: {
cartId: "ck5r8d5b500003f5o2aif0v2b",
id: "5e3293a3462051",
metadata: {
"engraving": "Jamie"
}
}
) {
id
metadata
}
}
mutation {
updateCart(
input: { id: "ck5r8d5b500003f5o2aif0v2b", currency: { code: GBP } }
) {
id
currency {
code
symbol
thousandsSeparator
decimalSeparator
decimalDigits
}
}
}
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
}
}
}
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.