0
0
GraphqlHow-ToBeginner · 4 min read

How to Implement Shopping Cart with GraphQL: Simple Guide

To implement a shopping cart in GraphQL, define a schema with types for CartItem and Cart, and create queries to fetch cart data plus mutations to add, update, or remove items. Use resolvers to handle the logic and store cart state, often in memory or a database.
📐

Syntax

A shopping cart GraphQL implementation typically includes:

  • Types: Define CartItem and Cart types to represent items and the cart.
  • Queries: Fetch the current cart and its items.
  • Mutations: Add, update quantity, or remove items from the cart.
  • Resolvers: Functions that process queries and mutations, managing cart data.
graphql
type CartItem {
  id: ID!
  productId: ID!
  name: String!
  quantity: Int!
  price: Float!
}

type Cart {
  items: [CartItem!]!
  total: Float!
}

type Query {
  getCart: Cart!
}

type Mutation {
  addItem(productId: ID!, quantity: Int!): Cart!
  updateItem(productId: ID!, quantity: Int!): Cart!
  removeItem(productId: ID!): Cart!
}
💻

Example

This example shows a simple in-memory shopping cart with GraphQL schema, resolvers, and server setup using Apollo Server.

javascript
const { ApolloServer, gql } = require('apollo-server');

// Schema definition
const typeDefs = gql`
  type CartItem {
    id: ID!
    productId: ID!
    name: String!
    quantity: Int!
    price: Float!
  }

  type Cart {
    items: [CartItem!]!
    total: Float!
  }

  type Query {
    getCart: Cart!
  }

  type Mutation {
    addItem(productId: ID!, quantity: Int!): Cart!
    updateItem(productId: ID!, quantity: Int!): Cart!
    removeItem(productId: ID!): Cart!
  }
`;

// Sample product data
const products = [
  { id: '1', name: 'T-shirt', price: 20.0 },
  { id: '2', name: 'Jeans', price: 40.0 },
  { id: '3', name: 'Hat', price: 15.0 },
];

// In-memory cart
let cartItems = [];

// Helper to calculate total
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Resolvers
const resolvers = {
  Query: {
    getCart: () => ({ items: cartItems, total: calculateTotal(cartItems) }),
  },
  Mutation: {
    addItem: (_, { productId, quantity }) => {
      const product = products.find(p => p.id === productId);
      if (!product) throw new Error('Product not found');
      const existing = cartItems.find(item => item.productId === productId);
      if (existing) {
        existing.quantity += quantity;
      } else {
        cartItems.push({
          id: String(cartItems.length + 1),
          productId,
          name: product.name,
          quantity,
          price: product.price,
        });
      }
      return { items: cartItems, total: calculateTotal(cartItems) };
    },
    updateItem: (_, { productId, quantity }) => {
      const item = cartItems.find(i => i.productId === productId);
      if (!item) throw new Error('Item not in cart');
      if (quantity <= 0) {
        cartItems = cartItems.filter(i => i.productId !== productId);
      } else {
        item.quantity = quantity;
      }
      return { items: cartItems, total: calculateTotal(cartItems) };
    },
    removeItem: (_, { productId }) => {
      cartItems = cartItems.filter(i => i.productId !== productId);
      return { items: cartItems, total: calculateTotal(cartItems) };
    },
  },
};

// Apollo Server setup
const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`Server ready at ${url}`);
});
Output
Server ready at http://localhost:4000/
⚠️

Common Pitfalls

Common mistakes when implementing a shopping cart with GraphQL include:

  • Not validating product existence before adding to cart, causing errors.
  • Allowing negative or zero quantities without removing items properly.
  • Not recalculating the cart total after mutations.
  • Storing cart state only in memory without persistence, losing data on server restart.

Always validate inputs and handle edge cases in resolvers.

javascript
/* Wrong: Adding item without checking product */
addItem: (_, { productId, quantity }) => {
  // Missing product validation
  cartItems.push({ productId, quantity });
  return { items: cartItems, total: 0 };
}

/* Right: Validate product and calculate total */
addItem: (_, { productId, quantity }) => {
  const product = products.find(p => p.id === productId);
  if (!product) throw new Error('Product not found');
  // Add or update item logic here
  // Calculate total
  return { items: cartItems, total: calculateTotal(cartItems) };
}
📊

Quick Reference

OperationGraphQL TypeDescription
Fetch CartQuery getCartReturns current cart items and total price
Add ItemMutation addItem(productId, quantity)Adds product to cart or increases quantity
Update ItemMutation updateItem(productId, quantity)Changes quantity or removes if zero
Remove ItemMutation removeItem(productId)Removes product from cart

Key Takeaways

Define clear GraphQL types for cart items and the cart itself.
Use queries to fetch cart data and mutations to modify it.
Validate product existence and input quantities in mutations.
Keep cart state consistent and recalculate totals after changes.
Consider persistence beyond in-memory storage for real apps.