skip to content
← back
project archive / case file

FreshKart

Groceries in the cart, whether you're logged in or not.

shippedSolo build — backend, frontend, payments, admin dashboardProof-of-work project
FreshKart — hero shot
loading the image…developing the print…
FreshKart — hero shot

0

cart modes — guest & authenticated

0%

server-calculated tax rate

0

order status stages tracked

0

seeded product categories

01 / the real problem
01

carts shouldn't require an account

forcing sign-up before someone can even build a cart is a conversion killer — the friction has to come after the value, not before it.

02

prices can't be trusted from the client

a checkout that recalculates totals from whatever the browser sends is one dev-tools edit away from a free order.

03

stock and payment state drift apart

card payments and cash-on-delivery follow different timelines — stock has to come off the shelf at the right moment for each, not the same moment for both.

02 / the approachbuild log, in order
  1. guest cart that becomes a user cart

    carts are cookie-based for guests and DB-based for logged-in users. on login or registration, the guest cart merges straight into the user's cart instead of getting dropped.

  2. optimistic UI, debounced sync, rollback on failure

    every cart mutation updates Zustand state immediately, then syncs to the server after a 500ms debounce. a failed sync rolls back to the pre-mutation snapshot instead of leaving the UI wrong.

  3. totals are computed server-side, always

    subtotal, 8% tax, and the delivery fee threshold are calculated from DB prices on every request — the client only ever displays numbers, never sets them.

  4. stock deduction keyed to payment method

    cash-on-delivery deducts stock at order creation; card payments wait for a verified Stripe webhook before touching inventory, so a card that never completes never reserves stock.

  5. JWT in an httpOnly cookie

    auth tokens aren't reachable from JS, closing off the usual XSS-token-theft path.

  6. AI drafts the product copy, not the data

    Gemini rewrites plain product names into Instacart-style titles and generates descriptions from the admin panel — a content assist, not something that touches pricing or stock logic.

03 / how it's built
frontend
React 19, Vite, TypeScript, Tailwind CSS 4, Radix UI, shadcn/ui
state
Zustand 5 (persisted cart) + TanStack React Query 5
backend
Node.js, Express 5, TypeScript
database
MongoDB + Mongoose 9
auth
Passport.js JWT, httpOnly cookies, bcryptjs
payments
Stripe Checkout Sessions + webhook verification
media
Cloudinary + Multer (memory storage, stream upload)
ai
Google Gemini via Vercel AI SDK
validation
Zod 4 on every request boundary
04 / what it doesclick a drawer

05 / a few screensimages land here
image coming soon
home — categories, deals, and featured products
image coming soon
product listing — filters, sort, and search
image coming soon
checkout — address picker and payment method
image coming soon
admin dashboard — analytics and product management
06 / why, not just what

why a dual guest/authenticated cart instead of requiring login upfront?

most shoppers want to see a full cart before deciding to create an account. cookie-based guest carts remove that wall, and merging into the user cart on login means nothing gets lost.

why deduct stock at different points for card vs. COD?

COD is a firm commitment the moment the order is placed. card payments aren't final until Stripe confirms it, so stock only comes off the shelf once the webhook verifies the charge actually went through.

why recompute prices server-side instead of trusting the cart payload?

a checkout that trusts client-sent totals is trivially editable in dev tools. every total is derived from the DB's current prices at order time, no exceptions.

why Gemini for product copy instead of writing it by hand?

titles and descriptions follow a fairly mechanical format (brand, count, descriptors, size). generating a first draft and letting the admin edit is faster than writing dozens from scratch, without touching anything price- or stock-related.

that's the whole file← back to the archive