🍔🛵🗺️CASE STUDY · FOOD DELIVERY · LIVE DEMO
Fresh & Quick is a full-stack delivery platform built from scratch. It joins web and phone apps, a Postgres database with vector search, workflow automation, an AI assistant and Google Maps routing.
🛡️ A working demo. Payments are mocked and no real money moves.
How each layer works 🔧
🖥️
Frontend: web and phone
Next.js App RouterTypeScriptTailwind CSSReact NativeExpo SDK 57
- ▸One Next.js codebase serves four portals: customer, vendor, delivery partner and admin. Each has its own sign-in and navigation shell.
- ▸The phone app (React Native + Expo) covers the customer, delivery partner and a small admin screen. It uses the same backend and API routes as the web app.
- ▸An account made on the web signs in on the phone. An order placed on one surface shows on the other.
- ▸Shared logic, such as tracking and distance math, is kept byte-identical between web and phone. A parity test fails if the two drift apart.
🗄️
Backend and database
Next.js API routesSupabase (self-hosted)PostgresRow-level securitySupabase AuthDocker
- ▸Every data-changing action goes through a server-side API route. The privileged service-role key never reaches the browser.
- ▸Reads that are safe for a user to make go straight to Supabase, guarded by row-level security policies scoped per role and per table.
- ▸Checkout runs as one Postgres function, so the order, its items and its payment record are written together or not at all.
- ▸One role column drives which portal a user lands in. New customers wait for admin approval before they can sign in.
- ▸The whole stack runs in local Docker containers, which keeps running cost at zero.
🔎
Vector database and RAG knowledge base
pgvectorHNSW indexOpenAI text-embedding-3-small1,536 dimensions
- ▸The vector store lives inside the same Supabase Postgres. There is no separate hosted vector service.
- ▸The how-to knowledge base is a folder of Markdown guides. It is split by heading, hashed and embedded, and only changed chunks are re-embedded. Today it holds 160 chunks.
- ▸A second index holds store and dish names, descriptions and cuisines. It never stores prices, fees or open status, so those are always read live.
- ▸Retrieval filters by the caller’s role. A customer sees customer guides; a vendor, partner or admin sees their own.
- ▸n8n keeps the indexes fresh: one workflow re-ingests the knowledge base, another syncs the catalog nightly.
🤖
Ask Zippy, the AI assistant
Claude (Anthropic SDK)Tool-calling loopStreaming repliesRate limits
- ▸Zippy answers how-to questions from the knowledge base and names the guide each answer came from. If the guides do not cover a question, it says so.
- ▸It looks up stores, menus, prices, options and open status from live data, and a signed-in customer can ask about their own orders.
- ▸Twelve tools back it: catalog lookups, order lookups and cart and checkout actions. The loop is bounded to four tool rounds per question.
- ▸Zippy proposes cart changes as cards. Nothing changes until the customer taps Confirm. It never places, pays for or cancels an order.
- ▸Identity comes only from the verified session. Signed-in users get 10 questions a minute and 60 a day by default. Each tool group has a kill switch.
- ▸A scripted evaluation scored 46 of 47 answers correct at the last recorded run. A second check compares answers to the database.
⚙️
n8n workflow automation
n8n (self-hosted)Postgres database webhooksGmailInternal API routes
- ▸Eleven workflows react to database events instead of polling. A trigger function on the orders and payments tables posts to n8n when a row changes.
- ▸They resolve the mock payment, notify the vendor, assign the nearest online delivery partner and email the customer on acceptance and on delivery.
- ▸About an hour after delivery, one workflow emails a review request. Others handle registration approval emails, Zippy re-ingestion and chat retention.
- ▸n8n calls back into the app through internal routes protected by a shared secret header.
- ▸Every checkout-critical flow has an in-process fallback. If n8n is down, checkout still completes, only slower.
🗺️
Google Maps: addresses, live tracking and drive time
Maps JavaScriptPlacesGeocodingRoutes
- ▸The location picker uses Places search, “use my current location” and a draggable pin. If Maps fails, the page falls back to typed coordinates.
- ▸Sign-up geocodes the address on the server before it creates the account. A vague or unknown address is refused.
- ▸All 77 stores have real geocoded Mumbai addresses, so distances and nearest-first ordering use real locations.
- ▸The order page draws the road route and shows text like “About 23 min · 8.3 km by road”. The time is driving time without live traffic.
- ▸Route requests fire only when the partner moves more than 150 m and 30 s have passed. A failure backs off for 60 s and the page falls back to a straight dashed line.
- ▸If a partner’s last ping is over five minutes old, the page shows “Last seen” and no estimate rather than a stale figure.
🔌
How it was built: MCP-driven development
Claude CodePlaywright MCPClaude in ChromeFigma MCPRefero MCP
- ▸The platform was built with Claude Code, using Model Context Protocol (MCP) servers as working tools.
- ▸Playwright MCP and Claude in Chrome drove the real app in a browser for live checks and manual screenshots.
- ▸Figma and Refero MCP servers fed the design work for the visual refreshes.
- ▸Each feature went through a written spec, a plan, tests and a review before merge.
Want something like this for your process? ⏳
Send it over. I'll reply with how I'd build it.