IAMUVIN

Case Studies & Build Logs

Building EuroParts Lanka: The Full Engineering Story

Uvin Vindula·June 2, 2025·14 min read
Share

TL;DR

I built EuroParts Lanka — Sri Lanka's first dedicated European car parts platform — as a solo engineer. The standout feature is an AI Part Finder powered by Claude's API: customers describe their car problem in plain Sinhala or English, and the system identifies the exact OEM part they need. The platform has served 966 customers, delivered 1,444 parts with a 14-day average delivery time, and built a 237-member community forum. The stack is Next.js 15, Supabase, Claude API, Tailwind v4, and Vercel. This is the full story of every architecture decision, tradeoff, and lesson learned.


The Client and the Problem

European car owners in Sri Lanka have a specific, painful problem: finding the right replacement part is a nightmare.

Here is what the landscape looked like before EuroParts Lanka. You own a 2018 BMW 320d. Your suspension is making a clunking noise over speed bumps. You need a front lower control arm bushing. You know something is wrong, but you probably do not know the exact part name, and you definitely do not know the OEM reference number.

Your options were:

  1. Visit a local garage. They guess, order something from a catalog, wait 3-6 weeks, and charge you a premium for the guesswork.
  2. Search international sites. eBay, FCP Euro, Pelican Parts — all priced in USD or EUR, none of them ship to Sri Lanka reliably, and you are navigating part numbers designed for mechanics, not car owners.
  3. WhatsApp groups. Fragmented communities where someone *might* know the answer, but there is no structured knowledge base.

The client — a European car parts importer with existing supply chain relationships in Germany and the UK — wanted a platform that solved all three problems at once. Not just an e-commerce store. A platform that could translate a customer's description of their problem into the correct part, price it in Sri Lankan Rupees with live exchange rates, and deliver it with full tracking.

The scope was ambitious for a solo build. Eleven European brands: BMW, Mercedes-Benz, Audi, Volkswagen, Porsche, Volvo, Land Rover, Jaguar, Mini, Peugeot, and Renault. Thousands of part numbers across multiple model years. An AI identification system. A community forum. Real-time pricing. Full order management.

I said yes.


Architecture Decisions

Before writing a single line of code, I spent three days mapping out the system architecture. Every decision at this stage compounds — a bad database schema costs you weeks later, and a wrong rendering strategy costs you seconds on every page load.

Why Next.js 15 and Not a Separate Backend

The first decision was whether to build a traditional frontend + API architecture or go with Next.js as a full-stack framework. For a solo developer building an e-commerce platform, the answer was straightforward.

Next.js 15 with the App Router gave me Server Components for the catalog pages (zero client-side JavaScript for browsing), Route Handlers for the API layer, Server Actions for mutations, and a single deployment target on Vercel. The alternative — a separate Express or Fastify API — would have doubled my deployment surface, added CORS configuration, and created a network hop between frontend and backend that did not need to exist.

The tradeoff: I am coupled to the Next.js ecosystem. If I ever need to serve the same API to a mobile app, I will need to extract the Route Handlers into a standalone service. For this project, that tradeoff was worth it. Ship now, extract later if needed.

Why Supabase and Not Raw PostgreSQL

Supabase gave me three things I did not want to build from scratch: authentication with email and OAuth providers, a real-time subscription layer for order status updates, and Row Level Security policies that let me enforce data access at the database level instead of in application code.

I considered PlanetScale (MySQL, excellent branching) and Neon (PostgreSQL, serverless). Supabase won because of the real-time subscriptions — when a customer's order status changes, the dashboard updates instantly without polling. That feature alone saved me from building a WebSocket server.

The Supabase client also integrates cleanly with Next.js middleware for session management. One createServerClient call in middleware, and every Server Component has access to the authenticated user without prop drilling or context providers.

The Rendering Strategy

Not every page on an e-commerce site has the same performance requirements. I split the rendering strategy into three tiers:

  • Static (ISR with 1-hour revalidation): Brand landing pages, category pages, blog posts, FAQ. These pages change infrequently and benefit from CDN edge caching.
  • Dynamic (Server Components with no caching): Product detail pages with live pricing, the shopping cart, order history. These need fresh data on every request because exchange rates affect pricing.
  • Client-side (React Client Components): The AI Part Finder chat interface, the community forum with real-time replies, the order tracking map. These are inherently interactive.

