Evolve Performance Storefront
Headless Shopify storefront for a weightlifting equipment brand.
- Role
- Designer & developer
- Client
- Evolve Performance

In short
- 01
Express API proxies Shopify's Storefront API; the access token never reaches the browser
- 02
Cart and checkout live in Shopify; the app keeps no local commerce state
- 03
Red-on-black cinematic UI with a ~2.5s logo intro that auto-skips for reduced motion
Built with TypeScript · Node.js · pnpm workspaces · React · Express 5 · PostgreSQL · Drizzle ORM · Shopify Storefront API (GraphQL) · Orval · Zod
Problem
Evolve Performance is a weightlifting equipment brand selling anime- and game-inspired lifting straps, with wrist wraps as the store's second category. The owner is non-technical and runs the business from Shopify Admin: products, inventory, orders, discounts and shipping live there, and Shopify's hosted checkout handles payment, shipping and taxes.
The goal was a custom storefront with its own cinematic design and full control over the shopping experience, with Shopify kept as the system of record. The codebase had earlier carried its own commerce backend (a custom admin portal, Stripe and ShipStation integrations, a Resend email pipeline and Clerk user accounts); those were retired, and the storefront now holds no local commerce state.
Approach
I built it as a headless Shopify storefront: a custom frontend reads Shopify data server-side through an Express API. The repository is a pnpm workspace with three artifacts: artifacts/storefront (the customer site at /), artifacts/api-server (the Express API under /api) and artifacts/admin, now a static notice page pointing the owner to Shopify Admin.
Shopping is a three-step flow: the home page shows two animated category panels (Lifting Straps and Wrist Wraps), /category/:slug lists that category's designs, and /products/:handle shows product detail with size variants. An all-gear shop, collections, cart and support pages sit alongside. The theme is dark red-on-black with scanlines and glitch hover effects, opened by a roughly 2.5-second logo intro on each full page load.
Architecture
Browser (artifacts/storefront; API hooks from lib/api-client-react, generated by Orval)
│ /api/shop/* /api/support /api/content
▼
Express 5 API (artifacts/api-server)
├─ routes/shop.ts ──► lib/shopifyStorefrontClient.ts ──► Shopify Storefront API (GraphQL)
│ products, collections, cart; money converted to integer cents
│ checkout = redirect to cart.checkoutUrl (Shopify hosted checkout)
└─ /support, /content ──► PostgreSQL via Drizzle ORM
site_settings, homepage_sections, website_pages, support_requests
shopifyStorefrontClient.ts is the only module that talks to Shopify. It takes the store domain and Storefront access token from environment variables, pins the API version, and fails loudly with a 503 when unconfigured.
The cart is Shopify's: the storefront keeps the cart id in localStorage (ep_shopify_cart_id), cart mutations are RPC-style POSTs, and checkout redirects to cart.checkoutUrl. PostgreSQL holds website content and support requests only. The API contract is an OpenAPI spec (lib/api-spec/openapi.yaml) from which Orval generates the lib/api-client-react hooks and the lib/api-zod schemas.
Technical decisions
- Shopify token stays server-side → the browser never sees
SHOPIFY_STOREFRONT_ACCESS_TOKEN; the storefront calls/api/shop/*only. - No local commerce state → Shopify is the system of record; no Stripe, ShipStation, email pipeline or user accounts.
- Money is integer cents end-to-end in the API (
priceInCents), converted server-side from Shopify's decimal amounts. - Old commerce tables were kept → non-destructive migration; Stripe/ShipStation columns and marketing tables were removed from the Drizzle schema only, not dropped.
- FAQ and returns copy live in
store-pages.ts→ deployed code does not depend on stale seeded copy. - Categorisation reads Shopify Product type first, then the taxonomy Category field; untyped products fall back to name matching, then Wrist Wraps.
- Featured products carry the Shopify tag
featured(fallback: four newest); a product's theme is its first non-featured tag. - The intro auto-skips for users with reduced motion enabled;
?intro=0skips it for tests and screenshots.
Challenges
- Shopify cart ids contain
?key=…, so they break as URL path segments; I pass them in query params or POST bodies instead. - A stale or expired cart id has to come back as an empty cart (
id: null) so the browser can clear its saved cart rather than show an error. - A locked Online Store channel or a 401/403 from the Storefront API looks like a code bug, but it means a bad or missing token, so that is the first thing to check.
Results
The Evolve Performance store is live. The storefront opens with the EP logo and EVOLVE wordmark, a Lifting Straps / Wrist Wraps / All Gear navigation with a cart, a "Free worldwide shipping on orders over $75" banner and a red marquee naming EP-01 Lifting Straps and EP-02 Wrist Wraps. The owner runs the business from Shopify Admin, and checkout happens on Shopify's hosted checkout. Evolve Performance is one of my Built.km clients.
Built.km
Want one like this?
Built.km builds websites and digital products for artists, businesses and brands, from the first conversation to deploy.
Start a projectKavin AI — an AI version of Kavin
Ask about Evolve Performance Storefront.
Kavin AI, the AI version of me, answers from the same approved write-up you just read.



