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

0
cart modes — guest & authenticated
0%
server-calculated tax rate
0
order status stages tracked
0
seeded product categories
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.
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.
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.
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.
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.
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.
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.
JWT in an httpOnly cookie
auth tokens aren't reachable from JS, closing off the usual XSS-token-theft path.
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.
- 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
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.