This split meant that 70% of page views hit the CDN edge cache and never touch the origin server. The remaining 30% are the high-value, interactive features that justify the server round-trip.


The AI Part Finder

This is the feature that makes EuroParts Lanka different from every other car parts website. It is also the feature I spent the most time getting right.

The Problem It Solves

A customer types: *"My 2019 Audi A4 is making a squealing noise when I brake, especially when it's wet outside."*

The system needs to:

  1. Identify the vehicle (2019 Audi A4, B9 chassis)
  2. Interpret the symptom (squealing under braking, worse in wet conditions)
  3. Map the symptom to likely components (brake pads, possibly brake disc surface)
  4. Return specific OEM part numbers from the catalog
  5. Present results with pricing, compatibility confirmation, and images

This is not a keyword search problem. "Squealing when braking" does not appear anywhere in a parts database. The system needs to reason about automotive symptoms and map them to components.

The Architecture

The AI Part Finder is a three-stage pipeline, not a single prompt.

Stage 1: Vehicle Identification and Validation

The first Claude API call takes the user's natural language input and extracts structured vehicle data. The system prompt is specific:

You are an automotive parts specialist. Extract the following from the customer's message:
- Make, Model, Year, Engine variant (if mentioned)
- Chassis/platform code (e.g., W205 for Mercedes C-Class 2015-2021)
- The symptom or problem described
- Any parts explicitly mentioned by name

If the vehicle cannot be identified with confidence, ask ONE clarifying question.
Return JSON only.

I use claude-sonnet for this stage because it is fast and accurate for structured extraction. The response is validated against a Zod schema before proceeding. If the model cannot confidently identify the vehicle, it returns a clarifying question, and the UI prompts the user.

The key decision here was not using a fine-tuned model or a RAG pipeline for vehicle identification. Claude's base knowledge of European car models, chassis codes, and model years is already excellent. A RAG approach would have added latency for retrieval and complexity for index maintenance, with minimal accuracy improvement for this specific subtask.

Stage 2: Symptom-to-Component Mapping

This is where the reasoning happens. The second API call receives the structured vehicle data from Stage 1 and a curated context window: the vehicle's known common failure points, the parts catalog filtered to that specific model and year, and a symptom-component mapping table I built manually from service bulletins and forum data.

The prompt engineering here took the longest to get right. Early versions were too eager to suggest parts — a customer describing a vague noise would get five unrelated suggestions. I added a confidence scoring requirement:

For each suggested component, provide:
- component_name: The common name
- oem_part_number: The exact OEM reference
- confidence: HIGH / MEDIUM / LOW
- reasoning: One sentence explaining why this component matches the symptom
- follow_up_question: If confidence is MEDIUM or LOW, a question that would increase confidence

The confidence threshold for displaying a result is MEDIUM. LOW-confidence matches are logged for analysis but not shown to the customer. HIGH-confidence matches get a green badge in the UI. This transparency builds trust — customers can see *why* the system is recommending a specific part.

Stage 3: Catalog Lookup and Pricing

The final stage is not AI at all. It is a deterministic database query. The OEM part numbers from Stage 2 are looked up in the Supabase catalog table, joined with the latest exchange rate data, and returned with pricing in LKR, stock availability, estimated delivery time, and compatible model years.

This separation is deliberate. The AI handles the fuzzy, reasoning-heavy work (symptom interpretation). The database handles the precise, structured work (pricing and availability). Mixing these — asking the AI to also return prices — would introduce hallucination risk on the most business-critical data point.

Handling Edge Cases

The tricky scenarios taught me the most:

Multi-component problems. A customer says "my car pulls to the left when braking." This could be a stuck caliper, uneven pad wear, a warped disc, or a worn tie rod. The system returns all plausible components ranked by confidence, and the UI presents them as "Most likely" through "Also check" tiers.

Wrong vehicle identification. A customer says "my BMW 3 Series" without specifying E90, F30, or G20. These are completely different cars with different parts. Stage 1 detects the ambiguity and asks: "Which year is your 3 Series? This helps me identify the exact chassis." The conversation continues naturally.

Out-of-catalog parts. The system identifies the correct part, but it is not in the EuroParts Lanka catalog. Instead of returning nothing, the system shows the OEM part number and offers to create a special order request. This turned into a significant revenue stream — roughly 15% of orders are special-order parts that the customer would never have found without the AI identification.

Performance

