A fullstack real-time chat application with support for private messages, group rooms, user profiles, authentication (JWT + Google OAuth), and live updates via WebSockets.
The app includes features such as message reactions, pinning, editing, search, pagination, profile customization, email verification, and responsive UI with theme and localization support.
Live Demo: https://chat-app-iota-three-22.vercel.app/chat
Backend API: https://chat-app-a77u.onrender.com/api/health
The application is a fullstack monorepo split into two workspaces — client and server — managed via npm workspaces.
How it fits together:
- The frontend communicates with the backend via REST API (HTTP) and WebSockets (Socket.IO)
- HTTP requests go through a Vercel proxy (
/api/*) to keep cookies first-party and avoid cross-site blocking - WebSocket connection goes directly to the backend on Render
- Real-time events are always broadcast from the server after data is persisted — the client only sends action requests
- Auth state lives in React Context; refresh tokens are stored in HTTP-only cookies and never appear in the URL
Auth flow:
- Email/password:
POST /api/auth/login→ access token (10min) in response + refresh token (7d) in HTTP-only cookie - Google OAuth: Google → Vercel proxy → backend sets cookie → redirects to
/auth/callback→ client calls/api/auth/refresh - Token refresh happens automatically via
fetchWithAuthwhen a 401 response is received
Storage:
- MongoDB Atlas — messages, rooms, users
- Cloudinary — user avatar uploads
- ImageKit CDN — preset avatar images
chat-app/
├── client/ # React + Vite frontend
│ ├── public/
│ │ └── favicon.svg
│ └── src/
│ ├── api/ # REST API functions
│ │ ├── auth.api.ts # login, register, refresh, OAuth
│ │ ├── fetchWithAuth.ts # fetch wrapper with auto token refresh
│ │ ├── messages.api.ts # fetch, search, pin, react
│ │ ├── rooms.api.ts # CRUD, join, leave
│ │ └── users.api.ts # profile, avatar upload
│ ├── assets/ # Lottie animations, SVG
│ ├── components/
│ │ ├── Chat/
│ │ │ ├── components/ # All chat UI components
│ │ │ │ ├── Avatar.tsx
│ │ │ │ ├── MessageList.tsx
│ │ │ │ ├── MessageItem.tsx
│ │ │ │ ├── Sidebar.tsx
│ │ │ │ ├── TopBar.tsx
│ │ │ │ ├── RoomInfoPanel.tsx
│ │ │ │ └── ... # 20 components total
│ │ │ └── Chat.tsx # Main chat orchestration component
│ │ ├── icons/
│ │ │ └── icons.ts # Lucide icon map
│ │ ├── AppLoader.tsx # Fullscreen / overlay loader
│ │ ├── LangToggle.tsx # uk/en language switcher
│ │ ├── ProtectedRoute.tsx # Auth guard for /chat
│ │ ├── ThemeToggle.tsx # Dark/light toggle
│ │ └── ToastWrapper.tsx # react-toastify config
│ ├── context/
│ │ ├── AuthContext.tsx # User state + token refresh logic
│ │ └── ThemeContext.tsx # Dark/light theme
│ ├── hooks/
│ │ ├── useMessages.ts # Messages + search + scroll + pagination
│ │ ├── useRooms.ts # Room CRUD + join/leave
│ │ ├── useSocketListeners.ts # All socket event subscriptions
│ │ ├── useUsers.ts # User list + own profile
│ │ ├── useSendingFallback.ts # Send state with timeout fallback
│ │ ├── useBackendHealth.ts # Backend availability polling
│ │ ├── useBreakpoint.ts # mobile/tablet/desktop detection
│ │ ├── useFormField.ts # Zod-backed form field state
│ │ └── useTheme.ts # System/manual theme detection
│ ├── i18n/ # Internationalization
│ │ ├── locales/
│ │ │ ├── en.ts
│ │ │ └── uk.ts
│ │ └── index.ts # i18next config
│ ├── pages/ # Route-level components
│ │ ├── ChatPage.tsx # Entry point → renders <Chat />
│ │ ├── LoginPage.tsx
│ │ ├── RegisterPage.tsx
│ │ ├── ActivationPage.tsx
│ │ ├── ForgotPasswordPage.tsx
│ │ ├── ResetPasswordPage.tsx
│ │ ├── GoogleCallbackPage.tsx
│ │ ├── SetupProfilePage.tsx
│ │ └── NotFoundPage.tsx
│ ├── services/
│ │ └── socket.ts # Socket.IO client singleton
│ ├── styles/
│ │ ├── theme.ts # Design tokens (colors, borders)
│ │ └── authPageClasses.ts # Shared auth page styles
│ ├── types/ # TypeScript interfaces
│ │ ├── chat.ts # ActiveChat union type
│ │ ├── message.ts
│ │ ├── room.ts
│ │ ├── socket.ts
│ │ └── user.ts
│ ├── utils/
│ │ ├── formatLastSeen.ts
│ │ └── toast.ts
│ ├── validations/
│ │ └── auth.schema.ts # Zod schemas (client-side)
│ ├── App.tsx # Routes + backend health gate
│ ├── index.css # Tailwind base
│ └── main.tsx # Entry point
│
└── server/ # Node.js + Express backend
└── src/
├── config/
│ ├── cloudinary.ts # Cloudinary SDK setup
│ ├── db.ts # MongoDB connection
│ └── passport.ts # Google OAuth strategies
├── controllers/ # Thin request handlers
│ ├── auth.controller.ts
│ ├── message.controller.ts
│ ├── room.controller.ts
│ └── user.controller.ts
├── errors/
│ └── AppError.ts # Typed error classes
├── middlewares/
│ ├── auth.middleware.ts # JWT validation
│ ├── errorMiddleware.ts # Global error handler
│ ├── guestMiddleware.ts # Block authenticated users
│ ├── upload.ts # Multer config
│ └── validateBody.ts # Zod request validation
├── models/ # Mongoose schemas
│ ├── Message.ts
│ ├── Room.ts
│ └── User.ts
├── routes/ # Express routers
│ ├── authRouter.ts
│ ├── messageRouter.ts
│ ├── roomRouter.ts
│ └── userRouter.ts
├── services/ # Business logic layer
│ ├── auth.service.ts # User CRUD, password, tokens
│ ├── message.service.ts # Queries + deletedFor filtering
│ ├── permissions.service.ts # assertRoomMember, assertMessageOwner
│ ├── room.service.ts
│ ├── upload.service.ts # Cloudinary upload/delete
│ └── user.service.ts
├── socket/
│ ├── handlers/ # Event handlers by domain
│ │ ├── message.handler.ts
│ │ ├── reaction.handler.ts
│ │ ├── room.handler.ts
│ │ ├── status.handler.ts
│ │ ├── typing.handler.ts
│ │ └── user.handler.ts
│ ├── socket.ts # Connection lifecycle
│ ├── socketAuth.ts # JWT middleware for sockets
│ └── socketInstance.ts # getIo/setIo singleton
├── state/
│ └── onlineUsers.ts # In-memory Map of connected users
├── types/
│ ├── express/ # Express Request augmentation
│ ├── message.ts
│ └── socket.ts
├── utils/
│ ├── catchError.ts # Async handler wrapper
│ ├── jwt.ts # Token generation/validation
│ └── mailer.ts # Brevo email templates
├── validations/
│ └── auth.schema.ts # Zod schemas (server-side)
├── app.ts # Express app + middleware setup
└── server.ts # HTTP server + Socket.IO init
- React (v19.2.5) — UI library
- React DOM — rendering engine
- TypeScript — static typing
- Vite — build tool & dev server
- React Router DOM — routing
- Tailwind CSS — styling
- Lucide React — icons
- React Toastify — notifications
- Lottie React — animations
- i18next + react-i18next — internationalization
- Socket.io Client — real-time communication
- Node.js — runtime environment
- Express (v5.2.1) — backend framework
- MongoDB + Mongoose — database & ODM
- Socket.io — real-time communication (WebSockets)
- JWT — authentication (access & refresh tokens)
- Passport.js — Google OAuth authentication
- bcrypt — password hashing
- Zod — schema validation
- Multer — file uploads
- Brevo (Sendinblue API) — email delivery service
- Google OAuth 2.0 — external authentication provider
- Cloudinary — image storage & management
- ImageKit (CDN / Image Optimization) — image uploads and delivery
- npm workspaces — monorepo (client + server)
- ESLint + Prettier — linting & formatting
- Jest + ts-jest — backend testing
- Vitest + Testing Library — frontend testing
- mongodb-memory-server — in-memory MongoDB for tests
- TypeScript — static type checking
- Vercel — frontend hosting
- Render — backend hosting
git clone https://github.com/your-username/chat-app.git
cd chat-appnpm installThis installs dependencies for both client and server via npm workspaces.
Create .env files in both server and client folders.
server/.env
PORT=5000
MONGO_URL=your_mongodb_connection
JWT_ACCESS_SECRET=your_access_secret
JWT_REFRESH_SECRET=your_refresh_secret
JWT_SETUP_SECRET=your_setup_secret
NODE_ENV=production # для локального має бути development
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
BREVO_API_KEY=your_brevo_api_key
FROM_EMAIL=your_email_from_brevo
CLOUDINARY_CLOUD_NAME=your_cloud_name
CLOUDINARY_API_KEY=your_api_key
CLOUDINARY_API_SECRET=your_api_secret
SERVER_URL=https://your-backend.onrender.com
CLIENT_URL=https://your-frontend.vercel.appclient/.env
VITE_API_URL=https://your-backend.onrender.com
VITE_BACKEND_URL=https://your-backend.onrender.com
VITE_IMAGE_KIT_URL=your_api_secretRequired third-party services:
- MongoDB Atlas — database
- Google Cloud Console — OAuth credentials
- Brevo — email delivery
- Cloudinary — avatar storage
- ImageKit — CDN for preset avatars
Backend
cd server
npm run devFrontend (in a separate terminal):
cd client
npm run dev| Command | Description |
|---|---|
npm run lint |
Run ESLint + Prettier check |
npm run lint:fix |
Auto-fix lint issues |
npm run format |
Format all files with Prettier |
| Command | Description |
|---|---|
npm run dev |
Start backend in development mode with hot reload |
npm run build |
Compile TypeScript to dist/ |
npm start |
Start compiled production server |
npm test |
Run backend test suite |
| Command | Description |
|---|---|
npm run dev |
Start frontend dev server |
npm run build |
Build for production |
npm run preview |
Preview production build locally |
npm test |
Run frontend test suite |
npm run test:watch |
Run tests in watch mode |
cd server
npm testCovers:
auth.test.ts— user creation, password hashing, JWT generation and validationpermissions.test.ts— room membership, room ownership, message ownership checksmessages.test.ts— delete-for-me filtering, delete-for-all behavior
Tests use an in-memory MongoDB instance — they never touch the real database.
cd client
npm testCovers:
ProtectedRoute.test.tsx— auth loading state, redirect when unauthenticated, render when authenticated
- Private messaging between users
- Group chat rooms
- Live updates via WebSockets (Socket.io)
- JWT authentication (access + refresh tokens)
- Google OAuth login
- Email verification system
- Password reset (forgot password flow)
- Protected routes and middleware
- Custom user profiles (username, bio, avatar)
- Avatar upload (with size & type validation)
- Online/last seen status
- Profile customization
- Send, edit, delete messages
- Message reactions (emoji reactions)
- Pin messages in chats
- Message search
- Pagination / infinite loading
- Create and manage group rooms
- Room owner permissions (edit/delete room)
- Room metadata (name, description, members)
- Real-time room updates
- Responsive design (mobile, tablet, desktop)
- Dark/light theme support
- Emoji picker with icons
- Smooth animations and loaders
- Empty states for all main views
- Multi-language support (i18n)
- Auto-detection of user language
- Persistent language settings
- Pagination for messages and rooms
- Optimized real-time updates
- Cold start loading state
- Error handling for API and sockets
| Decision | Reason |
|---|---|
| React + Vite | Fast dev experience, modern tooling, good TypeScript support |
| Express 5 | Familiar REST framework, async error handling built-in |
| MongoDB | Flexible schema fits chat data (varied message types, reactions) |
| Socket.io | Reliable WebSocket abstraction with fallback to polling |
| JWT + HTTP-only cookies | Stateless auth with XSS protection for refresh tokens |
| Vercel proxy for API | Avoids cross-site cookie blocking without requiring a custom domain |
| npm workspaces | Single repo for client and server, shared scripts |
| Brevo instead of nodemailer | Render free tier blocks outbound SMTP ports |
- In-memory online users state — the server tracks online users in a
Map. If the backend restarts, all users appear offline until they reconnect. A production-ready solution would use Redis. - Email in spam — transactional emails (verification, password reset) may land in spam without a custom verified domain on Brevo.
- Free tier cold start — the backend is hosted on Render free tier, which may take 30–60 seconds to start after inactivity. The frontend shows a loading screen until the backend is ready.
This project was built to demonstrate full-stack product development across multiple interacting concerns:
- Most technically challenging: Google OAuth token flow with cross-site cookie handling across Vercel + Render, and real-time socket reconnect behavior.
- Skills demonstrated: REST API design, WebSocket architecture, auth flows, file uploads, i18n, responsive UI, automated testing, deployment configuration.
- Known tradeoffs: Prioritized feature completeness and architectural clarity over production scalability (no Redis, no message queue, no CI/CD pipeline).
This repository includes documentation for AI agents and contributors:
CLAUDE.md— entry point for AI agents: project overview, structure, commands, and core rulesARCHITECTURE.md— system design: auth flows, WebSocket architecture, REST API reference, database schemaRULES.md— behavioral rules for AI agents working in this codebaseSTYLE_GUIDE.md— coding conventions and standards