A full‑stack e‑commerce application built with MongoDB, Express, React, and Node.js, with a luxury / editorial storefront (Tailwind CSS, light & dark themes). It covers the complete commerce lifecycle — browse → cart → checkout → payment → order pipeline (place → ship → deliver → return → refund) — plus real‑time order updates, an admin workspace, transactional and scheduled email, Redis caching, and AI‑assisted product features.
- Authentication & security — JWT (httpOnly cookie) sessions, Google OAuth sign‑in, two‑factor auth (TOTP), password reset by email
- Product discovery — catalogue with search, filters, and pagination; rich product pages with an image gallery / lightbox
- Reviews & ratings — customer reviews plus AI‑generated review summaries (Google Gemini)
- Wishlist — save items for later, with daily wishlist‑reminder emails
- Cart & checkout — multi‑step checkout with a saved address book (pick a saved shipping address at checkout)
- Orders — order history, one‑click reorder, and real‑time order status (Processing → Shipped → Delivered) over Socket.io
- Returns & refunds — request returns and track refund status
- Plus membership — subscription tier (Cashfree)
- Newsletter — subscribe / unsubscribe with a weekly digest email
- Dashboard with date‑range analytics (revenue, orders, products, returns, refunds, coupons) and lightweight count stats
- Products — create, update, delete; image uploads (PNG/JPEG/WebP) to AWS S3
- Orders — view and advance status (emits real‑time updates), handle returns and refunds
- Users — management and roles
- Coupons — create codes with a discount and optional expiry
- Admin routes are guarded so only authenticated admins can reach them
- Short, URL‑friendly 8‑character document IDs (
nanoid) - MongoDB indexes for hot query paths (catalogue filter/sort, order history, product text search)
- Gzip response compression
- Redis (Upstash) caching for product and order data
- Rate limiting — a general API limiter plus a strict limiter on auth endpoints (
express-rate-limit) - Pooled SMTP transport (Nodemailer) so transactional email doesn’t block responses
- Socket.io for real‑time product and order events
- Swagger API docs at
/api-docs - Route‑level frontend code splitting (React lazy loading)
Account activation, order confirmation, password reset, contact, weekly newsletter, and wishlist reminder.
Backend: Node.js, Express, MongoDB + Mongoose, Socket.io, JWT, speakeasy (2FA), Nodemailer (pooled SMTP) + EJS, Cashfree (payments), Google Generative AI (Gemini), Upstash Redis, express-rate-limit, AWS S3, Swagger.
Frontend: React, Redux + redux‑thunk, React Router, Tailwind CSS, MUI, Axios, Socket.io‑client, Chart.js / Recharts, React‑Toastify.
Payments are integrated via Cashfree (order checkout and membership). Stripe client libraries also remain in the frontend from an earlier integration.
- Node.js 18+ (developed on Node 22)
- MongoDB (local or Atlas)
- Optional: Upstash Redis (caching), AWS S3 (image storage), SMTP credentials (email), Cashfree, Google OAuth, Gemini API key
git clone https://github.com/AnantDuhan/MERN-Ecommerce.git
cd MERN-Ecommerce
# backend dependencies (root package.json)
npm install
# frontend dependencies
cd frontend && npm install && cd ..Create backend/config/config.env locally. Never commit real secrets.
# Core
PORT=4000
NODE_ENV=development # 'production' makes the backend serve the built frontend
FRONTEND_URL=http://localhost:3000
DB_URI=mongodb://localhost:27017/e-commerce # or DB_HOSTED_URI for Atlas
JWT_SECRET_KEY=your_jwt_secret
JWT_EXPIRES_IN=5d
COOKIE_EXPIRES=5
RESULT_PER_PAGE=8
CRON_SECRET=<random secret> # required to trigger the /jobs endpoints
ENABLE_IN_PROCESS_CRON=false # true only for a single always-on instance
# Email (pooled SMTP)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_MAIL=your-email@example.com
SMTP_PASSWORD=your-smtp-password
NEWSLETTER_UNSUBSCRIBE_URL= # optional; defaults to FRONTEND_URL/api/v1/unsubscribe
# Integrations (optional)
GOOGLE_CLIENT_ID=your_google_oauth_client_id
GEMINI_API_KEY=your_gemini_key
UPSTASH_REDIS_REST_URL=https://<instance>.upstash.io
UPSTASH_REDIS_REST_TOKEN=your_token
REDIS_URL= # or REDIS_HOSTED_URL for a local/remote Redis
CASHFREE_APP_ID=...
CASHFREE_SECRET_KEY=...
CASHFREE_ENVIRONMENT=SANDBOX
CASHFREE_RETURN_URL=...
CASHFREE_WEBHOOK_URL=...
CASHFREE_MONTHLY_AMOUNT=...
CASHFREE_YEARLY_AMOUNT=...
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_BUCKET_NAME=your_bucket_name
AWS_BUCKET_REGION=us-east-1Frontend (frontend/.env, optional):
REACT_APP_SOCKET_URL=http://localhost:4000 # must match the backend port for real-time featuresThe frontend dev server proxies API requests to
http://localhost:4000, so run the backend onPORT=4000in development. Socket.io defaults to the same URL.
# Terminal 1 — backend (repo root)
npm run dev # nodemon, or: npm start
# Terminal 2 — frontend
cd frontend && npm startClient: http://localhost:3000 · API: http://localhost:4000 · health check: GET /api/v1/health.
cd frontend && npm run build && cd ..
npm startTwo email jobs — weekly newsletter (backend/newsletterJob.js) and wishlist reminders (backend/wishlistJob.js, emails users with saved items).
They can run two ways:
- Externally triggered (default, recommended for hosts that sleep idle instances). Secret‑protected endpoints run each job on demand:
POST /api/v1/jobs/newsletterandPOST /api/v1/jobs/wishlist, both requiring anx-cron-secret: <CRON_SECRET>header..github/workflows/cron.ymlcalls these on a schedule (daily wishlist, weekly newsletter). Set repo secretsBACKEND_URLandCRON_SECRET. The request wakes a sleeping instance and the endpoint returns202immediately, running the batch in the background.
- In‑process timers. Set
ENABLE_IN_PROCESS_CRON=trueto run both on a dailysetIntervalinside the web process — only suitable for a single always‑on instance (multiple instances would send duplicates, and a sleeping instance never fires).
Use one mechanism or the other, not both, to avoid duplicate sends.
Run from the repo root with a valid DB_URI (back up the database before any --apply):
node backend/scripts/createIndexes.js # create the MongoDB indexes the app queries on
node backend/scripts/migrateIds.js # dry run: preview re-keying documents to short IDs
node backend/scripts/migrateIds.js --apply # perform the ID migration
node backend/scripts/repairOrderProductRefs.js # repair order → product references
node backend/scripts/fixRefTypes.js # (legacy) numeric→string ref type fixes; --apply to writemigrateIds.js is a dry run by default and is re‑runnable (already‑migrated documents are skipped).
Base path: /api/v1.
Auth
POST /register(multipart, image fieldimage) ·POST /login·GET /logoutPOST /password/forgot·PUT /password/reset/:token·GET /mePOST /auth/google- (auth endpoints are rate‑limited)
Address book
GET /addresses·POST /address/new·DELETE /address/:addressId
Products & reviews
GET /products·GET /product/:idPUT /admin/update/product/:id(admin) ·GET /admin/products(admin)POST /review·GET /reviews·DELETE /review/:reviewIdPOST /:id/summerize-reviews(admin — AI review summary)
Wishlist
GET /wishlist·POST /wishlist/:id·DELETE /wishlist/:id
Orders
POST /order/new·GET /orders/me·GET /order/:idPOST /order/:id/return·POST /order/reorder/:orderIdGET /admin/orders·GET /admin/returns·GET /admin/refunds(admin)
Payments, coupons & membership
POST /paymentPOST /coupon(admin) ·GET /coupons/all- Cashfree order & membership endpoints (see
routes/payment.js,routes/subscription.js)
Admin analytics
GET /admin/analytics?range=7d|30d|90d|12m|all·GET /admin/stats
Scheduled jobs (require x-cron-secret header)
POST /jobs/newsletter·POST /jobs/wishlist
Docs & health
GET /api/v1/health·GET /api-docs(Swagger UI) ·GET /api-docs.json
Uploads use Multer memory storage before writing to AWS S3 (PNG/JPEG/WebP). Product pages and order pages open a Socket.io connection to the backend; product/review/summary events and order‑status changes update the UI in real time. Set REACT_APP_SOCKET_URL to the backend origin for any non‑default setup.
docker-compose up --buildStarts the app, MongoDB, and Redis. The app is exposed on host port 4001 (container 4000).
The backend serves the compiled React app, so the whole thing deploys as one service — no separate frontend host, no CORS/cross‑site‑cookie setup.
Render (Docker):
- The
Dockerfileinstalls backend deps, builds the frontend, and runsnode backend/server.jswithNODE_ENV=production(which turns on static serving offrontend/build). - Set the environment variables above (
DB_URI,JWT_SECRET_KEY,FRONTEND_URL, SMTP, Cashfree, AWS, Upstash,CRON_SECRET, …). - Any
REACT_APP_*the client needs are inlined at build time — pass them as Docker build args (see theARGs in theDockerfile). Same‑origin meansREACT_APP_SOCKET_URLcan be left unset (the client defaults to the current origin in production).
Scheduled jobs: add repo secrets BACKEND_URL and CRON_SECRET so .github/workflows/cron.yml can trigger the email jobs (see Background jobs). On a free tier that sleeps, this is what keeps them running.
Any Node‑friendly host works (Render, Railway, Fly.io, a VPS). On free tiers the instance may cold‑start after idle; the client’s BackendWaker masks the first‑request delay.
- Fork the repo
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit (
git commit -m 'Add AmazingFeature') - Push (
git push origin feature/AmazingFeature) - Open a Pull Request
Apache License 2.0 — see LICENSE.
Anant Duhan — GitHub @AnantDuhan · LinkedIn @AnantDuhan
⭐ If you found this project helpful, please give it a star!