Kévin Sauvage.
Back to portfolio

Case study

ORUVA — Jewelry E-Commerce

A live jewelry label for the Spanish and French market: a headless Shopify storefront built solo, from brand to deployment.

Design & development (solo) · 2026

Next.js 16React 19TypeScriptShopify Storefront APIShopify Customer Account APIShopify Admin APIGraphQLGraphQL Code Generatornext-intlTailwind CSS 4Radix UIshadcn/uiServer ActionsZodUpstash RedisPlaywrightVercel
ORUVA homepage: hero slogan beside a model wearing gold jewelry
3

Languages

24

Page routes

75

Unit-test files

ES · FR

Delivery markets

Overview

The problem & the approach

ORUVA started as a template storefront and became a real store: a warm, modern European jewelry label selling gold-toned and silver pieces meant to be worn every day, not saved for later. The storefront is built on Next.js 16 (App Router, Cache Components) and the Shopify Storefront API, with checkout handled by Shopify.

I owned it end to end: brand positioning, storefront design, the Shopify data model, the code, caching, deployment and monitoring. The store runs in English, Spanish and French, prices include VAT, and delivery is tracked to Spain and France.

The source code is private now that it is a commercial project, so this page walks through the decisions instead. I am happy to go through the code on a call.

What I owned

  • Defined the brand positioning and designed the storefront UI

  • Set up the App Router structure, caching and revalidation

  • Modelled the Shopify data contract (metaobjects, menus, policies) and built the tooling to seed and sync it

  • Integrated the Storefront, Admin and Customer Account APIs

  • Set up deployment on Vercel, environment validation, error monitoring and SEO plumbing

Product

What it does

Catalog & product pages

Collections with sorting, filters and pagination. Product pages with variant selection, an image gallery, VAT-inclusive pricing and live stock status.

Persistent cart & delivery estimate

A cart kept in sync with the Shopify cart across sessions, with a delivery estimator by country and a free-shipping threshold. Payment happens on Shopify checkout.

Predictive search

Instant suggestions from a dedicated API route that queries the Storefront API as the shopper types, announced to screen readers through a live region.

Customer accounts

Sign-in through the Shopify Customer Account API, with an account area for orders and saved addresses.

Wishlist

Saved products with optimistic add/remove, so the heart responds instantly and rolls back cleanly if the server disagrees.

Three languages

English, Spanish and French with locale-prefixed routes, translated interface text and translated Shopify content.

Newsletter & contact

A double opt-in newsletter and a contact form, both validated with Zod and rate limited.

Legal & consent

Cookie consent that gates analytics, plus privacy, terms, refund, shipping, legal-notice and cookie pages managed in Shopify.

Dark & light themes

A theme switch that follows the system preference and remembers the choice.

Engineering

How it is built

Content lives in Shopify, not in env vars

Hero, promo bar, FAQ, size chart, navigation and brand identity are Shopify metaobjects read through cached getters. Environment variables hold only secrets and deployment config, so a brand change never needs a rebuild. CLI scripts seed and sync that content.

Cache Components with webhook-driven purging

Pages are prerendered with `use cache` and tagged. Shopify webhooks, verified with an HMAC secret, invalidate exactly the tags that changed, so the catalog stays fresh without giving up static speed.

Failures are never cached

Cached getters throw instead of returning an empty fallback, so a short Shopify outage cannot poison the cache for hours. Fallbacks live in uncached wrappers, and the cart estimator sits in its own Suspense island.

Typed GraphQL end-to-end

GraphQL Code Generator turns the Storefront and Admin schemas into a typed SDK, and CI fails if the generated output drifts from the committed files.

OAuth done properly

Customer sign-in uses the authorization-code flow with PKCE (S256) and a state check. A proxy renews sessions and handles auth redirects.

Optimistic UI without stale rollbacks

Cheap toggles like the wishlist use `useOptimistic`. The cart uses request-id sequencing, so the latest server answer always wins over a slower earlier one.

Hardened delivery

Content-Security-Policy, HSTS and cross-origin headers, Zod-validated Server Actions, Upstash Redis rate limiting that holds across serverless instances, and store HTML sanitized before rendering.

Quality

Tested, typed and accessible

Quality & delivery
  • 75 Vitest files covering server actions, services, auth, rate limiting, caching and Shopify helpers, with enforced coverage thresholds

  • Playwright suites: axe accessibility checks (WCAG 2.1 AA, light and dark) over the main routes, and an add-to-cart smoke test

  • Strict TypeScript with generated GraphQL types and no hand-written API shapes

  • ESLint, Stylelint and Prettier, plus a codegen drift check in CI

  • Sentry error reporting, with metadata, sitemap and robots rules generated for every language

Want to see it live?

The store is live: browse the catalog, switch language and add to cart. The source code is private, so I am happy to walk through it on a call.