This repository implements the Kairos Nexus real‑time 1‑to‑1 chat assignment end‑to‑end:
- React 18 + TypeScript frontend with TailwindCSS and shadcn‑style UI (Button, Card, Input, ScrollArea)
- Node.js + Express + Socket.io backend
- PostgreSQL + Prisma for message persistence
- Jest + React Testing Library (frontend) and Jest + Supertest (backend)
- GitHub Actions CI running lint, tests, and builds on every push/PR
The app allows two users (for example User A and User B) to log in, send messages, and see them appear in real time in both tabs, backed by a persistent messages table.
-
Frontend
- React 18 with TypeScript
- Vite bundler
- TailwindCSS
- shadcn‑style UI components:
Button– used for actions like Send and ContinueInput– used for username and message inputsCard‑style layouts – used inLoginPageand chat layoutScrollArea– used for the message list and auto‑scroll container
- Socket.io client
- Jest + React Testing Library
-
Backend
- Node.js + Express
- Socket.io
- PostgreSQL with Prisma ORM
- Jest + Supertest
-
CI/CD
- GitHub Actions workflow at
.github/workflows/test.yml- Installs frontend and backend dependencies
- Runs ESLint
- Runs Jest tests (frontend + backend)
- Builds frontend and backend
- GitHub Actions workflow at
-
Chat Interface
- Implemented in
src/src/pages/ChatPage.tsxwith supporting components insrc/src/components/. - Message list shows conversation history in chronological order with the latest messages at the bottom.
- Message bubbles display sender name, text, and timestamp, with different styling for "you" vs. other users.
- Uses a
ScrollAreacomponent to keep the conversation scrollable and visually polished.
- Implemented in
-
Socket.io Integration
- Socket client logic lives in
src/src/services/socket.ts. - On mount, the chat hook (
src/src/hooks/useChat.ts) connects to the Socket.io server. - The client:
- Listens for
messageevents from the server and appends them to the message list. - Emits
sendMessagewhen the user sends a message. - Also supports user presence/typing events (used in the sidebar and typing indicators).
- Listens for
- Socket client logic lives in
-
User Routing / Login
src/src/pages/LoginPage.tsxprovides a simple login experience where the user enters a username (e.g. User A or User B).- The username is stored in a global React context (
src/src/context/AuthContext.tsx). - Once logged in, the user is routed into the
ChatPageexperience. - The Sign Out button in
ChatPagenow:- Clears the username from context, and
- Refreshes the page to fully reset state.
-
Loading History
src/src/services/api.tsimplements REST calls to the backend:GET /api/messagesfor historyPOST /api/messagesto create a new message
useChatcallsfetchMessageson mount and populates the message list before starting real‑time subscriptions.
-
UI/UX Polish
- Fully responsive, mobile‑first layout driven by TailwindCSS.
- shadcn‑style
Button,Card,Input, andScrollAreacomponents give a consistent design system. - Loading states:
- Initial "Loading Nexus..." when username is missing.
- "Connecting nexus..." spinner while messages are loading.
- Sidebar shows active users and status, plus typing indicators where applicable.
-
Backend Requirements
- Implemented inside
server/:- Routes:
GET /api/messages,POST /api/messagesinroutes/messageRoutes.ts. - Controllers:
messageController.tshandles fetching and creating messages. - Services:
messageService.tsuses Prisma to read/write to theMessagetable. - Sockets:
sockets/chatSocket.tswires up Socket.io events.
- Routes:
- Socket.io Events:
sendMessage(client → server):- Validates payload (
sender,text). - Persists message to the DB.
- Broadcasts
messageto all connected clients.
- Validates payload (
message(server → clients):- Emitted with the full message object (id, sender, text, createdAt).
- Validation:
- REST requests go through validation middleware (non‑empty
senderandtext). - Invalid payloads return 400 Bad Request with an error payload.
- REST requests go through validation middleware (non‑empty
- Implemented inside
-
Database
- Prisma schema (
prisma/schema.prisma) defines aMessagemodel:id(UUID primary key)sender(username)text(message body)createdAt(timestamp)
- Migrations are run via
npx prisma migrate devand Prisma client is generated vianpx prisma generate.
- Prisma schema (
-
Testing
- Frontend (
src/):- Jest + React Testing Library verify that:
- Messages render correctly.
- The Send button triggers message sending.
- Mocked Socket.io events update the UI (message appended to the list).
src/src/__tests__/Services.test.tscovers REST API service helpers and socket helpers. The global fetch mock usesglobalThisto avoid TypeScript errors.
- Jest + React Testing Library verify that:
- Backend (
server/):- Jest + Supertest verify:
GET /api/messages(happy path).POST /api/messages(happy path).- Validation errors return 400 when payload is invalid.
- Socket broadcast behavior is tested in dedicated Socket.io tests.
- Jest + Supertest verify:
- Coverage thresholds are set to 70%+ for branches, functions, lines, and statements in both frontend and backend Jest configs.
- Frontend (
-
CI/CD
.github/workflows/test.ymlruns on push/PR and:- Spins up a PostgreSQL service.
- Installs frontend (
src/) and backend (server/) dependencies. - Runs Prisma migrations and generates the Prisma client.
- Runs ESLint for both frontend and backend.
- Runs Jest tests for both frontend and backend.
- Builds the frontend and backend to ensure production builds succeed.
- Node.js 18+ (recommended 20+)
- npm
- Docker (for Postgres) or a local PostgreSQL instance
git clone https://github.com/joshuaoni/realtime-chat.git
cd realtime-chatCopy .env.example to .env in both src/ (frontend) and server/ (backend)
Ensure DATABASE_URL points to a running PostgreSQL instance and VITE_API_BASE_URL points to the same PORT you set in server/.env.
Option A – Docker
docker run --name kairos-postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=chat_app -p 5432:5432 -d postgres:16Option B – Local Postgres
Create a chat_app database and update DATABASE_URL in .env to match your credentials.
Option C – Remote Postgres
Use existing remote postgres URI.
cd server
npm install --legacy-peer-deps
# Run Prisma migrations
npx prisma migrate dev --name init
# Generate Prisma client
npx prisma generate
# Start the backend in dev mode
npm run devThe backend runs at http://localhost:<PORT>.
cd src
npm install --legacy-peer-deps
npm run devThe frontend runs at http://localhost:5173 (Vite default).
cd src
npm testThese tests cover:
- Successful message rendering
- Send button triggering message send
- Mocked Socket.io message events updating the UI
cd server
npm testEnsure PostgreSQL is running and Prisma migrations have been applied. Tests cover:
GET /api/messagesPOST /api/messages- 400 error on invalid payloads
- Socket.io broadcast path
Both test suites enforce ~70% minimum coverage on critical paths via Jest coverage thresholds.
cd src
npm run lintcd server
npm run lintcd src
npm run buildcd server
npm run build-
Start the backend:
cd server npm run dev -
Start the frontend:
cd src npm run dev -
Open
http://localhost:5173in two browser tabs. -
In tab 1, log in as
User A. -
In tab 2, log in as
User B. -
Send messages from both tabs:
- Messages appear in both tabs instantly via Socket.io.
- Refresh either tab; historical messages are loaded from PostgreSQL.
-
To sign out, click the logout icon; the app will clear your username and refresh the page to reset state.
On every push/PR, the workflow in .github/workflows/test.yml runs:
- Install dependencies for
src/(frontend) andserver/(backend) - Run ESLint via each package’s
npm run lint - Run Jest tests in both frontend and backend
- Build the frontend and backend
All steps must pass for the check to succeed, giving confidence that the assignment remains working and production‑ready.