A full-featured MERN Stack (MongoDB, Express.js, React.js, Node.js) e-commerce application for a pharmacy and healthcare store.
- Features
- Tech Stack
- Project Structure
- Prerequisites
- Installation & Setup
- Environment Variables
- Seeding Sample Data
- Running the Application
- API Documentation
- Database Schema
- Application Modules
- Admin Dashboard
- Deployment
- π User registration, login, and JWT authentication
- ποΈ Browse products by categories and subcategories
- π Full-text search with filters (category, price, prescription required)
- π Shopping cart with quantity management
- β€οΈ Wishlist management
- π³ Multi-step checkout with address selection
- π Prescription upload and verification workflow
- π¦ Order tracking with real-time status updates
- β Product reviews and ratings system
- π Loyalty points system (1 point per βΉ10 spent)
- π Multiple delivery addresses management
- π€ Personal profile management
- π Analytics dashboard with revenue charts
- π¦ Complete order management with status updates
- π Product catalogue management (CRUD)
- π₯ User account management
- π Prescription review and approval workflow
- π Inventory monitoring with low-stock alerts
- π Sales and revenue reporting
- Prescription (Rx) required flag on medicines
- Salt/generic name tracking
- Dosage form and strength information
- Side effects and usage instructions
- Batch number and expiry date management
- Prescription upload and pharmacist review workflow
- Medicine categories: OTC, Prescription, Vitamins, Medical Devices, Baby Care, etc.
| Layer | Technology |
|---|---|
| Frontend | React.js 18, React Router v6, Context API |
| Backend | Node.js, Express.js |
| Database | MongoDB with Mongoose ODM |
| Authentication | JWT (JSON Web Tokens) + bcryptjs |
| File Upload | Multer |
| Styling | Custom CSS with CSS Variables |
| HTTP Client | Axios |
| Icons | React Icons (Feather Icons) |
| Toast Notifications | React Hot Toast |
| Build Tool | Vite |
pharmaez/
βββ server/ # Backend (Node.js + Express)
β βββ config/
β β βββ db.js # MongoDB connection
β βββ controllers/
β β βββ authController.js # Auth, profile, addresses, prescriptions
β β βββ productController.js# Products, reviews
β β βββ cartController.js # Shopping cart
β β βββ orderController.js # Orders, admin stats
β β βββ adminController.js # Admin: users, prescriptions, config
β βββ middleware/
β β βββ auth.js # JWT protect + adminOnly + generateToken
β β βββ errorHandler.js # Global error handler + 404
β βββ models/
β β βββ User.js # User schema (addresses, prescriptions, wishlist)
β β βββ Product.js # Product schema (medical fields, reviews)
β β βββ Cart.js # Cart schema
β β βββ Order.js # Order schema (status history, prescription)
β β βββ AdminConfig.js # Banners, settings
β βββ routes/
β β βββ authRoutes.js
β β βββ productRoutes.js
β β βββ cartRoutes.js
β β βββ orderRoutes.js
β β βββ adminRoutes.js
β βββ utils/
β β βββ seeder.js # Database seeder
β βββ uploads/ # Uploaded prescription files
β βββ .env.example
β βββ index.js # Express server entry point
β βββ package.json
β
βββ client/ # Frontend (React.js + Vite)
βββ src/
β βββ assets/styles/
β β βββ global.css # Global styles, utilities, CSS variables
β β βββ components.css # Shared component styles
β βββ components/
β β βββ admin/
β β β βββ AdminLayout.jsx # Admin sidebar layout
β β β βββ AdminLayout.css
β β βββ common/
β β β βββ ProductCard.jsx # Reusable product card
β β β βββ ProductCard.css
β β βββ layout/
β β βββ Navbar.jsx # Responsive navigation + search
β β βββ Navbar.css
β β βββ Footer.jsx
β β βββ Footer.css
β βββ context/
β β βββ AuthContext.jsx # Global auth state
β β βββ CartContext.jsx # Global cart state
β βββ pages/
β β βββ admin/
β β β βββ AdminDashboard.jsx
β β β βββ AdminProducts.jsx
β β β βββ AdminProductForm.jsx
β β β βββ AdminOrders.jsx
β β β βββ AdminUsers.jsx
β β β βββ AdminInventory.jsx
β β β βββ AdminPrescriptions.jsx
β β β βββ AdminPages.css
β β βββ auth/
β β β βββ AuthPages.jsx (LoginPage + RegisterPage)
β β β βββ LoginPage.jsx
β β β βββ RegisterPage.jsx
β β β βββ AuthPages.css
β β βββ user/
β β β βββ UserDashboard.jsx
β β β βββ OrdersPage.jsx
β β β βββ OrderDetailPage.jsx
β β β βββ WishlistPage.jsx
β β β βββ PrescriptionsPage.jsx
β β β βββ ProfilePage.jsx
β β β βββ UserPages.css
β β βββ HomePage.jsx # Hero, categories, featured
β β βββ HomePage.css
β β βββ ProductsPage.jsx # Listing with filters/pagination
β β βββ ProductsPage.css
β β βββ ProductDetailPage.jsx # Detail, tabs, reviews
β β βββ ProductDetailPage.css
β β βββ CartPage.jsx
β β βββ CartPage.css
β β βββ CheckoutPage.jsx # 3-step checkout
β β βββ CheckoutPage.css
β β βββ OrderConfirmationPage.jsx
β βββ utils/
β β βββ api.js # Axios instance with interceptors
β βββ App.jsx # Routes + providers
β βββ main.jsx
βββ index.html
βββ vite.config.js
βββ package.json
- Node.js v18 or higher
- MongoDB v6 or higher (local or MongoDB Atlas)
- npm or yarn
cd pharmaezcd server
npm installCreate your environment file:
cp .env.example .envEdit .env with your values (see Environment Variables).
cd ../client
npm installCreate server/.env with these values:
PORT=8000
MONGO_URI=mongodb://localhost:27017/pharmaez
JWT_SECRET=pharmaez_super_secret_jwt_key_change_in_production
JWT_EXPIRES_IN=7d
NODE_ENV=developmentMONGO_URI=mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/pharmaez?retryWrites=true&w=majorityAfter setting up your .env, run the seeder to populate the database with:
- 1 Admin user
- 1 Sample customer
- 12 Healthcare products across all categories
cd server
npm run seedDefault login credentials after seeding:
| Role | Password | |
|---|---|---|
| Admin | admin@pharmaez.com | admin123 |
| User | raj@example.com | user123 |
Terminal 1 β Backend:
cd server
npm run dev # Uses nodemon for hot-reload
# Server starts at http://localhost:8000Terminal 2 β Frontend:
cd client
npm run dev # Vite dev server with HMR
# App starts at http://localhost:5173Open your browser at http://localhost:5173
# Build frontend
cd client
npm run build
# dist/ folder is created
# Start backend in production
cd ../server
NODE_ENV=production npm startBase URL: http://localhost:8000/api
| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /auth/register |
Public | Register new user |
| POST | /auth/login |
Public | Login and get JWT token |
| GET | /auth/profile |
Private | Get user profile + wishlist |
| PUT | /auth/profile |
Private | Update profile |
| PUT | /auth/change-password |
Private | Change password |
| POST | /auth/address |
Private | Add delivery address |
| DELETE | /auth/address/:id |
Private | Delete delivery address |
| POST | /auth/wishlist/:productId |
Private | Toggle product in wishlist |
| POST | /auth/prescription |
Private | Upload prescription (multipart) |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /products |
Public | List with filters: keyword, category, subcategory, minPrice, maxPrice, prescriptionRequired, sort, page, limit |
| GET | /products/featured |
Public | Featured products |
| GET | /products/category/:category |
Public | Products by category |
| GET | /products/:id |
Public | Single product |
| POST | /products/:id/reviews |
Private | Add review |
| POST | /products |
Admin | Create product |
| PUT | /products/:id |
Admin | Update product |
| DELETE | /products/:id |
Admin | Delete product |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /cart |
Private | Get user's cart |
| POST | /cart |
Private | Add item { productId, quantity } |
| PUT | /cart/:itemId |
Private | Update item quantity |
| DELETE | /cart/:itemId |
Private | Remove item |
| DELETE | /cart |
Private | Clear entire cart |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| POST | /orders |
Private | Place order |
| GET | /orders/my |
Private | Get user's orders |
| GET | /orders/:id |
Private | Get order by ID |
| PUT | /orders/:id/cancel |
Private | Cancel order |
| GET | /orders/admin/stats |
Admin | Dashboard statistics |
| GET | /orders/admin/all |
Admin | All orders with filters |
| PUT | /orders/admin/:id/status |
Admin | Update order status |
| Method | Endpoint | Access | Description |
|---|---|---|---|
| GET | /admin/users |
Admin | All users with pagination |
| PUT | /admin/users/:id/toggle |
Admin | Toggle user active status |
| GET | /admin/prescriptions |
Admin | All uploaded prescriptions |
| PUT | /admin/prescriptions/:userId/:prescriptionId |
Admin | Approve/reject prescription |
| GET | /admin/config |
Public | Get store configuration |
| PUT | /admin/config |
Admin | Update store configuration |
| GET | /admin/inventory |
Admin | Inventory report |
GET /api/health
name, email (unique), password (hashed), phone, dateOfBirth, gender
addresses: [{ fullName, phone, street, city, state, pincode, isDefault }]
prescriptions: [{ fileName, fileUrl, uploadedAt, status, doctorName, notes }]
wishlist: [ObjectId β Product]
isActive, role (user|admin), loyaltyPoints
name, genericName, description, manufacturer
category (enum: 10 categories), subcategory
images: [String], price, discountPrice, discountPercent
stock, unit, packSize, prescriptionRequired
saltComposition, dosageForm, strength
sideEffects: [String], usageInstructions, storageInstructions
expiryDate, batchNumber, isAvailable, isFeatured
reviews: [{ user, name, rating, comment }]
rating, numReviews, tags: [String]
user: ObjectId β User
items: [{ product, name, image, price, discountPrice, quantity, prescriptionRequired, packSize }]
couponCode, discount
virtuals: totalItems, subtotal
user: ObjectId β User
orderNumber (auto-generated, e.g. PEZ7829430001)
items: [{ product, name, image, price, quantity, packSize, prescriptionRequired }]
shippingAddress: { fullName, phone, street, city, state, pincode }
paymentMethod (cod|upi|card|netbanking|wallet)
paymentStatus (pending|paid|failed|refunded)
itemsPrice, shippingPrice, taxPrice, discountAmount, totalPrice
orderStatus (placed|confirmed|processing|shipped|delivered|cancelled|returned)
prescriptionStatus (not_required|pending|approved|rejected)
statusHistory: [{ status, timestamp, note }]
estimatedDelivery, deliveredAt, notes, couponCode
- JWT-based stateless authentication
- Role-based access control (user / admin)
- Protected and admin-only routes
- Password hashing with bcryptjs (salt rounds: 10)
- 10 healthcare categories
- Full-text search across name, genericName, and tags
- Advanced filtering (price range, category, prescription required)
- Sort by price, rating, newest, relevance
- Pagination (12 per page by default)
- Product reviews with rating aggregation
- Customer uploads prescription (image/PDF)
- System flags order as "prescription pending"
- Admin reviews prescription in dashboard
- Admin approves or rejects with notes
- Order proceeds or is cancelled
- Real-time stock validation
- Prevents overselling
- Free shipping threshold (βΉ499)
- GST calculation (5%)
- 3-step checkout: Address β Payment β Review
- Multiple saved addresses
- Auto-generated order numbers (PEZ + timestamp + sequence)
- Status progression: placed β confirmed β processing β shipped β delivered
- Status history audit trail
- Stock auto-deduction on order placement
- Stock restoration on cancellation
- Loyalty points awarded automatically (1 point per βΉ10)
Access at /admin after logging in with admin credentials.
| Page | URL | Description |
|---|---|---|
| Dashboard | /admin |
KPIs, revenue chart, recent orders, low stock |
| Products | /admin/products |
List, search, edit, delete products |
| Add/Edit Product | /admin/products/new |
Full product creation form |
| Orders | /admin/orders |
All orders, status updates, detail panel |
| Users | /admin/users |
User list, activate/deactivate |
| Inventory | /admin/inventory |
Stock overview, low-stock alerts |
| Prescriptions | /admin/prescriptions |
Review, approve, reject prescriptions |
- Set environment variables on your platform
- Set
MONGO_URIto your MongoDB Atlas connection string - Set
NODE_ENV=production - Start command:
node index.js
- Build:
npm run build - Publish directory:
dist - Set the
VITE_API_URLif you move away from the proxy approach
Build the React app and serve it from Express:
// Add to server/index.js for production
if (process.env.NODE_ENV === 'production') {
const path = require('path');
app.use(express.static(path.join(__dirname, '../client/dist')));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, '../client/dist/index.html'));
});
}- Change
JWT_SECRETto a strong random string in production - Enable HTTPS in production
- Configure CORS to only allow your frontend domain
- Add rate limiting (express-rate-limit) for production
- Validate and sanitize all inputs
- Never commit
.envto version control
This project is for educational purposes. Built with the MERN stack.
- React.js, Node.js, Express.js, MongoDB, Mongoose
- React Router v6, React Hot Toast, React Icons
- Axios, Multer, bcryptjs, jsonwebtoken
- Vite (build tool)