A small real-time chat application built with a MERN-style stack (Node.js + Express backend, MongoDB, React frontend) using Socket.IO for realtime messaging and Cloudinary for image uploads.
- Demo / Overview
- Features
- Tech stack
- Repository structure
- Prerequisites
- Environment variables
- Run locally
- API (main endpoints)
- How authentication works
- Realtime (Socket.IO) behavior
- Deployment notes
- Contributing
- Acknowledgements & References
This project implements a simple chat application with:
- User sign up / login
- Profile picture upload (Cloudinary)
- Realtime online presence and messaging with Socket.IO
- Persistent message storage in MongoDB
It is intended as a practice project for building a modern realtime web app.
- Sign up / Login / Logout
- Upload profile picture (base64 image strings uploaded to Cloudinary)
- View list of users (sidebar)
- Send text and image messages
- Real-time delivery for online recipients
- JWT-based authentication stored in an HTTP-only cookie
- Backend: Node.js, Express, Mongoose (MongoDB), Socket.IO
- Frontend: React (Vite), Tailwind CSS, Zustand for state
- Image storage: Cloudinary
- Auth: JWT stored in cookie
-
backend/index.js- Express app entry (connects DB, mounts routes, starts server)src/controllers/- route handlers (auth.controller.js,message.controller.js)src/routes/-auth.route.js,message.route.jssrc/lib/- helpers:db.js,cloudinary.js,socket.js,utils.jssrc/models/- Mongoose models forUserandMessage
-
frontend/src/- React app source (pages, components, store hooks)- Vite + Tailwind + Zustand for local state
- Node.js (v16+ recommended) and npm
- A MongoDB instance (local or hosted like Atlas)
- A Cloudinary account for file uploads
Create a .env file in the backend/ folder (or provide env vars by other means). Example entries used by this project:
PORT=5000
MONGODB_URI=mongodb+srv://<user>:<password>@cluster0.mongodb.net/chat-app?retryWrites=true&w=majority
JWT_SECRET=your_jwt_secret_here
# Cloudinary
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
Notes:
- The backend reads
process.env.MONGODB_URIfor Mongo connection. - Cloudinary configuration is read from
CLOUDINARY_CLOUD_NAME,CLOUDINARY_API_KEY,CLOUDINARY_API_SECRET. - The JWT secret must be provided as
JWT_SECRETand is used to sign auth cookies.
Open two terminals (one for backend, one for frontend).
- Install dependencies and start dev server:
cd backend
npm install
npm run dev- The
backend/package.jsonincludes adevscript:nodemon index.js. The server will run onPORT(defaults to5000). - The server currently allows CORS from
http://localhost:5173(this is where the frontend dev server usually runs).
- Install dependencies and start the dev server:
cd frontend
npm install
npm run dev- The
frontend/package.jsonprovidesdev(vite),buildandpreviewscripts.
All backend routes are mounted under /api.
-
Auth
POST /api/auth/signup— create account (body:{ fullName, email, password })POST /api/auth/login— login (body:{ email, password }) — setsjwtcookie on successPOST /api/auth/logout— clears cookiePUT /api/auth/update-profile— protected; expects{ profilePic }(base64 string); uploads to Cloudinary and updates userGET /api/auth/check— protected; returns user data based on cookie
-
Messages
GET /api/messages/users— protected; returns all users except the logged-in user (sidebar list)GET /api/messages/:id— protected; get conversation messages with user:idPOST /api/messages/send/:id— protected; send a message to:id. Accepts{ text, image }whereimagecan be a base64 string to upload to Cloudinary.
Protected routes require a valid JWT stored in the jwt cookie (the middleware reads req.cookies.jwt).
- When a user signs up or logs in successfully, a JWT is generated and sent to the client as an HTTP-only cookie named
jwtusinggenerateTokenin the backend utilities. - Protected routes use
protectRoutemiddleware which verifies the cookie's JWT and attaches the user toreq.user.
- The server initializes a
socket.ioserver and maintains a mapuserSocketMapto map user IDs to socket IDs for online-user tracking. - On connection, clients should pass their
userIdin the socket handshake query so the server can track them. - When a message is saved in the backend, the server checks
getReceiverSocketId(receiverId)and emitsnewMessageto the receiver if they are online.
Important CORS/origin settings used in the code:
- Express CORS origin is set to
http://localhost:5173inbackend/index.js. - Socket.IO CORS origin is also configured for
http://localhost:5173inbackend/src/lib/socket.js.
If your frontend runs on a different host/port, update these origin values.
- For production you will typically:
- Host MongoDB on Atlas (or another hosted DB)
- Set Cloudinary env vars from your deployment provider
- Build the frontend with
npm run buildinsidefrontend/and serve the static build with a static host (Netlify, Vercel) or serve it from the backend by adding a static folder serving step. - Ensure CORS and socket origins match the deployed front-end domain.
- Feel free to open issues or PRs. Keep changes small and focused. Add tests if you change data logic.
- Built as a practice MERN realtime chat app using Socket.IO and Cloudinary for images.
If you want, I can also:
- add a
READMEsection with screenshots or GIFs (if you provide images), - add a sample
.env.examplefile inbackend/, or - add a short guide in the front-end
READMEdescribing the store hooks and where to configure socket connections.
Made with ❤️ — if you want changes, tell me what to adjust.