← Back to projects

CarHunter

Tyre and wheel marketplace in Tallinn, Estonia.

Year2026
ServicesDevelopment, E-commerce

CarHunter (rattad.com) sells used and premium tyres and wheels to drivers across Tallinn and the wider Baltic region. It's a straightforward pitch — better condition, better price than new — built on top of one of the messiest product catalogues in e-commerce: no two tyres in the warehouse are ever quite the same.

NSWEB was brought on as the full-stack technology partner, owning the platform end to end — a headless Medusa v2 backend, a Next.js 15 storefront, the data model underneath it, the internal tools the warehouse team uses every day, and the pipeline that gets stock onto the site in the first place.

CarHunter storefront homepage

1. CarHunter storefront, engineered by NSWEB on Medusa v2 and Next.js 15

The Challenge

Shopify, WooCommerce, and even Medusa's own defaults are all built around the same assumption: a "product" is a stable thing you can restock. A red T-shirt in size M is the same red T-shirt in size M forever. A used tyre isn't — it's a physical object with its own tread depth, its own DOT manufacture week, its own scuffs, and once it's sold, that exact unit is never coming back.

That single fact broke a lot of default assumptions at once:

Our Solutions

Next.js

Next.js

Typescript

Typescript

Medusa

Medusa

PostgreSQL

PostgreSQL

Redis

Redis

Stripe

Stripe

Tailwind CSS

Tailwind CSS

Docker

Docker

1. A product schema built for one-off inventory

Instead of forcing tyres and wheels into Medusa's generic variant model, we treat each listing's specs as structured JSONB metadata — tyre_width, tyre_height, tyre_diameter, tyre_type, condition, dot_week, dot_year for tyres, and a parallel schema of bolt_pattern, wheel_width, offset, center_bore for wheels — validated on the way in so a malformed row can never reach the storefront.

On top of that, we built a dedicated /store/products-filtered endpoint that runs hand-tuned SQL directly against those metadata fields: numeric casts so 18" and 18 match the same wheel, an EXISTS subquery that hides anything with zero real stock, and a computed dot_year * 100 + dot_week value so "freshest DOT first" sorts chronologically instead of alphabetically. The filtered ID set is then handed back to Medusa's own Query Graph to resolve pricing, images, and variants — so the storefront still gets correct multi-region, multi-currency pricing, we just get to decide what counts as a match first.

Tyre catalogue with fitment filters

2. Fitment-exact filtering by width, height, diameter, season and DOT age — not approximate matches

2. Turning a spreadsheet catalogue into a one-click import

Migrating the legacy inventory — and keeping it topped up every time a new container of stock arrives — meant the CSV pipeline had to be treated as a real feature, not a one-time script. The admin gets a CSV import tool that validates headers before touching the database, processes rows in batches of 25 for speed, and if a batch fails, automatically falls back to retrying each row individually so one bad line doesn't sink an entire import. Products are matched by handle, so re-uploading the same file updates existing listings instead of duplicating them, and stock quantities are reconciled against SKUs in the same pass.

What used to be a manual, error-prone spreadsheet exercise is now something a non-technical team member runs whenever new stock lands, with a clear success/failure report at the end instead of a silent guess.

3. Internal tools built for the warehouse floor, not the boardroom

Medusa's stock admin panel is built for someone selling generic retail SKUs — it has no concept of a bolt pattern or a DOT week. We built a dedicated Tire & Disk Manager view directly into the admin: a searchable, filterable table (SKU, brand, spec, condition, price, stock) and a schema-safe create/edit drawer that only ever asks for fields that actually apply to what's being listed.

Around it sit a few smaller tools that solve real, specific friction: a printable per-product QR code so a physical stack of tyres on a shelf can be scanned straight through to its storefront listing, and two-step-confirmation bulk-delete widgets for clearing test data between import runs. And because the people actually running inventory day to day work in Russian, we translated the Medusa admin panel itself — not just the storefront — so the internal tooling isn't a second language barrier on top of the job.

Tyre product detail page

3. Every physical unit captured with its own condition, DOT date and spec sheet

4. A storefront that speaks the market's languages

The Baltic tyre market is genuinely trilingual, so the storefront ships with Estonian, English, and Russian via next-intl, with locale read from a cookie and content, emails, and the admin panel all following suit. A custom edge middleware detects the visitor's country — from the URL, then Cloudflare's geo header, then Vercel's — and routes them into the right /{countryCode}/ segment automatically, so a returning visitor never has to re-pick their region.

The same schema-driven approach carries over to wheels: a parallel filter set for bolt pattern, wheel width, offset, and center bore, so the two halves of the catalogue — tyres and discs — get equally precise treatment instead of one being an afterthought.

Wheel catalogue with fitment filters

4. A parallel fitment schema for wheels: bolt pattern, offset and center bore

5. A transactional backbone that doesn't drop orders on the floor

Every order-lifecycle event — placed, canceled, shipped, refunded — along with account events like welcome emails, staff invites, and password resets, is handled by a dedicated Medusa workflow that pulls the full order graph before rendering a React Email template and sending it through Resend. Eight templates in total, each with translated subjects and bodies, so a Russian-speaking customer and an Estonian-speaking one both get an invoice that reads naturally. On the payment side, Stripe cards sit alongside iDeal, Bancontact, PayPal, and a manual/COD option — enough coverage that most EU buyers can pay the way they already trust.

6. Shipping it as one predictable unit

The backend and storefront live in a single pnpm/Turborepo monorepo, built into multi-stage Docker images and published to GHCR. A docker-compose stack wires up Postgres and Redis with real health checks, and the backend runs its own migrations automatically on boot — so a deploy is "pull the new tag and restart," not a checklist.

Outcome

Ready to build your masterpiece?

Let's turn your vision into a high-performance digital product. Start with a quick discovery call.

Start a Project