A blazing-fast, WhatsApp-inspired real-time chat app β Web Push Notifications, Voice Notes, Socket.io events, emoji reactions, reply threads, image sharing, and 32 switchable themes. Zero polling. Pure vibes.
π Live Demo Β Β·Β π Report Bug Β Β·Β β¨ Request Feature
|
|
| Service | Role |
|---|---|
| Cloud database | |
| Media CDN | |
| Full-stack deployment |
Before you begin, make sure you have:
β
Node.js >= 18
β
A MongoDB Atlas account (free tier works great)
β
A Cloudinary account (free tier works great)
git clone https://github.com/UTKARSHH20/fullstack-chat-app.git
cd fullstack-chat-app# Backend
npm install --prefix backend
# Frontend
npm install --prefix frontendCreate a .env file inside the backend/ directory:
# ββ Database ββββββββββββββββββββββββββββββββββββββββββ
MONGODB_URL=your_mongodb_atlas_connection_string
# ββ Server ββββββββββββββββββββββββββββββββββββββββββββ
PORT=5001
NODE_ENV=development
# ββ Auth ββββββββββββββββββββββββββββββββββββββββββββββ
JWT_SECRETKEY=your_super_secret_key
GOOGLE_CLIENT_ID=your_google_client_id
# ββ Cloudinary ββββββββββββββββββββββββββββββββββββββββ
CLOUDINARY_CLOUD_NAME=your_cloudinary_cloud_name
CLOUDINARY_API_KEY=your_cloudinary_api_key
CLOUDINARY_API_SECRET=your_cloudinary_api_secret
# ββ Web Push Notifications ββββββββββββββββββββββββββββ
VAPID_PUBLIC_KEY=your_vapid_public_key
VAPID_PRIVATE_KEY=your_vapid_private_key
VAPID_SUBJECT=mailto:test@example.comCreate a .env file inside the frontend/ directory:
VITE_VAPID_PUBLIC_KEY=your_vapid_public_key
VITE_GOOGLE_CLIENT_ID=your_google_client_id
β οΈ Never commit.envto version control. It is already listed in.gitignore.
Open two terminals and run:
# Terminal 1 β Backend (http://localhost:5001)
cd backend && npm run dev
# Terminal 2 β Frontend (http://localhost:5173)
cd frontend && npm run devThen visit http://localhost:5173 π
# From the project root
npm run build # installs deps + builds the React app
npm start # starts Express, which serves the built frontendThis app deploys as a single service on Render β the Express backend serves the compiled React frontend in production. No separate static hosting needed.
π Render Configuration (click to expand)
| Setting | Value |
|---|---|
| Build Command | npm install --prefix backend && npm install --prefix frontend && npm run build --prefix frontend |
| Start Command | npm run start --prefix backend |
| Node Version | 24 |
| Environment Variables | (set all variables from the .env section above) |
The backend uses
express.staticto servefrontend/distand a catch-allapp.use()handler for SPA routing. Note:app.get("*")is intentionally avoided due to an Express 5 + path-to-regexp v8 incompatibility.
fullstack-chat-app/
β
βββ π backend/
β βββ src/
β βββ controllers/
β β βββ auth.controller.js
β β βββ message.controller.js
β βββ lib/
β β βββ cloudinary.js
β β βββ db.js
β β βββ socket.js
β β βββ webpush.js
β β βββ utils.js
β βββ middleware/
β β βββ auth.middleware.js
β βββ models/
β β βββ message.model.js
β β βββ user.model.js
β βββ routes/
β β βββ auth.route.js
β β βββ message.route.js
β βββ index.js
β
βββ π frontend/
β βββ public/
β β βββ service-worker.js
β βββ components/
β β βββ Navbar.jsx
β βββ lib/
β β βββ axios.js
β β βββ socket.js
β βββ pages/
β β βββ ChatPage.jsx
β β βββ LoginPage.jsx
β β βββ ProfilePage.jsx
β β βββ SettingsPage.jsx
β β βββ SignUpPage.jsx
β βββ src/
β βββ store/
β β βββ useAuthStore.js
β β βββ useChatStore.js
β β βββ useThemeStore.js
β βββ App.jsx
β βββ main.jsx
β
βββ .gitignore
βββ package.json
ββββββββββββββββ hash pw ββββββββββββββββ JWT cookie ββββββββββββββββββ
β Local Auth β ββββββββββββββββΊ β bcryptjs β ββββββββββββββββΊ β HTTP-only β
β (User/Pass) β β (10 rounds) β β Secure Cookie β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββ
β²
ββββββββββββββββ ID Token ββββββββββββββββ β
β Google OAuth β ββββββββββββββββΊ β google-auth β ββββββββββββββββββββββββββ
β (One-tap) β β validator β
ββββββββββββββββ ββββββββββββββββ
- Local Authentication: User signs up β password hashed with
bcryptjs(10 salt rounds). On login, a JWT is signed with 15-day expiry and set as an HTTP-only, secure,SameSite=Strictcookie. - Google OAuth: User logs in/signs up via Google Identity Services β ID token verified server-side via
google-auth-libraryβ user account created/resolved β JWT cookie issued. - Session Persistence: On page refresh,
checkAuth()re-validates the session cookie and reconnects the WebSockets. - Security Middleware: All authentication endpoints are rate-limited (max 20 requests per 15 minutes), and express inputs are validated using custom middleware. All response headers are secured via Helmet.
β‘ Real-time Architecture
Express and Socket.io share a single HTTP server instance. A userId β socketId map held in memory enables direct message routing to specific clients β no broadcasting to everyone, no unnecessary noise.
π Web Push Integration
A robust implementation of the Web Push API using Service Workers and VAPID keys. If the recipient is offline (no active Socket connection), the backend automatically triggers a native OS-level push notification to alert them.
β©οΈ Reply Threading
Replied messages store a replyTo snapshot (sender name + message text) directly on the message document. The thread context is always preserved even if the original message is later deleted β no orphaned threads.
π§ Express 5 Wildcard Fix
path-to-regexp v8 (used internally by Express 5) rejects bare "*" route patterns at startup. The SPA catch-all fallback uses app.use() instead, which bypasses the regex engine entirely and works perfectly.
π Sidebar Privacy Design
Instead of listing all registered users (which would expose your entire user base), the sidebar only shows people you've already messaged. A dedicated GET /api/messages/search?q= endpoint powers the "New Chat" modal for discovering new people.
π± Mobile Layout
No framework tricks needed β a simple hidden md:flex toggle on the sidebar and chat panel gives a native-feeling single-panel experience on mobile, with proper back navigation on small screens.
# Fork the repo, then:
git checkout -b feature/your-feature-name
git commit -m "feat: describe your change"
git push origin feature/your-feature-name
# Open a Pull Request β
Please keep PRs focused β avoid mixing unrelated changes in a single PR.
Distributed under the MIT License. See LICENSE for more information.
