A full-stack real-time group chat application. Users can register, log in, and send messages in a shared room. Messages are delivered instantly over WebSockets (Socket.IO) and persisted in MongoDB.
| Layer | Technologies |
|---|---|
| Frontend | React 19, Vite, TypeScript, MUI, Tailwind CSS, TanStack Query, Formik, Zod, Socket.IO Client, Axios |
| Backend | Node.js, Express, TypeScript, MongoDB, Mongoose, Socket.IO, JWT, Zod |
chat-app/
├── backend/ # Express API + Socket.IO server
│ ├── src/
│ │ ├── auth/ # Register & login
│ │ ├── users/ # User CRUD
│ │ ├── chat/ # Messages, stats, WebSocket handlers
│ │ └── main.ts
│ └── .env.example
├── frontend/ # React SPA
│ ├── src/
│ └── .env.example
└── README.md
- Node.js 18+ (20+ recommended)
- npm (comes with Node.js)
- MongoDB running locally or a MongoDB Atlas connection string
git clone <your-repo-url>
cd chat-appcd backend
npm install
cp .env.example .envEdit backend/.env:
NODE_ENV=development
PORT=8080
MONGO_URI=mongodb://localhost:27017/chat-app
JWT_SECRET=your_jwt_secret_key_here_change_in_production
FRONTEND_ORIGIN=http://localhost:5173Important: Do not add spaces around
=in.envfiles.FRONTEND_ORIGINmust match your frontend URL for CORS and Socket.IO.
Start the backend:
npm run devServer runs at http://localhost:8080
Open a new terminal:
cd frontend
npm install
cp .env.example .envfrontend/.env (defaults are fine for local dev):
VITE_API_URL=http://localhost:8080Start the frontend:
npm run devApp runs at http://localhost:5173
- Open http://localhost:5173/register and create an account.
- Log in at http://localhost:5173/login.
- You are redirected to the chat home page.
- Open a second browser window (or incognito), register another user, and send messages — they appear in real time for all connected users.
| Variable | Required | Description |
|---|---|---|
PORT |
No | Server port (default: 8080) |
NODE_ENV |
No | development | production | test |
MONGO_URI |
Yes | MongoDB connection string |
JWT_SECRET |
Yes | Secret for signing JWT tokens |
FRONTEND_ORIGIN |
Yes | Frontend URL for CORS and Socket.IO (e.g. http://localhost:5173) |
| Variable | Required | Description |
|---|---|---|
VITE_API_URL |
No | Backend URL (default: http://localhost:8080) |
Never commit .env files. Use .env.example as a template. .env is listed in .gitignore.
| Command | Description |
|---|---|
npm run dev |
Start dev server with hot reload (tsx watch) |
npm run build |
Compile TypeScript to dist/ |
npm start |
Run production build (node dist/main.js) |
npm run lint |
Lint source files |
| Command | Description |
|---|---|
npm run dev |
Start Vite dev server |
npm run build |
Type-check and build for production |
npm run preview |
Preview production build locally |
npm run lint |
Lint source files |
Base URL: http://localhost:8080
| Method | Endpoint | Body |
|---|---|---|
POST |
/api/auth/register |
{ email, firstName, lastName, password } |
POST |
/api/auth/login |
{ email, password } |
Login response includes accessToken — use it as Authorization: Bearer <token> on protected routes.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/users |
List all users |
GET |
/api/users/:id |
Get user by ID |
PUT |
/api/users/:id |
Update own profile |
DELETE |
/api/users/:id |
Delete own account |
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/chat/messages?page=1&limit=30 |
Fetch messages (newest page first, returned oldest-first) |
POST |
/api/chat/messages |
Send message { "content": "hello" } |
GET |
/api/chat/stats |
{ totalUsers, totalMessages, onlineCount } |
Connect with JWT:
import { io } from "socket.io-client";
const socket = io("http://localhost:8080", {
auth: { token: accessToken },
});| Event | Direction | Payload |
|---|---|---|
send_message |
Client → Server | { content: string } |
new_message |
Server → Client | Full message with sender info |
user_online |
Server → Client | { userId, onlineCount } |
user_offline |
Server → Client | { userId, onlineCount } |
All authenticated users join a single shared "group" room automatically on connect.
POST /api/auth/register— create a user.POST /api/auth/login— copyaccessTokenfrom the response.- Set Authorization → Bearer Token on chat requests.
POST /api/chat/messagesandGET /api/chat/messagesto test HTTP chat.
Postman does not support Socket.IO out of the box; use the web app or a socket.io-client script for real-time testing.
# Backend
cd backend
npm run build
npm start
# Frontend
cd frontend
npm run build
npm run preview # or serve frontend/dist with any static hostSet production environment variables (MONGO_URI, JWT_SECRET, FRONTEND_ORIGIN, VITE_API_URL) to your deployed URLs before building.
| Issue | Fix |
|---|---|
| CORS errors | Ensure FRONTEND_ORIGIN in backend .env exactly matches the frontend URL (no trailing slash mismatch). |
| Socket not connecting | Confirm backend is running, token is valid, and VITE_API_URL points to the backend. |
| MongoDB connection failed | Start local MongoDB (brew services start mongodb-community) or fix MONGO_URI. |
401 Unauthorized |
Log in again; token may be expired (default: 1 day). |
| Messages not appearing live | Check browser console for Socket.IO errors; verify both apps use the same backend URL. |
MIT