Skip to main content
Episode 10 of 24Storefront50 min

Build a shopping cart that trusts the server, not the browser

Add a cart with quantities and stock checks that saves for guests, merges into accounts later, and always takes prices from the database.

You will have: A working cart (add, change quantity, remove, see totals) stored in your database, tied to a secure cookie for guests, where prices and stock are always checked on the server.

Before you start

  • Episode 9 is merged: product pages show real images
  • Local Postgres is running and the seed has products with variants and stock
  • Decide your rules in plain words: the most of one item a customer may buy at once, and whether the cart shows shipping or tax now (the simple answer for now is no; checkout in Episode 12 adds them)
  • You are on the main branch with a clean working tree

Make it yours

Change the example (fashion store) to your own. Your answers are saved in this browser and used in every episode.

Other series keywords (5)

Your prompt

Paste into: {{ai_agent}} opened in your project folder.
You are a senior full-stack developer working inside my Next.js (App Router), TypeScript, Tailwind and Drizzle project for "Thread & Needle", a fashion e-commerce store selling women's and men's clothing, shoes and accessories to customers in United States, charging in USD. I am not a programmer, so explain technical words briefly.

Goal: build the shopping cart. The key principle is that the browser may only say which item and how many. The server decides the price, the name, the image and whether there is stock. A visitor must never be able to change a price by editing anything in their browser.

Plan first: show me your plan and wait for my OK before changing anything. Include the new tables, how a guest cart is remembered, and how the cart will later merge into a customer account after login in Episode 11.

Tasks, after I approve:
1. Create a branch named feature/cart.
2. Database (drizzle-kit migration): carts (id, optional user id that stays empty for now, hashed guest token, created, updated, expiry) and cart items (cart id, variant id, quantity, a unique pair of cart and variant). Do not store prices in cart items. Prices are read from the products tables every time.
3. Guest identity: when a guest first adds an item, create a cart and set a random, unguessable token in a cookie that is httpOnly, sameSite lax, secure in production, with a limited lifetime. Store only a hash of the token in the database.
4. Server actions or route handlers, each validating input with Zod: add item, set quantity, remove item, clear cart. Quantity must be a whole number from 1 to a sensible maximum per line (use 10 unless I say otherwise). Adding the same variant twice increases its quantity.
5. Stock rules: never allow a quantity above the stock available. If stock has dropped since the item was added, lower the quantity or flag the line, and tell the customer in plain words. Ignore draft products and unknown variants.
6. A single function that loads the cart for display: it returns lines with current name, image, price and stock, plus the subtotal in whole minor units, calculated only on the server. Use it everywhere, so the page and the checkout in Episode 12 share it.
7. A function mergeGuestCartIntoUserCart that Episode 11 will call after login. Write and test it now: quantities add up, stock limits still apply, and the guest cart is removed afterwards.
8. Interface: an Add to cart button on the product page (enable the button from Episode 8, requiring a chosen variant), a cart page, a small cart count in the header, a friendly empty cart, and a mini cart panel that opens after adding. Everything must work with the keyboard and a screen reader, and show clear error messages.
9. A cleanup script that deletes expired carts, which I can run by hand for now.

Security rules:
- Never accept a price, name or total from the browser.
- Identify the cart only by the cookie, never by an id in the address, so nobody can open another person's cart.
- Do not log cookies or tokens.

Verify by running yourself and showing the results: type check, lint, production build, and automated tests for: adding, changing and removing items; the quantity ceiling; the stock limit; a tampered request with a fake price (price ignored); a request for another cart (denied); and the merge function.

Commit in small steps, push and open a pull request.

Report back in this format:
1. What you did.
2. What I should check myself.
3. Anything that needs my decision.

Review the prompt, then copy it. Never paste real passwords or API keys into a chat.

How to check it worked

  • On a product page, choose a size and click Add to cart: the header count changes and the mini cart shows the item with the right price
  • On the cart page, change a quantity and remove an item: the subtotal updates each time
  • Try to set a quantity higher than the stock you seeded: the cart refuses or lowers it and explains why
  • Close the browser tab, open the site again: the cart is still there. Open the site in a private window: that cart is empty
  • The agent's test results include the fake-price test and the other-cart test, both passing
  • In the browser's developer tools, look at the cookies: the cart cookie is marked HttpOnly and its value is a long random string

Sample result

Example: fashion store

Example cart for the fashion store. Prices come from the database; the agent's tests prove a tampered price is ignored.

If something goes wrong

The cart empties itself when you refresh or restart the dev server.
Ask the agent to confirm the cart is saved in the database and that the cookie is being set. On a local address the Secure flag must be off, and on the live site it must be on.
The header count and the cart page disagree.
Both should call the same server function. Ask: "Make the header count and the cart page use one shared function, and add a test that proves they match."
Two people buy the last item at nearly the same time.
The cart only checks stock; it does not reserve it. The final check happens at checkout in Episode 12, where the agent will lock the stock inside a database transaction. For now, make sure the cart message is honest and says stock is not held until you pay.
The agent suggests keeping the cart in the browser's local storage to save time.
Decline. Browser storage can be edited by the visitor, cannot be shared with your account, and invites tampering. The database cart is the plan.

More in Storefront

This is part of the series Build a secure, no-cost e-commerce website with AI agents. See all 24 episodes, the tools used and answers to common questions on the series page.

We use optional cookies for analytics and referral tracking. Learn more about our privacy policy