Vintage Cars
Classic-car showroom and inventory platform
A Flutter showroom app over a hardened Express API — browse and filter classic stock on mobile, manage inventory, brands and categories from the dealer dashboard. Zod validates every request boundary and a Sharp pipeline normalises listing photography, so provenance and condition read the way the stock deserves.
The problem
Classic-car dealers sell on photography and provenance, and general-purpose listing sites flatten both. Stock also changes slowly but matters enormously per unit, so inventory accuracy and image quality outrank catalogue scale. A single missing service record can move an asking price by five figures.
Our approach
A Flutter client gives the showroom a native feel on mobile from the same codebase that powers the dealer dashboard. The API takes hardening seriously for a public-facing surface: Zod schemas validate every request, uploads run through a Sharp pipeline that normalises and resizes listing photography, and rate limiting sits in front of the auth routes. Provenance is modelled as an append-only event log rather than a free-text notes field.
The outcome
A showroom where listings look the way the stock deserves, and a dashboard where a dealer can maintain taxonomy and inventory without touching a spreadsheet.
What is inside
Flutter client with showroom browsing, dealer dashboard and authentication
Brand and category taxonomy layered over the car inventory model
Zod schema validation on every API boundary
Sharp image pipeline normalising and resizing listing photography on upload
Rate limiting and JWT auth protecting public endpoints
Aggregate stats endpoint powering the dashboard overview
Original React + Ant Design web build retained as a legacy client
Engineering notes · 4 min read
Provenance is an event log, not a notes field
Small
Catalogue size
value per unit is hugeAppend-only
Provenance
never editedSharp
Image pipeline
normalised on uploadA classic car is worth what its history can prove. Owners, service intervals, restoration work, one documented accident — each of those is a dated fact that changes the asking price, and none of them should be editable after the fact.
So provenance is stored as an append-only sequence of events rather than a free-text notes field. A correction is a new event that supersedes an old one, which means the record of what the dealer believed last year survives.
Hardening a public surface
Everything a browsing visitor touches is unauthenticated, which makes the API the attack surface. Zod schemas validate every request boundary rather than trusting a route handler to check the shapes it needs, and rate limiting sits in front of the auth routes.
Uploads get the most attention, because that is where a public endpoint accepts arbitrary bytes. Every image goes through a Sharp pipeline that re-encodes and resizes it, which normalises listing photography and, as a side effect, discards anything that was not actually an image.
- Zod validation on every boundary, not just the ones that felt risky
- Sharp re-encode on upload — normalises presentation and strips non-image payloads
- Rate limiting in front of auth, because credential stuffing is the cheapest attack
- One aggregate stats endpoint for the dashboard, rather than six chatty ones
- The original React + Ant Design web client kept as a legacy surface during the Flutter transition
| Screen | At 375px | Decision |
|---|---|---|
| Stock list | Stacks into cards cleanly | Mobile-ready |
| Listing detail | Works, images reflow | Mobile-ready |
| Provenance timeline | Needs horizontal room to read as a sequence | Desktop advised in the modal |
Client
Back end
Data
Legacy
Platforms
iOS · Android · Web
Need a platform like Vintage Cars?
We can walk you through how this was built, what it cost to run, and what the equivalent looks like for your business.
Or email us directly at [email protected]