codexmachina
App schema

E-commerce store, built on every verified stack.

E-commerce storefront: product catalog with per-SKU variants, guest-compatible carts, and price-snapshotting orders.

40 verified stacks40 greenLast updated 2026-08-23How we verify →

40 verified stacks

Showing 12 across 1 app types. Every stack page links its neighbours along each axis, so any combination is a click or two from here.

Next.js 16 (App Router) + MySQL 8Auth.js (NextAuth) · Resend
Next.js 16 (App Router) + MySQL 8Better Auth
Next.js 16 (App Router) + MySQL 8Clerk
Next.js 16 (App Router) + Postgres (Neon)Auth.js (NextAuth)
Nuxt 4 + MySQL 8Better Auth
Nuxt 4 + Postgres (Neon)Better Auth
Nuxt 4 + Postgres (Neon)Clerk
Nuxt 4 + Postgres (Neon)Supabase Auth · Resend
React Router v8 + MySQL 8Clerk
React Router v8 + MySQL 8Supabase Auth · Resend
React Router v8 + Postgres (Neon)Better Auth
React Router v8 + Postgres (Neon)Supabase Auth

What E-commerce store gives you

Price and stock do not live on a product here. products holds the display unit — a unique slug (indexed as idx_product_slug), a name, a description — while product_variants holds what a shopper can actually buy: a unique sku, price_cents, inventory_qty, one row per SKU under its product. Everything downstream points at a variant rather than a product, which is why 'Large / Black is sold out but Medium is not' is representable without a nullable stock column or a JSON blob. Carts are two tables and one constraint.

carts.user_id is text (matching Better Auth's user.id) and nullable, so a guest can fill a basket before an account exists to attach it to; claiming that basket at login is a single UPDATE on carts, because cart_items hangs off cart_id and not off the shopper. cart_items then carries the composite unique (cart_id, variant_id), which turns 'add to cart' into an upsert that bumps quantity instead of an insert that quietly leaves the same SKU on two checkout lines. Orders exist in order to stop being the catalog. order_items does not read price through its variant FK: it stores sku and unit_price_cents copied at checkout, and variant_id is nullable with ON DELETE SET NULL.

Reprice a variant, rename a SKU, discontinue a line — last quarter's orders still total what the customer was charged, and the FK is a convenience for 'show me this product's sales' rather than the source of the money. orders.total_cents is the captured total; provider_payment_id is an opaque Stripe-or-whoever string, deliberately not a foreign key into a payments table this schema does not own; orders.user_id is ON DELETE SET NULL so a closed account does not erase its own sales history. All money is integer cents, never numeric or float, and orders.status is text under orders_status_check (pending, paid, shipped, cancelled) with idx_order_status behind it.

What the schema does not do is reserve inventory: inventory_qty is a plain column with no hold, no reservation table and nothing stopping it going negative, so overselling under concurrency is your checkout transaction's problem.

Built for

Render a product page from its URL slug

products.slug is unique and carries idx_product_slug for the lookup; idx_variant_product on product_variants.product_id then returns every buyable SKU with its price_cents and inventory_qty in one scan.

Add to cart without duplicating a line

The composite unique cart_items_cart_variant_unique on (cart_id, variant_id) is the conflict target: re-adding a SKU bumps quantity on the row that already exists rather than leaving two lines for one variant.

Hand a guest's basket to the account they just created

carts.user_id is nullable and indexed by idx_cart_user, and cart_items references cart_id rather than the shopper — so claiming a guest cart is one UPDATE against a single carts row, with the items following for free.

The fulfilment queue: paid but not yet shipped

orders.status is text under orders_status_check with idx_order_status over it, so the warehouse view is an index scan and adding a 'refunded' state is a constraint change instead of an ALTER TYPE.

What did this order cost, two years later

idx_order_item_order on order_items.order_id returns the lines, each holding its own frozen sku and unit_price_cents; variant_id is ON DELETE SET NULL, so retiring a SKU cannot rewrite the receipt.

Why E-commerce store, specifically

note

Money is stored as integer cents on product_variants (price_cents) and snapshotted onto order_items (unit_price_cents) at checkout — repricing or deleting a variant never rewrites order history.

note

cart_items carries a composite unique on (cart_id, variant_id) so the app bumps quantity rather than inserting duplicate rows; orders.status is a text + CHECK column (pending/paid/shipped/cancelled) to avoid ALTER TYPE migrations.