Skip to content
All projects

Published

Used-car price guide and auction portal

A public valuation guide for used cars backed by a financial institution: look up what a model is worth, follow the monthly price index, and see which repossessed cars are going to auction — with a back office where every number is uploaded, reviewed and approved before the public ever sees it.

  • Go
  • Gin
  • PostgreSQL
  • GORM
  • Next.js
  • React
  • TypeScript
  • LDAP

Overview

A price guide is only useful if people trust the numbers, and a bank publishing valuations has to be able to show where each one came from and who signed it off. The public side is a search: find the model, see the price range, read the index, check the auction calendar. The private side is the part that makes it trustworthy.

The parts that were interesting to build

Every figure arrives as a versioned file, and nothing is live until someone approves it. Monthly valuations come in as spreadsheets. An upload is parsed, validated against a fixed column contract, and written as a batch of rows tagged with the id of the file they came from — with the file itself marked unapproved. Public reads never query the table directly; they first resolve the newest approved file, then read only rows belonging to it. Two things fall out of that design for free. A bad upload is invisible to the public no matter how wrong it is, because it was never approved. And rolling back is not a data operation at all — you approve the previous file. Every upload is recorded against the person who made it.

Authentication against the corporate directory, permissions from a matrix. Staff sign in against the organisation's directory over a secured connection. Group membership is what determines the role, and a user in more than one relevant group is rejected rather than guessed at. The permission itself is a lookup: the action being attempted, in the area being attempted, for that group — create, update, approve, view the log — resolved per request.

Cascading filters that stay fast. The catalogue narrows brand to model to sub-model, each step a distinct query against indexed columns, with results memoised in process under composite keys for a few minutes. It is a small cache with a short life, which is the right size for a dataset that only changes once a month.

Scale

  • 74 endpoints across 12 modules · 15 tables · 20 migrations · around 20,000 lines of backend code
  • 40 screens across the public site and the back office · two scheduled jobs
  • Deployed by pipeline onto managed hosts, without containers
Status
Published
Published
Sep 12, 2026
Stack
Go · Gin · PostgreSQL · GORM · Next.js · React · TypeScript · LDAP