Overview
A production-style storefront built on Next.js 16 (App Router) and React 19. Products, variants, sale prices and collections come from the Shopify Storefront API (Shopify's public mock.shop), payments run through Stripe Checkout, and every page is served behind a strict, nonce-based Content Security Policy.
Technical Challenges
The Catalog Is an Untrusted External API
Products are fetched server-side over the Storefront GraphQL API, cached for five minutes, and validated with Zod at the fetch boundary. Each product is validated on its own, so one malformed record is dropped instead of taking the store down. Money strings are converted to integer cents without floating-point rounding, and images are only accepted from Shopify's CDN. Because mock.shop speaks the real Storefront schema, moving to a live Shopify store is a configuration change.
Server-Side Pricing with Stripe Checkout
The browser only ever sends variant IDs and quantities. A Server Action re-prices the cart from the catalog, rejects sold-out items and creates a Stripe Checkout Session with inline price_data, so Shopify stays the single source of truth and nothing has to be synced into Stripe. Card details are entered on Stripe's hosted page, which keeps them off the server entirely. A webhook verifies Stripe's signature against the raw request body before an order counts as paid, and live keys are refused while the catalog is mock data.
Cart State Without a Backend
The cart lives in Redux Toolkit and is persisted to localStorage, keyed by variant ID. Since anyone can edit localStorage, a saved cart is schema-validated on load: unknown variants are dropped, quantities are clamped and stored prices are ignored. Tabs stay in sync through the storage event. The store is created per provider rather than as a module singleton, which would otherwise leak one shopper's cart into another shopper's server render.
Search Without an LLM
Search is keyword search over the cached catalog: text is split on punctuation (so "USB-C" matches "usb"), plurals and "-ing" forms are folded, every word must match, and title matches outrank collection, option and description matches. It drives as-you-type suggestions in an accessible combobox and a results page with filters and sorting, and on this catalog it outperforms the Storefront API's own search, which returns nothing for "usb hub" or "charger".
Security Hardening
A fresh CSP nonce is generated per request in Next.js Proxy, alongside HSTS, framing, referrer and permissions headers. The strict policy blocks next/image's inline positioning styles, so the same rules are applied from the stylesheet instead of weakening the policy with 'unsafe-inline'. A custom image loader hands resizing to Shopify's CDN, which leaves Next's image optimizer, and its server-side fetching, unused. CI runs lint, type checks, Vitest and React Testing Library tests, a production build and npm audit on every pull request.
System architecture
Every page is rendered per request behind a nonce-based CSP. Product data comes from the Shopify Storefront API through a Zod-validated, cached catalog layer, and images load straight from Shopify's CDN. The cart lives in the browser and sends only variant IDs and quantities: a Server Action re-prices them from the catalog before creating a Stripe Checkout Session, and a signature-verified webhook confirms payment.