The full three-stage pipeline completes in 2.1 seconds average. Stage 1 takes ~600ms (Claude API latency), Stage 2 takes ~1.2s (larger context window), and Stage 3 takes ~300ms (Supabase query with index lookup). I stream the Stage 2 response to the UI using the Vercel AI SDK, so the customer sees the reasoning appear in real-time rather than waiting for the full response.

The Claude API costs for this feature average $0.03 per conversation. At ~200 conversations per month, that is $6/month in API costs for a feature that directly drives sales. The ROI is not even close.


Database and Backend

Schema Design

The Supabase PostgreSQL schema has 14 tables. The core ones:

  • vehicles — Make, model, year, chassis code, engine variants. Normalized with a vehicle_variants junction table for models that span multiple years.
  • parts — OEM part number (primary), aftermarket cross-references, category, subcategory, weight, dimensions. Each part has a compatibility JSONB column mapping to vehicle variant IDs.
  • orders — Standard e-commerce order model with status enum (pending, confirmed, sourcing, shipped, delivered), payment method, shipping address, and a tracking_events JSONB array for the order timeline.
  • exchange_rates — Daily snapshots of EUR/LKR and GBP/LKR from the Central Bank of Sri Lanka API. Used for pricing calculations.
  • fuel_prices — Weekly fuel price data from Ceylon Petroleum Corporation. Displayed on the dashboard as a community utility for European car owners who care about running costs.
  • forum_posts and forum_replies — The community forum schema with full-text search indexes, vote counts, and category tags.

The Exchange Rate Pipeline

Pricing European car parts in Sri Lankan Rupees requires accurate, up-to-date exchange rate data. I built a Supabase Edge Function that runs on a daily cron schedule:

  1. Fetches the latest indicative rates from the Central Bank of Sri Lanka's published data.
  2. Validates the response against the previous day's rate (rejecting changes greater than 5% as likely API errors).
  3. Inserts the new rate into the exchange_rates table.
  4. Triggers a Supabase real-time event that invalidates the ISR cache for product pages via a Next.js revalidation webhook.

The fuel price pipeline works similarly, pulling from Ceylon Petroleum Corporation's published prices on a weekly schedule.

These data feeds serve a dual purpose. They keep product pricing accurate, and they give European car owners a reason to visit the platform even when they are not buying parts. The fuel price widget on the homepage drives repeat traffic.

Row Level Security

Every table has RLS policies. Customers can read their own orders but not other customers' orders. Forum posts are publicly readable but only editable by the author. Admin users have full access through a role-based policy.

The RLS policies replaced approximately 400 lines of authorization middleware I would have otherwise written in the API layer. More importantly, they cannot be bypassed by a misconfigured API endpoint — the database itself enforces access control.

Order Management and WhatsApp Integration

The order flow is: Cart > Checkout > Payment confirmation (manual bank transfer or card) > Order confirmed > Sourcing from supplier > Shipped > Delivered.

Each status transition triggers two things: a Supabase real-time event that updates the customer's order tracking page instantly, and a WhatsApp message via the WhatsApp Business API. Customers receive proactive updates without opening the website.

The WhatsApp integration was more complex than expected. Sri Lankan customers overwhelmingly prefer WhatsApp over email for transactional communication. The message templates required approval from Meta, the delivery receipts needed to be tracked, and the session-based pricing model meant I had to batch non-urgent messages within the 24-hour session window to minimize costs.


Frontend and UX

Design Philosophy

European car parts are a premium product category. The design needed to communicate precision and trustworthiness — not the flashy aesthetics of a consumer brand, but the confident minimalism of an engineering tool.

