Case study — Moto Market

The right part, first time.

A Nepali-first, multi-vendor marketplace for new and used bike and car parts in Kathmandu. Buyers save their vehicle once, see only parts that fit it, and compare the same part across verified shops in Teku, Balaju and Ring Road. Sellers list stock, negotiate and fulfil; platform staff verify shops and settle disputes.

Data model74Prisma models, 74 enums
Surface79Pages across buyer, seller, admin
Packages10Workspace projects, 20 API modules
Tests183Unit + 21 Playwright specs
LocalesNE · ENNepali first, English second
01 — The problem

Buying a part in Kathmandu means three shops, two wrong fits, and a whole afternoon.

Parts counters don't publish inventory. Prices vary by street. Fitment is a conversation with the man behind the counter, and used parts are sold on trust alone. A generic e-commerce template can list products; it can't answer “does this fit my 2019 Pulsar?”

The platform also had to work for the seller with a spreadsheet and the admin who has to decide whether a shop is real — in Nepali, on a mid-range phone, paid through wallets that most global stacks have never heard of.

02 — Role & stack

Solo, in a monorepo
built for a team

Domain model, contract, API, three route groups, the design system, CI guardrails and the Docker deployment — all one pair of hands, structured as if ten would follow.

Buyer appGarage, fitment search, price comparison across shops, cart, multi-seller checkout, order timeline, reviews and messaging.
Seller consoleOnboarding with PAN and shopfront documents, CSV bulk import, quick edit, inventory with cost and margin, payouts, ledger and statements.
Admin consoleSeller approval, counterfeit reports, disputes, moderation flags, campaigns, commission rules, settlement imports and payout runs.
Shared packagesContract, API, auth, db, i18n, email, utils, config and a Storybook-documented UI system called Torque.
Frontend

Next.js 16 · React 19 · Tailwind 4 · next-intl · TanStack Query

API

oRPC · Zod 4 · contract-first · OpenAPI at /api/openapi

Data

PostgreSQL · Prisma 7 · trigram search indexes

Auth & pay

Better Auth phone OTP · eSewa · Khalti · cash on delivery

Tooling

Turborepo · pnpm · Vitest · Playwright · Storybook · Docker

03 — What I built

Three storefronts
over one catalog

Vehicle garage

Type, make, model and variant hierarchy; a buyer saves their bike once and every search is filtered to parts that fit it.

New vs. used, honestly

Used listings carry a condition report with the donor vehicle's mileage and close-up photos.

Compare across counters

The same part from Teku, Balaju and Ring Road shops side by side — price, condition, rating and delivery.

Nepal-native checkout

Cash on delivery plus eSewa and Khalti wallets behind one gateway interface, with coupons, tax and commission rules.

Offers & messaging

Buyer-seller conversations with negotiated offers whose accepted price carries straight into the order.

Trust layer

Verified-shop badges, reviews with replies, disputes with evidence, counterfeit reports, and a part-request board.

04 — Deep dive

Search that knows your bike

The garage is the product's spine. A buyer picks type, make, model and variant once; from then on the catalog, the compare view, saved searches and stock alerts are all scoped to what actually fits.

  • Fitment is modeled twice: per catalog part and per individual listing.
  • Search runs against trigram indexes, tested with a 50,000-listing perf seed.
  • A real regression was found by the p95 test and fixed before it shipped.
  • Result grids target a mid-range Android on 3G: 44px targets, no letter-spacing on Devanagari.
EngineBrakesSuspensionElectricalBodyTyresFuel system+8 more
05 — Contract-first monorepo

One rule: logic lives
in exactly one place

Ten workspace packages, but a single source of truth for every business rule. The contract package defines the procedures; the API package implements them; server components and the browser call the same functions through different transports.

From schema to shipped
01ContractZod schemas and procedure definitions, shared by server and every client.
02APIThe only place business logic lives — 20 domain modules, one scaffold command to add another.
03Server callsReact Server Components call procedures in-process; the browser calls them over RPC.
04EnumsGenerated from the Prisma schema; CI fails if the generated file is stale.
05GuardsWorkspace deps, tokens, icons, typecheck, lint at zero warnings, coverage floors, build, format.
06TenancyCross-tenant tests on orders and payments prove one seller can never read another's rows.

Payout account numbers are encrypted at rest. Every package carries its own conventions file, and a single check command runs the whole gauntlet before anything merges.

06 — Engineering decisions

Nepal is not
a locale setting

01
Money is integer paisa, and the field name says soDomain

Every monetary column is named with its unit — pricePaisa, not price — and a test fails if a field carrying money doesn't follow the convention. Display formats with lakh and crore grouping.

Why — Rs. 1,25,000 is how a Kathmandu buyer reads a number. Floating-point rupees is how a marketplace loses one.

02
Store UTC, show Bikram SambatTime

All timestamps are UTC in the database. A utils module converts to Nepali calendar dates at the edge, with Nepal's UTC+05:45 offset handled once.

Why — Two calendars in one table is a bug factory. One canonical clock, one conversion, tested.

03
Built to the real gateway shape, without live keysPayments

eSewa and Khalti adapters implement signed fields, callback verification and amount checks exactly as the providers document them, behind one gateway interface, with network calls kept out of database transactions. Dev pages exercise the flows.

Why — When merchant credentials arrive, swapping the stub for the live adapter should be a config change, not a rewrite.

04
A phone number is the accountIdentity

Login is a phone OTP normalized to E.164, with an OTP lockout table, rate limiting and an auth event log. The dev-only OTP echo refuses to start in production.

Why — Most of the audience has a SIM and a Facebook account, not an email address they check.

05
Audits that record what wasn't verifiedProcess

Buyer, seller and admin audit documents list the paths that were exercised and the ones blocked by infrastructure, alongside thirteen architecture decision records.

Why — A green checklist that hides its gaps is worse than an honest one. The next engineer should know where to look first.

07 — Scale

Buyer, seller and admin domains, end to end.

74 models · 23 migrationsVehicles, fitment, listings, condition reports, carts, orders, shipments, payouts, disputes and moderation.
20 API modulesFrom catalog and garage to payments, messaging, part requests and admin — each contract-first.
2 localesNepali as the default, English second, with Devanagari-safe typography rules in the design system.
13 ADRsRecorded decisions on auth, payments, tenancy, media, and the contract-first API.

Built for the counter, not the catalog.