CartQL is a GraphQL Shopping Cart API designed to work with your existing frontend or backend.
Built for the Jamstack - No more writing custom business logic to handle cart and checkout. Works with Apollo Client, URQL, fetch, and more.
Flexible cart items - Store any type of data on cart and cart items with the CartQL Mutations API
No replatforming - CartQL was built to handle custom cart items for SKUs, promotions, shipping, and so much more.
Bring your own inventory - Whether you're storing products in the filesystem, or in another API, such as CMS, it works with CartQL.
Bring your own frontend, or backend - No more learning new client-side libraries for each framework to manage cart state. Work with what you already use, or stitch it with other GraphQL APIs.
The API is completely open, and free to use. See some of the common mutations users use when building their commerce experience.
To get stated, use this endpoint:
https://api.cartql.com
Run the following query, or modify it with your own id
, or currency code such as EUR
:
query {cart(id: "ck5r8d5b500003f5o2aif0v2b", currency: { code: GBP }) {...CartWithItems}}fragment CartWithItems on Cart {...CartInfoitems {...ItemInfo}}fragment CartInfo on Cart {idisEmptyabandonedtotalItemstotalUniqueItemscurrency {codesymbol}subTotal {amountformatted}shippingTotal {amountformatted}taxTotal {amountformatted}grandTotal {amountformatted}metadatanotescreatedAtupdatedAt}fragment ItemInfo on CartItem {idnamedescriptionimagesquantitymetadataunitTotal {amountformatted}lineTotal {amountformatted}createdAtupdatedAt}
Items by default are of type SKU
, but you can add SHIPPING
, and TAX
, which modify the subTotal
, grandTotal
values.
mutation {addItem(input: {cartId: "ck5r8d5b500003f5o2aif0v2b"id: "5e3293a3462051"name: "Full Logo Tee"description: "Purple Triblend / L"images: ["full-logo-tee.png"]price: 2000}) {idisEmptyabandonedtotalItemstotalUniqueItemssubTotal {formatted}}}
Update any of the item properties using the updateItem
GraphQL mutation.
mutation {updateItem(input: {cartId: "ck5r8d5b500003f5o2aif0v2b"id: "5e3293a3462051"price: 2500quantity: 2}) {idisEmptyabandonedtotalItemstotalUniqueItemssubTotal {formatted}}}
No longer need a cart item? You can remove it easily.
mutation {removeItem(input: { cartId: "ck5r8d5b500003f5o2aif0v2b", id: "5e3293a3462051" }) {idisEmptyabandonedtotalItemstotalUniqueItemssubTotal {formatted}}}
metadata
Some items have more than just a name
, description
, and price
, but attributes such as engraving, personalized notes, and more.
You can use the metadata
object to store custom data about items, and the cart itself.
mutation {updateItem(input: {cartId: "ck5r8d5b500003f5o2aif0v2b",id: "5e3293a3462051",metadata: {"engraving": "Jamie"}}) {idmetadata}}
No matter the location of your users, you can format the cart based on their currency.
The updateCart
GraphQL mutation accepts properties for currency
, which include things such as the code
, symbol
, thousandsSeparator
, and more.
CartQL will automatically figure out the symbol, separator, and more, based on the code
you give it.
Try swapping out GBP
in the example below with EUR
, TRY
, or USD
, and see all money meta in the cart change.
mutation {updateCart(input: { id: "ck5r8d5b500003f5o2aif0v2b", currency: { code: GBP } }) {idcurrency {codesymbolthousandsSeparatordecimalSeparatordecimalDigits}}}
Are you ready to checkout your cart?
CartQL provides a checkout
mutation that you can use on the frontend to help capture customer addresses, emails, and notes.
Quite often you will want to control the checkout flow, and the checkout
mutation lets you capture the shipping, and billing address of your customer, and returns an immutable cart as an "Order".
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"}billing: {# Optionalname: "Jamie Barton"line1: "123 Cart Lane"city: "Newcastle upon Tyne"postalCode: "NE14 CQL"country: "England"}}) {idgrandTotal {formatted}}}
Instead of building your own checkout, you can opt to use something like Stripe Checkout.
Together with serverless functions, you can fetch items from your cart, and create a Stripe checkout session.
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) {idisEmptyitems {idnamedescriptionunitTotal {amountcurrency {code}}quantity}}}`;exports.handler = async function (event) {const { cartId } = JSON.parse(event.body);const {cart: { isEmpty, items },} = await request(process.env.GATSBY_GRAPHQL_ENDPOINT, query, {cartId,});if (isEmpty) {return {statusCode: 400,body: JSON.stringify({ message: "The cart is empty." }),};}try {const session = await stripe.checkout.sessions.create({mode: "payment",payment_method_types: ["card"],success_url: `${process.env.URL}/thankyou`,cancel_url: `${process.env.URL}/cart`,line_items: items.map(({name,description,unitTotal: {amount: unit_amount,currency: { code: currency },},quantity,}) => ({...(description && { description }),price_data: {currency,unit_amount,product_data: {name,...(description && { description }),},},quantity,})),});return {statusCode: 201,body: JSON.stringify(session),};} catch ({ message }) {return {statusCode: 401,body: JSON.stringify({ message }),};}};
There's mutations to set all items in the cart with setItems
, increment, or decrement just the quantity of items with incrementItemQuantity
, and decrementItemQuantity
. You can also empty the cart with emptyCart
, or delete it with deleteCart
.
The gatsby-cartql-starter is a great place to get going! Built with Apollo Client 3, and a filesystem based inventory. 🚀