A full-stack, real-time chat application built on the MERN stack with secure authentication, instant messaging via WebSockets, and image sharing support.
- Secure authentication with JWT stored in HttpOnly cookies
- Real-time messaging powered by Socket.io
- Live online/offline presence indicators
- Image attachments in chat, stored via Cloudinary
- Responsive UI built with React, Vite, and Tailwind CSS
| Layer | Technologies |
|---|---|
| Frontend | React, Vite, Tailwind CSS, Zustand |
| Backend | Node.js, Express, Socket.io |
| Database | MongoDB (via Mongoose) |
| Media Storage | Cloudinary |
| Auth & Security | JSON Web Tokens (JWT), Bcrypt.js |
QuickTalk runs as a monorepo with a single Node.js server handling both REST API requests and WebSocket connections on one port.
quick-talk/
├── backend/
│ ├── src/
│ │ ├── controllers/ # Auth & message business logic
│ │ ├── models/ # Mongoose schemas (User, Message)
│ │ ├── routes/ # API route definitions
│ │ ├── middleware/ # JWT auth guard
│ │ ├── lib/ # db, cloudinary, socket, jwt utils
│ │ └── index.js # App entry point
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── components/ # ChatContainer, Sidebar, MessageInput, etc.
│ │ ├── pages/ # Login, Signup, Home, Profile
│ │ ├── store/ # Zustand stores (auth, chat, theme)
│ │ └── lib/ # axios instance, utils
│ └── package.json
└── package.json # Root scripts for build/start
-
Authentication — On login/signup, the backend issues a JWT and sets it as an HttpOnly cookie. An auth middleware verifies this token on every protected route, so the token is never exposed to client-side JavaScript.
-
Real-time delivery — The Express app and Socket.io share a single HTTP server (
http.createServer(app)), letting both REST and WebSocket traffic flow through the same port. When a user connects, the backend stores auserId → socketIdmapping (userSocketMap) for targeted message delivery. -
Messaging flow — Sending a message is a hybrid REST + WebSocket flow:
- Frontend POSTs the message to the backend for persistence in MongoDB
- The backend looks up the recipient's socket ID and emits a
newMessageevent directly to them - The recipient's UI updates instantly via an active socket subscription
-
Image attachments — Images are converted to base64 on the frontend, uploaded to Cloudinary by the backend, and the returned URL is stored in the message document.
-
Online presence — The backend broadcasts a
getOnlineUsersevent whenever users connect or disconnect, so all clients can update their online indicators in real time.
- Node.js (v18+)
- MongoDB instance (local or Atlas)
- Cloudinary account (for image uploads)
Create a .env file inside backend/:
MONGODB_URI=your_mongodb_connection_string
PORT=5001
JWT_SECRET=your_jwt_secret
NODE_ENV=development
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret# Clone the repo
git clone https://github.com/sathwik49/quick-talk.git
cd quick-talk
# For Backend
cd backend
npm install
npm run dev
# For Frontend
cd frontend
npm install
npm run dev
# To build
npm run build
npm run preview
The frontend uses three Zustand stores:
useAuthStore— manages the authenticated user, the Socket.io connection lifecycle, and the list of online users.useChatStore— manages messages, the contact list, and the currently selected conversation. Subscribes to incoming socket events and filters them by the active conversation.useThemeStore— manages and persists the selected DaisyUI theme tolocalStorage.
This project is open source and available for learning purposes.