The color palette is anchored in deep navy (#0A1628) with steel blue accents and amber (#F7931A) for CTAs and price highlights. Typography is Inter for body text and Plus Jakarta Sans for headings. Every product image has a consistent white background with controlled lighting.

The Catalog Experience

The parts catalog uses a three-level navigation: Brand > Model > Category. Each level is a Server Component that fetches data at request time (for accurate pricing) but leverages the Next.js cache for the navigation structure.

Product cards show the part image, OEM number, compatibility badge (which models it fits), price in LKR, and estimated delivery time. The compatibility badge is critical — it tells the customer immediately whether the part fits their specific vehicle, reducing returns.

The Forum

The community forum was a strategic decision, not a feature request. A forum gives European car owners a reason to register, visit regularly, and build trust with the platform before they need to buy a part.

The forum has 237 registered members as of launch. Threads are organized by brand and topic (maintenance, modifications, troubleshooting, marketplace). Each thread has full-text search powered by Supabase's tsvector columns, and popular threads appear on the homepage as social proof.

I built the forum with real-time reply updates using Supabase's real-time subscriptions. When someone replies to your thread, you see it appear without refreshing. This small detail makes the forum feel alive, which is critical for community retention.

Animations and Interactions

I used Framer Motion sparingly. Page transitions use a subtle fade-slide (200ms, ease-out). Product cards have a hover lift effect. The AI Part Finder chat has a typing indicator animation. The order tracking timeline animates step completion.

No scroll-jacking. No parallax. No hero animations that delay content visibility. The target audience is pragmatic — they want to find a part and buy it. Every animation serves a functional purpose: confirming an action, indicating loading, or providing spatial continuity between pages.


Performance Results

Performance was not an afterthought. I set targets before writing code and measured throughout.

MetricTargetActual
LCP (Largest Contentful Paint)< 2.5s1.8s
FID (First Input Delay)< 100ms45ms
CLS (Cumulative Layout Shift)< 0.10.02
TTI (Time to Interactive)< 3.5s2.9s
Lighthouse Performance Score> 9094
AI Part Finder Response< 3s2.1s
Page Size (median)< 500KB340KB

The key optimizations:

  • Server Components by default. The catalog browsing experience ships zero client-side JavaScript. Only the interactive features (cart, AI chat, forum) are Client Components.
  • Image optimization. Every product image runs through Next.js Image with automatic WebP/AVIF conversion, responsive srcSet, and explicit dimensions to prevent CLS.
  • Font strategy. Inter and Plus Jakarta Sans are self-hosted with font-display: swap and preloaded for the critical weights (400, 500, 600, 700).
  • Bundle analysis. The Claude SDK is only imported in the API route, never in a Client Component. Framer Motion is dynamically imported where used. The main bundle is 89KB gzipped.

Business Results

Numbers matter more than architecture diagrams. Here is what the platform has delivered:

  • 966 customers served since launch
  • 1,444 parts delivered
  • 14-day average delivery from order to doorstep (for parts sourced from Europe, this is competitive)
  • 11 European brands covered with active inventory
  • 237 forum members with organic growth, no paid acquisition
  • 15% of orders originated from the AI Part Finder's special order feature (parts not in catalog but identified by the AI)
  • 4.8/5 average satisfaction from post-delivery surveys
  • $6/month average Claude API cost for the AI Part Finder

The AI Part Finder has a measurable impact on conversion. Customers who use the Part Finder have a 34% higher conversion rate than those who browse the catalog directly. The reasoning is straightforward: the Part Finder eliminates the uncertainty of "is this the right part for my car?" which is the primary purchase barrier.


What I'd Do Differently

Honest retrospection is more valuable than polished narratives. Here is what I would change:

1. Start with the Forum Earlier

I built the forum as the last feature before launch. In hindsight, launching the forum three months before the e-commerce features would have built an audience and generated organic SEO content. The forum threads now rank for long-tail keywords like "BMW F30 320d timing chain Sri Lanka" — that SEO value compounds over time.

2. Build a Part Number Search First, AI Second

The AI Part Finder gets the attention, but the most common use case is a customer who already knows their OEM part number and just wants to check price and availability. I should have built a fast, simple part number search as the primary flow and positioned the AI as the secondary "not sure what you need?" path. I added the direct search later, but the initial architecture assumed AI-first, which over-complicated the early UX.

3. Invest in Structured Product Data Earlier

The parts catalog started as a spreadsheet import. Getting structured data — accurate fitment information, cross-reference numbers, high-quality images — was the biggest ongoing time investment. If I were starting over, I would build the admin tooling for product data entry first, with validation rules that enforce completeness before a part goes live.

4. WhatsApp as the Primary Channel from Day One

WhatsApp is not a notification channel in Sri Lanka — it is the communication layer. I integrated it as an add-on, but it should have been the primary customer touchpoint from the beginning. Order placement via WhatsApp, support via WhatsApp, delivery updates via WhatsApp. The web platform is the catalog and admin layer; WhatsApp is where the customer relationship lives.


Lessons for Other Developers

AI Features Need Boundaries, Not Just Prompts

The AI Part Finder works because it has strict boundaries. The AI handles symptom interpretation. The database handles pricing. The UI handles confidence communication. When I initially tried to have the AI do everything — interpret symptoms, look up prices, generate recommendations — the results were unreliable. Separation of concerns applies to AI features just as much as traditional code.

Solo Builds Need Ruthless Scope Control

Eleven brands, an AI system, a community forum, real-time pricing, WhatsApp integration, and a full e-commerce flow — this is a lot for one developer. I shipped it by being ruthless about what "done" means. V1 of the forum had no image uploads. V1 of the cart had no saved addresses. V1 of the AI Part Finder had no conversation history. Every feature launched at its minimum useful state and was iterated based on real customer feedback.

Supabase RLS Policies Are Worth the Learning Curve

If you are building a multi-user application with different access levels, RLS policies save you from an entire category of bugs. Every authorization check that lives in your API code is a check that can be accidentally removed, incorrectly modified, or bypassed by a new endpoint. RLS policies at the database level are the last line of defense, and they cannot be bypassed by application code.

Exchange Rate Data Is Harder Than You Think

Fetching a number from an API seems trivial. But when that number determines product pricing, you need validation (is this rate plausible?), fallback logic (what happens when the API is down?), historical tracking (show the customer how the price has changed), and audit trails (prove to the business that pricing was correct at the time of purchase). I built all of this incrementally, and I wish I had thought through the requirements upfront.

Community Features Drive Retention More Than Features

The forum, the fuel price widget, the exchange rate display — these are not directly revenue-generating features. But they bring European car owners back to the platform regularly. When they eventually need a part, EuroParts Lanka is already bookmarked. The acquisition cost of a customer who is already visiting your site is zero.


Tech Stack Breakdown

LayerTechnologyWhy
FrameworkNext.js 15 (App Router)Server Components, Route Handlers, ISR — full-stack in one deployment
DatabaseSupabase (PostgreSQL)RLS, real-time subscriptions, auth, Edge Functions
AIClaude API (claude-sonnet)Best reasoning for symptom-to-part mapping, structured JSON output
StylingTailwind CSS v4CSS-first config, design tokens as CSS variables, fast iteration
AnimationFramer MotionDeclarative, performant, React-native integration
DeploymentVercelEdge caching, preview deployments, zero-config Next.js hosting
MessagingWhatsApp Business APITransactional messages, order tracking, customer preferred channel
SearchSupabase Full-Text SearchPostgreSQL tsvector, no external search service needed
MonitoringVercel Analytics + Speed InsightsCore Web Vitals tracking, real user metrics

Key Takeaways

  1. AI works best when it handles one stage of a pipeline, not the entire flow. The three-stage architecture (extract, reason, lookup) is more reliable than a single prompt that tries to do everything.
  1. Server Components are a genuine performance advantage for e-commerce. 70% of EuroParts Lanka page views ship zero client JavaScript. That is not a vanity metric — it directly impacts LCP and conversion.
  1. Build for the customer's communication preference, not yours. In Sri Lanka, that is WhatsApp. In other markets, it might be Line, WeChat, or SMS. The platform should adapt to the customer, not the other way around.
  1. Community features are a long-term investment with compounding returns. The forum generates SEO content, builds trust, reduces support load (customers answer each other's questions), and drives repeat visits. Build it early.
  1. Solo does not mean low quality. It means ruthless prioritization. Every feature at its minimum useful state, shipped fast, iterated based on data. The 966 customers do not know or care that one person built the platform. They care that it works.

About the Author

I'm Uvin Vindula — a full-stack engineer and AI developer building production-grade applications from Sri Lanka and the UK. EuroParts Lanka is one of several client projects I've delivered end-to-end, from architecture through deployment and ongoing optimization.

If you're building something ambitious and need an engineer who can own the entire stack — frontend, backend, AI integration, deployment, and performance — I'd like to hear about it.

[View my services](/services) | [Get in touch](/contact)

You can also explore more of my work at uvin.lk or follow me @IAMUVIN where I build in public.

Working on a Web3 or AI project?

Share
Uvin Vindula

Uvin Vindula

Web3 and AI engineer based in Sri Lanka and the UK. Author of The Rise of Bitcoin. Director of Blockchain and Software Solutions at Terra Labz. Founder of uvin.lk — Sri Lanka's Bitcoin education platform with 10,000+ learners.