Adding Cart Line Items
Adding Cart Line Items
Welcome back! 👋 Now that we can create carts and fetch them in a stable, predictable shape, it’s time to make a cart useful by adding items to it.
In this lesson you’ll follow the full “Add to cart” flow end-to-end: the API route parses and validates the request, the service layer enforces business rules (like inventory limits), and the repository performs an add-or-increment write safely inside a transaction. This is the exact behavior you see in real e-commerce backends when a user clicks “Add to cart” multiple times.
Previously…
In the last lesson, we treated the cart as a first-class backend resource: you created carts and fetched them back fully “hydrated” with items and computed totals. That stable response shape is what makes today’s work clean—once we add cart_items rows, GET /api/carts/:id automatically becomes meaningful because totals can now be computed from those line items.
What a “line item” represents
A cart line item is one row in cart_items. It captures:
- Which product was added (
product_id) - How many (
quantity) - What price was used when it was added (
unit_price_cents)
That last field is a snapshot: it preserves stability if product prices change later. Our totals logic (from the previous lesson) uses this snapshot when computing subtotal and tax.
The API endpoint for adding items
The main entry point for “Add to cart” is:
POST /api/carts/:id/items
This is implemented in src/app/api/carts/[id]/items/route.ts. The handler is deliberately strict: it validates the cart ID, safely parses JSON, validates the body shape, then delegates to the service layer and converts the result into a consistent HTTP response.
Route setup and imports:
This first part shows what the route depends on and how Next.js provides dynamic route params in this codebase.
successanderrorare the project’s response helpers, so every route returns a consistent envelope instead of hand-rollingNextResponseformatting in each file.parseJson(req)is used instead ofreq.json()directly so the route can handle malformed JSON without crashing. That keeps error handling consistent and prevents “unexpected 500s” for simple client mistakes.RouteContextis important: in this project,context.paramsis a Promise. That’s why the handler doesconst { id } = await context.paramsinstead of accessingparamssynchronously.
