A full-stack e-commerce marketplace built on a modern JavaScript stack - multi-role authentication, a product catalog with rich client-side filtering, and a clean feature-sliced frontend architecture.
Threadora is a production-oriented full-stack application with a clear separation between Buyer and Seller roles. Sellers can list products with cloud-hosted images; Buyers browse a polished catalog with real-time filtering, color-swatch selection, and wishlist interactions - all gated behind JWT-authenticated, role-enforced API endpoints.
Key engineering highlights:
- Role-based access control -
authSellermiddleware gates every seller-only route - Multi-strategy auth - email/phone + password and Google OAuth 2.0 (Passport.js, stateless/session-less)
- Feature-sliced frontend - each domain (auth, products, shared) is a self-contained module with its own pages, hooks, services, states, and styles
- Redux Toolkit auth state -
setUser / setLoading / setErrorpattern with auseAuthcustom hook as the single integration surface - ImageKit cloud storage - products support an
images[]array backed by CDN delivery - Global error pipeline -
notFoundHandler+errorHandlermiddleware with no sensitive data leakage
Node.js + Express 5.2.1
├── Authentication
│ ├── JWT (jsonwebtoken) - stateless, HTTP-only cookies
│ ├── Passport.js - Google OAuth 2.0 (session: false)
│ └── bcryptjs - pre-save hook hashing with 10 salt rounds
├── Database
│ └── MongoDB + Mongoose 9.6.3
├── File Management
│ ├── Multer - multipart/form-data parsing
│ └── ImageKit - CDN-backed cloud image storage
├── Validation & Security
│ ├── express-validator - schema-level request validation
│ ├── CORS - configured for trusted origins
│ └── cookie-parser - HTTP-only cookie support
└── Observability
└── Morgan - HTTP request logging (dev format)
React 19.0 + Vite
├── State Management
│ └── Redux Toolkit — authSlice (user, loading, error)
├── Routing
│ └── React Router v7 — createBrowserRouter
├── Styling
│ └── Tailwind CSS v4 + scoped feature CSS
├── HTTP Client
│ └── Axios — centralised auth API service
└── UI/UX
├── React Toastify — toast notifications
├── Inline SVG icon system — zero external icon dependency
└── Color swatch selector, wishlist toggle, filter chips
Threadora/
├── Backend/
│ ├── server.js # Entry point — starts HTTP server
│ ├── src/
│ │ ├── app.js # Express app — middleware, routes, Passport bootstrap
│ │ ├── config/ # Centralised env config (config.js)
│ │ ├── controllers/
│ │ │ ├── auth.controller.js # signup, login, google, getUser, logout
│ │ │ └── product.controller.js # createProduct, getProducts
│ │ ├── middlewares/
│ │ │ ├── auth.middleware.js # authMiddleware (JWT) + authSeller (role guard)
│ │ │ ├── validate.middleware.js # express-validator error formatter
│ │ │ ├── error.middleware.js # Global error handler
│ │ │ └── notFound.middleware.js # 404 catch-all
│ │ ├── models/
│ │ │ ├── user.model.js # User schema — roles: buyer | seller, optional googleId
│ │ │ └── product.model.js # Product schema — images[], price{amount,currency}, stock
│ │ ├── routes/
│ │ │ ├── auth.route.js # /api/auth — signup, login, google, me, logout
│ │ │ └── product.route.js # /api/products — seller-gated CRUD
│ │ ├── services/ # Business logic decoupled from controllers
│ │ ├── validators/
│ │ │ ├── auth.validator.js # signupValidator, loginValidator
│ │ │ └── product.validator.js # createProductValidator
│ │ └── utils/ # asyncHandler wrapper
│ └── package.json
│
└── Frontend/
├── src/
│ ├── main.jsx # React DOM entry — Redux Provider + RouterProvider
│ ├── app/
│ │ ├── App.jsx # Root — RouterProvider wrapper
│ │ ├── app.routes.jsx # Route definitions: /, /login, /signup
│ │ ├── app.store.js # Redux store
│ │ └── index.css # Global resets + design tokens
│ └── features/
│ ├── auth/ # Auth feature module
│ │ ├── components/
│ │ │ ├── GoogleButton.jsx # OAuth trigger button
│ │ │ └── Protected.jsx # Route guard component
│ │ ├── hooks/
│ │ │ └── useAuth.js # loginUser, signupUser, logoutUser
│ │ ├── pages/
│ │ │ ├── Login.jsx # Email/phone + password form
│ │ │ └── Signup.jsx # Multi-step signup with role selection
│ │ ├── services/
│ │ │ └── auth.api.js # Axios auth endpoints
│ │ ├── states/
│ │ │ └── auth.slice.js # Redux slice — user, loading, error
│ │ └── styles/
│ │ └── auth.css # Scoped auth UI styles
│ ├── products/ # Products feature module
│ │ ├── pages/
│ │ │ └── Home.jsx # Product catalog — filtering, swatches, wishlist
│ │ └── styles/
│ │ └── products.css # Scoped product UI styles
│ └── shared/ # Cross-feature components
│ ├── components/
│ │ ├── Header.jsx # Top nav — logo, links, mobile menu
│ │ └── Footer.jsx # Site footer
│ └── styles/
└── vite.config.js
- ✅ JWT issued on login/signup, stored in HTTP-only cookies
- ✅ Google OAuth 2.0 — Passport strategy, session-less (
session: false) - ✅ bcryptjs pre-save hook — passwords hashed before hitting the DB
- ✅
authMiddleware— validates JWT, attachesreq.userto the request - ✅
authSeller— role guard; rejects non-seller requests with 403 - ✅
GET /api/auth/me— hydrates the Redux store on app load - ✅
POST /api/auth/logout— clears the JWT cookie server-side
- ✅ Buyer (default) — browse catalog, wishlist items
- ✅ Seller — create and manage product listings
- ✅ Role selection at signup via an accessible card-picker UI
- ✅
phoneandpasswordfields conditionally required — skipped for OAuth users
- ✅ Product grid with full-bleed imagery
- ✅ Badge overlays — Just In, Member Exclusive, Sale, Coming Soon
- ✅ Color swatch selector (up to 5 visible, overflow count shown)
- ✅ Sale price with percentage-off badge calculated dynamically
- ✅ Wishlist toggle (heart icon) per card
- ✅ Filter chips — All / New & Featured / Apparel / Accessories / Sale
- ✅ Responsive navigation with mobile hamburger menu and slide-in drawer
- ✅ Inline SVG icon system — no external icon library
- ✅
POST /api/products— create product (seller only); supportsimages[], multi-currencyprice - ✅
GET /api/products— fetch authenticated seller's own listings - ✅
name,description,category,stock,price.amount— all validated server-side - ✅ ImageKit integration for cloud-hosted product images
- ✅ Feature-sliced directory structure — auth, products, shared are fully independent modules
- ✅
useAuthhook — single integration point for all auth mutations - ✅
useSelector/ Redux Toolkit —loadinganderrorstates drive UI feedback - ✅ Client-side field validation on Login and Signup before hitting the API
- ✅
Protected.jsx— declarative route guard for authenticated-only pages
- Node.js v18+
- MongoDB (local or Atlas)
- Google OAuth 2.0 credentials
- ImageKit account
cd Backend
npm installCreate a .env file:
PORT=5000
MONGODB_URI=your_mongodb_connection_string
JWT_SECRET=your_jwt_secret_key
# Google OAuth
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
# ImageKit
IMAGEKIT_PUBLIC_KEY=your_imagekit_public_key
IMAGEKIT_PRIVATE_KEY=your_imagekit_private_key
IMAGEKIT_URL_ENDPOINT=your_imagekit_url_endpointnpm run dev # Development with hot-reload (nodemon)
npm start # Production servercd Frontend
npm install
npm run devOpen http://localhost:5173
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/signup |
Public | Register with fullName, email, phone, password, role |
POST |
/login |
Public | Login with credential (email or phone) + password |
GET |
/google |
Public | Initiate Google OAuth flow |
GET |
/google/callback |
Public | OAuth callback — issues JWT cookie and redirects |
GET |
/me |
Private | Returns the authenticated user from JWT cookie |
POST |
/logout |
Private | Clears the JWT cookie server-side |
All product routes require a valid JWT and
role: seller.
| Method | Endpoint | Access | Description |
|---|---|---|---|
POST |
/ |
Seller | Create a product listing |
GET |
/ |
Seller | Fetch the authenticated seller's products |
| Method | Endpoint | Description |
|---|---|---|
GET |
/health |
Returns { status: 'OK' } — liveness probe |
| Concern | Approach |
|---|---|
| Auth tokens | HTTP-only cookies — inaccessible to JS |
| Password storage | bcryptjs — 10 salt rounds, hashed in Mongoose pre-save hook |
| Input validation | express-validator on every mutating route |
| Role enforcement | authSeller middleware — 403 on role mismatch |
| OAuth | Passport.js session-less — no server-side session store required |
| Error responses | Global errorHandler — never exposes stack traces in production |
| Secrets | All credentials via .env — never committed |
| Package | Version | Purpose |
|---|---|---|
express |
5.2.1 | Web framework |
mongoose |
9.6.3 | MongoDB ODM |
passport-google-oauth20 |
— | Google OAuth strategy |
jsonwebtoken |
— | JWT sign / verify |
bcryptjs |
— | Password hashing |
express-validator |
— | Request validation |
imagekit |
— | Cloud image upload |
morgan |
— | HTTP request logging |
cookie-parser |
— | Cookie parsing |
| Package | Version | Purpose |
|---|---|---|
react |
19.0 | UI library |
react-router-dom |
v7 | Client-side routing |
@reduxjs/toolkit |
— | State management |
axios |
— | HTTP client |
react-toastify |
— | Toast notifications |
vite |
— | Build tooling |
npm run dev # Development with hot-reload (nodemon)
npm run start # Production servernpm run dev # Vite dev server
npm run build # Production bundle
npm run lint # ESLint
npm run preview # Preview production build locally- Buyer-facing product browsing connected to live API data
- Seller dashboard — full CRUD UI for product management
- Product detail page with image gallery
- Cart and checkout flow
- Pagination / infinite scroll on the product catalog
- Image upload UI wired to ImageKit
- Admin role — platform-wide oversight
- Fork the repo and create a feature branch (
git checkout -b feat/your-feature) - Commit using Conventional Commits (
feat:,fix:,docs:, etc.) - Open a pull request with a clear description of the change
ISC License — see the LICENSE file for details.
Built with Node.js · React · MongoDB · ImageKit