A full-stack food delivery platform — discover restaurants, order food, track deliveries in real time
- Overview
- Live Features
- Tech Stack
- System Architecture
- Project Structure
- Database Design
- API Reference
- Key Modules
- Security
- Getting Started
- Environment Variables
- Scripts
- Author
QuickEats is a production-ready, full-stack food delivery web application built with the MERN stack. It supports three distinct user roles — Customer, Restaurant Owner, and Delivery Partner — each with a dedicated dashboard, tailored UI, and role-protected API access.
The platform features real-time geolocation-based restaurant discovery, a multi-restaurant shopping cart, Razorpay payment integration, OTP-secured deliveries, and transactional email notifications powered by Brevo.
| Feature | Description |
|---|---|
| Google OAuth + Email Auth | Sign up / sign in with Google (Firebase) or email + password |
| Password Reset via OTP | Secure 6-digit OTP sent to email (15-min expiry) |
| Geolocation Discovery | GPS-based nearby restaurant listing with adjustable radius (km) |
| Interactive Map | View restaurant locations on a Leaflet map |
| Multi-Restaurant Cart | Add items from multiple restaurants into a single persisted cart |
| Checkout | Review order by restaurant, select delivery address with map picker |
| Razorpay Payments | Inline online payment or Cash-on-Delivery |
| Order Tracking | Live order status (Pending → Preparing → Out for Delivery → Delivered) |
| Delivery Boy Location | Track the delivery partner's live GPS location on a map |
| OTP Delivery Confirmation | Confirm receipt with a one-time code sent to the delivery partner |
| Order History | Full history with per-restaurant breakdowns |
| Dark / Light Mode | Persistent theme preference via Redux + localStorage |
| Feature | Description |
|---|---|
| Restaurant Registration | Create a restaurant with cover photo (Cloudinary upload), cuisine, address, map picker |
| Menu Management | Add, edit, or delete menu items with categories and food type (Veg / Non-Veg) |
| Order Dashboard | See all incoming orders, update status in real time |
| Delivery Assignment | Notify nearby delivery partners about new orders |
| Restaurant Overview | See rating, item count, and open/closed status at a glance |
| Feature | Description |
|---|---|
| Assignment Dashboard | View available delivery assignments broadcast by restaurant owners |
| Accept / Decline | Choose which deliveries to take on |
| OTP-Verified Delivery | Receive OTP via email; customer enters OTP to confirm delivery |
| Delivery History | Track completed assignments and stats |
| Technology | Version | Purpose |
|---|---|---|
| React | 19.x | Core UI library |
| Vite | 7.x | Build tool & dev server |
| Tailwind CSS | 4.x | Utility-first styling (via @tailwindcss/vite) |
| Redux Toolkit | 2.x | Global state management |
| redux-persist | 6.x | Persist state to localStorage (cart, theme, user) |
| React Router DOM | 7.x | Client-side routing |
| Axios | 1.x | HTTP client (cookie-based auth) |
| Firebase | 12.x | Google OAuth authentication |
| Leaflet + react-leaflet | 1.9 / 5.x | Interactive maps & geolocation |
| Lucide React | 0.56x | Icon library |
| React Icons | 5.x | Extended icon set |
| React Toastify | 11.x | Toast notifications |
| React Spinners | 0.17x | Loading indicators |
| reactjs-popup | 2.x | Modal & popup components |
| Technology | Version | Purpose |
|---|---|---|
| Node.js | 22.x | Runtime environment |
| Express | 5.x | REST API framework |
| Mongoose | 9.x | MongoDB ODM with schema validation |
| MongoDB Atlas | — | Primary database (geospatial 2dsphere indexes) |
| Redis | 5.x | Caching layer (two separate instances) |
| bcryptjs | 3.x | Password hashing |
| jsonwebtoken | 9.x | JWT authentication (HttpOnly cookies) |
| Multer | 2.x | Multipart file upload handling |
| Cloudinary | 1.x | Cloud image storage & CDN |
| Razorpay | 2.x | Payment gateway integration |
| Brevo SMTP API (via Axios) | — | Transactional email (OTP + delivery notifications) |
| express-rate-limit | 8.x | API rate limiting |
| cookie-parser | 1.x | Parse HttpOnly cookie tokens |
| cors | 2.x | Cross-Origin Resource Sharing |
| dotenv | 17.x | Environment variable management |
| Tool | Purpose |
|---|---|
| concurrently | Run client & server together in development |
| nodemon | Auto-restart server on file changes |
| ESLint | Code linting (react-hooks + react-refresh plugins) |
| npm Workspaces | Monorepo management (single npm install at root) |
┌──────────────────────────────────────────────────────────────────────────┐
│ CLIENT (React + Vite) │
│ │
│ ┌────────────┐ ┌──────────────┐ ┌─────────────────────────────┐ │
│ │ Firebase │ │ Redux Store │ │ React Router DOM (SPA) │ │
│ │ Google Auth│ │ (Persisted) │ │ / /signup /signin │ │
│ └─────┬──────┘ └──────┬───────┘ │ /checkout /my-orders │ │
│ │ │ │ /profile /404 │ │
│ └────────┬─────────┘ └─────────────────────────────┘ │
│ │ │
│ Axios (withCredentials: true) │
└─────────────────┼────────────────────────────────────────────────────────┘
│ HTTPS (HttpOnly Cookie JWT)
┌─────────────────▼────────────────────────────────────────────────────────┐
│ SERVER (Express 5 + Node 22) │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Middleware Stack │ │
│ │ CORS → JSON Parser → URL Encoder → Cookie Parser │ │
│ │ → Rate Limiters → isAuth (JWT) → authorizeRoles │ │
│ └──────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌──────────┬───────────┼──────────┬──────────┬─────────────┐ │
│ │ /auth │ /user │ /restaurants│ /items│ /orders │ │
│ └────┬─────┴─────┬─────┴─────┬─────┴────┬────┴──────┬──────┘ │
│ │ │ │ │ │ │
│ ┌────▼───────────▼───────────▼──────────▼───────────▼──────┐ │
│ │ Controllers │ │
│ │ auth | user | restaurant | item | order │ │
│ └────────────────────────────────────────────────────────┬─┘ │
│ │ │
│ ┌───────────────────────────────┐ ┌────────────────────▼──────────┐ │
│ │ External Services │ │ Data Layer │ │
│ │ Brevo (Email) │ │ MongoDB Atlas (Primary DB) │ │
│ │ Cloudinary (Images) │ │ Redis (Cache — 2 instances) │ │
│ │ Razorpay (Payments) │ │ Mongoose ODM │ │
│ │ Nominatim (Geocoding) │ └───────────────────────────────┘ │
│ └───────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
User selects items
│
▼
Cart (Redux + localStorage)
│
▼
/checkout ── map picker for delivery address
│
├─ [Online] ──► POST /api/orders/create ──► Razorpay order created
│ ──► Client opens Razorpay modal
│ ──► POST /api/orders/verify-payment (HMAC verified)
│
└─ [COD] ──► POST /api/orders/create ──► Order saved as "Pending"
│
▼
Owner Dashboard shows order
│
▼
Owner changes status ──► "Preparing"
│
▼
POST /api/orders/delivery/notify
(Brevo email sent with OTP to delivery boy)
│
▼
Delivery boy accepts
PUT /api/orders/delivery/:id/accept
Status ──► "Out for Delivery"
│
▼
Customer verifies OTP
POST /api/orders/verify-otp/:id
Status ──► "Delivered"
quick-eats/ ← npm workspace root
├── package.json ← Workspace config + concurrently
├── package-lock.json
│
├── client/ ← React + Vite frontend
│ ├── firebase.js ← Firebase Google Auth config
│ ├── vite.config.js ← Vite + Tailwind + React plugin
│ ├── index.html
│ └── src/
│ ├── main.jsx ← Entry: Provider + PersistGate + BrowserRouter
│ ├── App.jsx ← Route definitions + auth guards
│ ├── index.css ← Global styles + theme variables
│ │
│ ├── pages/
│ │ ├── Home.jsx ← User home (restaurant discovery)
│ │ ├── Guest.jsx ← Landing page for unauthenticated users
│ │ ├── SignUp.jsx ← Registration (email + Google)
│ │ ├── SignIn.jsx ← Login flow
│ │ ├── ResetPassword.jsx ← 4-step OTP password reset wizard
│ │ ├── Checkout.jsx ← Cart review + address + payment
│ │ ├── MyOrders.jsx ← Order history + live tracking
│ │ └── PageNotFound.jsx ← 404 page
│ │
│ ├── components/
│ │ ├── General/ ← Shared across all roles
│ │ │ ├── FoodCard.jsx
│ │ │ ├── MapPicker.jsx ← Leaflet map for address selection
│ │ │ ├── Footer.jsx
│ │ │ ├── Loader.jsx
│ │ │ └── ErrorComponent.jsx
│ │ │
│ │ ├── User/ ← Customer-specific components
│ │ │ ├── UserHeader.jsx
│ │ │ ├── UserMobileMenu.jsx
│ │ │ ├── UserProfileDropdown.jsx
│ │ │ ├── RestaurantCard.jsx
│ │ │ ├── CartItem.jsx
│ │ │ ├── OrderSummary.jsx
│ │ │ ├── SearchBar.jsx
│ │ │ ├── SearchedItems.jsx
│ │ │ ├── ShowOnMap.jsx ← Live delivery tracking on Leaflet
│ │ │ └── SampleItems.jsx
│ │ │
│ │ ├── Owner/ ← Restaurant owner components
│ │ │ ├── OwnerHeader.jsx
│ │ │ ├── OwnerMobileMenu.jsx
│ │ │ ├── OwnerProfileDropdown.jsx
│ │ │ ├── AddRestaurantForm.jsx
│ │ │ ├── AddItemForm.jsx
│ │ │ ├── ManageRestaurant.jsx
│ │ │ └── RestaurantOverview.jsx
│ │ │
│ │ ├── DeliveryBoy/ ← Delivery partner components
│ │ │ ├── Header.jsx
│ │ │ ├── MobileMenu.jsx
│ │ │ ├── ProfileDropdown.jsx
│ │ │ ├── AssignmentCard.jsx
│ │ │ └── Stats.jsx
│ │ │
│ │ ├── Guest/
│ │ │ └── GuestHeader.jsx
│ │ │
│ │ ├── Profile.jsx ← Shared profile management (all roles)
│ │ ├── UserDashboard.jsx ← Customer main dashboard
│ │ ├── OwnerDashboard.jsx ← Owner main dashboard
│ │ └── DeliveryBoyDashboard.jsx
│ │
│ ├── hooks/
│ │ ├── useNearbyRestaurants.jsx ← GPS + pagination + 60s cache
│ │ ├── useGetCity.jsx ← Reverse geocoding on mount
│ │ ├── useGetItems.jsx ← Fetch menu items for a restaurant
│ │ ├── useGetMyRestaurant.jsx ← Owner's restaurant data
│ │ ├── useSearchItems.jsx ← Full-text item search
│ │ └── useLonLat.jsx ← Coordinate utilities
│ │
│ └── redux/
│ ├── store.js ← Configured store + redux-persist
│ └── slices/
│ ├── userSlice.js ← Auth, cart, coords, restaurants
│ ├── ownerSlice.js ← Owner restaurant state
│ └── themeSlice.js ← Dark/light mode
│
└── server/ ← Express REST API
├── index.js ← Bootstrap: middleware, routes, static serve
├── config/
│ ├── env.js ← Validated env loader (throws on missing keys)
│ ├── db.js ← MongoDB connection (Cloudflare DNS fallback)
│ └── filePath.js ← __dirname equivalent for ESM
│
├── models/
│ ├── user.model.js ← User (roles, OTP fields, 2dsphere location)
│ ├── restaurant.model.js ← Restaurant (GeoJSON Point, rating, indexes)
│ ├── restaurantProfile.model.js ← 1:1 extended profile (image, cuisine, items)
│ ├── item.model.js ← Menu item (full-text index, category enum)
│ ├── order.model.js ← Order (multi-restaurant, Razorpay fields)
│ └── deliveryAssignment.model.js ← Assignment (broadcast, OTP, status)
│
├── routes/
│ ├── auth.routes.js ← /api/auth/*
│ ├── user.routes.js ← /api/user/*
│ ├── restaurant.routes.js ← /api/restaurants/*
│ ├── item.routes.js ← /api/items/*
│ └── order.routes.js ← /api/orders/*
│
├── controllers/
│ ├── auth.controllers.js ← signUp/signIn/signOut/googleAuth/OTP flow
│ ├── user.controllers.js ← Profile, city lookup, location update
│ ├── restaurant.controllers.js ← CRUD + geo queries + Nominatim geocoding
│ ├── item.controllers.js ← CRUD + full-text search + sample items
│ └── order.controllers.js ← Place order, Razorpay, delivery lifecycle
│
├── middlewares/
│ ├── auth.js ← isAuth (JWT cookie) + authorizeRoles
│ ├── multer.js ← Local disk temp storage for Cloudinary
│ └── rateLimiter.js ← Three rate limiters (auth / location / reset)
│
└── utils/
├── cloudinary.js ← Upload + local cleanup helper
├── mail.js ← Brevo HTML email (OTP + delivery notify)
├── otpToken.js ← Crypto-secure OTP generator
├── token.js ← JWT sign helper
├── redisClient.js ← Primary Redis (user/session cache)
└── redisClientRestaurant.js ← Secondary Redis (restaurant cache)
QuickEats uses MongoDB Atlas with Mongoose. All models include timestamps: true.
┌──────────────┐ ┌───────────────────────────┐
│ User │ 1 ─── N │ Order │
│──────────────│ │───────────────────────────│
│ fullName │ │ user (ref User) │
│ email │ │ paymentMethod (COD|Online)│
│ password │ │ deliveryAddress │
│ role (enum) │ │ addressLine │
│ user │ │ coordinates {lat, lon} │
│ owner │ │ orders[] (sub-document) │
│ deliveryBoy│ │ restaurantId (ref) │
│ location │ │ items[] {itemId, qty} │
│ (GeoJSON) │ │ subTotal │
│ orders[] │ │ assignment (ref) │
│ otp fields │ │ assignedDeliveryBoy │
└──────────────┘ │ totalAmount │
│ status (enum) │
│ razorpayOrderId │
│ razorpayPaymentId │
└───────────────────────────┘
┌────────────────────┐ 1:1 ┌───────────────────────┐
│ Restaurant │ ──── │ RestaurantProfile │
│────────────────────│ │───────────────────────│
│ name │ │ restaurantId (ref) │
│ city / state │ │ cuisine │
│ location (2dsphere)│ │ address │
│ isOpen │ │ image (Cloudinary URL)│
│ rating │ │ phone / email │
│ ratingCount │ │ owner (ref User) │
└────────────────────┘ │ items[] (ref Item) │
│ orders[] │
└───────────────────────┘
┌───────────────────────┐ ┌─────────────────────────────┐
│ Item │ │ DeliveryAssignment │
│───────────────────────│ │─────────────────────────────│
│ name (text index) │ │ orderId (ref Order) │
│ description │ │ assignedTo (ref User) │
│ price │ │ restaurantId (ref) │
│ restaurantId (ref) │ │ broadcastedTo[] (User[]) │
│ foodType │ │ status │
│ Vegetarian │ │ unassigned | assigned │
│ Non-Vegetarian │ │ completed │
│ category (enum) │ │ acceptedAt │
│ Appetizer │ │ otp (numeric) │
│ Main Course ... │ └─────────────────────────────┘
│ image (Cloudinary URL)│
└───────────────────────┘
| Collection | Field(s) | Index Type | Purpose |
|---|---|---|---|
users |
location |
2dsphere |
Geospatial delivery-boy lookups |
users |
email |
Standard | Fast auth lookup |
users |
mobile, role |
Standard | Role filtering |
restaurants |
location |
2dsphere |
$nearSphere nearby queries |
restaurants |
state, city, isOpen |
Compound | City/state listing with status filter |
restaurants |
rating |
Descending | Top-rated sorting |
items |
name |
text |
Full-text food search |
items |
restaurantId, category |
Compound | Menu category filtering |
restaurantProfiles |
restaurantId, owner |
Standard | 1:1 ownership lookup |
All endpoints are prefixed with /api. Protected routes require a valid JWT in the token HttpOnly cookie.
| Method | Endpoint | Auth | Rate Limited | Description |
|---|---|---|---|---|
POST |
/signup |
— | ✅ | Register with email & password |
POST |
/signin |
— | ✅ | Log in, receive HttpOnly JWT cookie |
POST |
/google-auth |
— | ✅ | Firebase Google OAuth sign-in / upsert |
POST |
/signout |
— | — | Clear auth cookie |
POST |
/reset-password/send-otp |
— | ✅ | Send 6-digit OTP to email |
POST |
/reset-password/resend-otp |
— | ✅ | Resend OTP |
POST |
/reset-password/verify-otp |
— | — | Verify OTP (15-min window) |
POST |
/reset-password/reset |
— | — | Set new password after OTP verified |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET |
/current |
✅ | Get current authenticated user |
PUT |
/update |
✅ | Update profile (name, phone, etc.) |
POST |
/get-city |
Rate Limited | Reverse geocode coordinates to city/state |
POST |
/update-location |
✅ | Save user's GPS coordinates to DB |
| Method | Endpoint | Auth | Role | Description |
|---|---|---|---|---|
POST |
/create |
✅ | owner | Create restaurant + upload cover photo |
PUT |
/edit/:restaurantId |
✅ | owner | Update restaurant details |
DELETE |
/delete/:restaurantId |
✅ | owner | Delete restaurant |
GET |
/id/:restaurantId |
— | — | Get restaurant + profile by ID |
GET |
/owner |
✅ | owner | Get authenticated owner's restaurant |
GET |
/city/:city |
— | — | List restaurants in a city |
GET |
/state/:state |
— | — | List restaurants in a state |
GET |
/nearby |
✅ | Rate Limited | Paginated nearby restaurants (geospatial) |
GET |
/address |
✅ | Rate Limited | Reverse geocode via Nominatim |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST |
/create/:restaurantId |
✅ | Add menu item to a restaurant |
PUT |
/edit/:itemId |
✅ | Update item details |
DELETE |
/delete/:itemId |
✅ | Delete a single item |
DELETE |
/restaurant/:restaurantId |
✅ | Delete all items of a restaurant |
GET |
/restaurant |
✅ | Get all items for owner's restaurant |
POST |
/search |
✅ | MongoDB full-text item search |
POST |
/samples |
✅ | Get sample items for multiple restaurants |
| Method | Endpoint | Auth | Role | Description |
|---|---|---|---|---|
POST |
/create |
✅ | user | Place order (Razorpay order created if Online) |
GET |
/all |
✅ | user/owner | Get orders (role-based: history vs. incoming) |
PUT |
/status/:id |
✅ | owner | Update order status |
POST |
/verify-payment |
✅ | user | Verify Razorpay HMAC signature |
POST |
/verify-otp/:id |
✅ | user | Confirm delivery with OTP → "Delivered" |
GET |
/delivery/my |
✅ | deliveryBoy | View available assignments |
PUT |
/delivery/:id/accept |
✅ | deliveryBoy | Accept a delivery assignment |
GET |
/deliveryboy-location/:orderId |
✅ | user | Get delivery partner's live GPS location |
POST |
/delivery/notify |
✅ | owner | Broadcast assignment + send Brevo email |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Returns { status: "OK", timestamp } |
The useNearbyRestaurants hook implements a smart caching and pagination strategy:
- Browser Geolocation API gets the user's GPS coordinates on mount
- Coordinates are passed to
GET /api/restaurants/nearbywith a configurable radius (default 3 km, UI-adjustable) - Results are paginated (8 per page) with full
paginationmetadata - Each restaurant is enriched by fetching its profile (
/api/restaurants/id/:id) - Results are cached in Redux + localStorage with a 60-second TTL — fast navigations skip the API call entirely
- A developer mode lets you override GPS coordinates from a map picker for testing different locations
- Cart state lives in Redux and is persisted to
localStorageviaredux-persist - Supports items from multiple restaurants in one checkout session
Checkout.jsxgroups items by restaurant, fetches prices, and calculates totals- Delivery address is selected interactively via the Leaflet MapPicker component
- On submit: creates an order server-side, optionally opens the Razorpay modal, then verifies payment signature
- Email/Password: bcryptjs hashed. JWT stored in an HttpOnly cookie — never accessible to JavaScript (XSS safe)
- Google OAuth: Firebase handles the popup/redirect flow. The ID token is exchanged at
/api/auth/google-authwhich upserts the user in MongoDB and sets the cookie - OTP Reset: 4-step flow: enter email → receive OTP email → verify OTP → set new password. OTP fields use Mongoose
select: falseto prevent accidental exposure
utils/mail.js sends two types of beautifully styled, branded HTML emails via the Brevo REST API:
- Password Reset OTP — green-branded card with prominent OTP code, 15-minute expiry notice, and security warning
- Delivery Assignment Notification — blue-branded email with Order ID and the delivery verification OTP
utils/cloudinary.js provides a clean uploadToCloudinary(filePath, folder) helper:
- Multer buffers the multipart file to a local temp directory
- The helper uploads to Cloudinary with
resource_type: 'auto' - The local temp file is always deleted (success or failure) to prevent disk leaks
- Returns the CDN
secure_urlwhich is stored in MongoDB
Two separate Redis clients are configured for isolation:
| Client | Purpose |
|---|---|
redisClient |
General cache — user sessions, city lookups |
redisClientRestaurant |
Restaurant cache — nearby query results, menu data |
Dual instances allow independent scaling, separate TTLs, and independent eviction policies.
Three express-rate-limit configurations protect sensitive endpoints:
| Limiter | Window | Max Requests | Applied To |
|---|---|---|---|
authRateLimiter |
15 min | 20/IP | /signup, /signin, /google-auth |
passwordResetRateLimiter |
15 min | 5/IP | All /reset-password/* routes |
locationRateLimiter |
10 min | 15/user | /nearby, /address, /get-city |
| Measure | Implementation |
|---|---|
| JWT in HttpOnly cookies | Tokens never exposed to JavaScript — no XSS risk |
| Role-based access control | authorizeRoles(['user' / 'owner' / 'deliveryBoy']) on every protected route |
| Password hashing | bcryptjs with automatic salt rounds |
| OTP field isolation | Mongoose select: false on otp, password — never returned in queries |
| Rate limiting | Three independent limiters on auth, password reset, and geolocation |
| Startup env validation | Server throws immediately if any required env variable is missing |
| Razorpay HMAC verification | Server-side signature check before any order is marked as paid |
| Strict CORS | Allowlist restricted to CLIENT_URL; credentials: true required |
| Reverse-proxy trust | app.set("trust proxy", 1) ensures correct IP extraction in production |
| File cleanup on failure | Temp uploads deleted from disk even if Cloudinary upload fails |
- Node.js
>= 22.x - npm
>= 9.x - A MongoDB Atlas cluster
- Two Redis instances (or one with separate logical DBs)
- A Cloudinary account
- A Razorpay account (test keys work locally)
- A Brevo account for transactional email
- A Firebase project with Google Sign-In enabled
- A Geoapify API key (for reverse geocoding)
# 1. Clone the repository
git clone https://github.com/rahulgavhar/quick-eats.git
cd quick-eats
# 2. Install all workspace dependencies in one step
npm install --workspaces --include=dev
# 3. Configure environment variables
# Create server/.env (see Environment Variables section)
# Create client/.env (see Environment Variables section)
# 4. Start client + server concurrently
npm run devClient →
http://localhost:5173
Server →http://localhost:3000
# Server
PORT=3000
NODE_ENV=development
CLIENT_URL=http://localhost:5173
# Database
MONGO_URI=mongodb+srv://<user>:<password>@cluster.mongodb.net/quickeats
# Auth
JWT_SECRET=your_super_secret_jwt_key
# Redis
REDIS_URL=redis://localhost:6379
REDIS_RESTAURANT_URL=redis://localhost:6380
# Cloudinary
CLOUDINARY_URL=cloudinary://api_key:api_secret@cloud_name
# Email (Brevo)
OTP_EMAIL=noreply@yourdomain.com
OTP_EMAIL_PASSWORD=your_brevo_api_key
# Geolocation
GEO_API_KEY=your_geoapify_api_key
# Payments
RAZORPAY_API=rzp_test_xxxxxxxxxxxxxxxx
RAZORPAY_KEY_SECRET=your_razorpay_key_secretVITE_API_URL=http://localhost:3000
VITE_FIREBASE_APIKEY=your_firebase_web_api_keynpm run dev # Start client + server concurrently (nodemon + vite)
npm run build # Install deps in all workspaces + build client for production
npm run start # Start production Node server (serves built React app as static files)npm run dev # nodemon index.js — hot-reload on file changes
npm run start # node index.js — productionnpm run dev # Vite dev server with HMR
npm run build # Production build → client/dist/
npm run preview # Preview production build locally
npm run lint # ESLint (react-hooks + react-refresh rules)Contributions, issues, and feature requests are welcome!
- Fork the repository
- Create your feature branch:
git checkout -b feature/amazing-feature - Commit your changes:
git commit -m 'feat: add amazing feature' - Push to the branch:
git push origin feature/amazing-feature - Open a Pull Request
Please open an issue first for major changes to discuss what you'd like to change.
Rahul Gavhar
Made with ❤️ and a lot of ☕
⭐ Star this repo if you found it useful!