A collaborative real-time application built as part of a structured portfolio-building program focused on production-grade engineering practices — moving beyond "it works" into understanding why it works.
This is Flagship Project 1 of 3, designed to build depth in WebSockets, concurrency, and real-time state synchronization.
Milestone 1 proved a JWT could gate a live WebSocket connection. Milestone 2 added board creation and room-based joining with membership authorization. Milestone 3 builds the full collaborative core: a real-time chat engine with sending, editing, and deleting messages — including dual authorization, soft-delete, and paginated history retrieval.
Auth (REST)
- User model with bcrypt password hashing via a Mongoose
prehook - Signup / login endpoints
- Zod-based request validation middleware
- Centralized error handling middleware
- HTTP-side JWT verification middleware (separate from the socket-side version, since tokens are read from different locations)
Boards (REST)
- Board model — owner reference + role-based membership array (
owner/moderator/member) - Board passwords hashed via the same
prehook pattern used for users createBoardendpoint — owner is automatically added toboardMembersat creation timegetMessagesendpoint — paginated message history for a board, membership-gated, with soft-deleted messages sanitized (content stripped) before being sent to the client- Sensitive fields (hashed passwords) stripped from API responses
Messages (Real-time, Socket.io)
- Message model — supports text, image, or both on a single message; optional reply-to reference; soft-delete fields (
deleted,deletedBy,deletedAt) for a future recoverable-delete window - Custom schema-level validation ensuring a message always has text, an image, or both — never neither
sendMessage— verifies sender is an actual board member before persisting and broadcastingeditMessage— author-only, verified against the message's stored senderdeleteMessage— dual authorization: the original sender OR a board owner/moderator can delete; soft-deletes (marksdeleted: truerather than removing the record) and broadcasts structured deletion data (deletedBy,isAdminDelete) so the frontend controls its own display logic- All real-time events scoped to
io.to(boardId)— reaching only members of that specific board, never the whole server
Real-time infrastructure (Socket.io)
- Socket.io wired onto a raw HTTP server (required for the WebSocket upgrade handshake)
io.use()middleware verifies a JWT at connection time, before a socket is accepted- Socket event logic lives in its own module (
sockets/boardHandlers.js), keepingserver.jsfocused purely on infrastructure wiring joinBoard— skips password verification for existing members (e.g. reconnecting after a refresh); requires the correct board password for first-time joiners, then adds them as a member
- Node.js (ES Modules)
- Express
- Socket.io
- MongoDB Atlas + Mongoose
- JWT + bcrypt
- Zod (validation)
config/ → Database connection
controllers/ → Auth and board logic
middleware/ → Error handling, validation, JWT verification (HTTP + socket)
models/ → Mongoose schemas (User, Board, Message)
routes/ → Express route definitions
sockets/ → Socket.io event handlers, registered onto io from server.js
validators/ → Zod schemas
server.js → App entry point — Express, HTTP server, Socket.io wiring
npm installCreate a .env file:
PORT=3000
MONGO_URI=your_mongodb_atlas_connection_string
JWT_SECRET=your_secret_here
Run in development:
npm run dev| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Create a new user |
| POST | /api/auth/login |
Authenticate and receive a JWT |
| POST | /api/boards/create-board |
Create a new board (requires Authorization: Bearer <token>) |
| GET | /api/boards/:boardId/messages |
Fetch a board's message history, paginated (requires Authorization: Bearer <token>) |
Message history pagination: the endpoint returns the 10 most recent messages by default. Pass ?before=<ISO timestamp> (the createdAt of the oldest message currently loaded) to fetch the next 10 older messages — anchored to a timestamp rather than a page number, so results stay accurate even as new messages arrive elsewhere in the room.
Clients must attach a valid JWT when connecting:
const socket = io("http://localhost:3000", {
auth: { token: yourJWT }
});Joining a board:
socket.emit("joinBoard", { boardId, boardPassword });
socket.on("joinSuccess", (data) => { /* now in the room */ });
socket.on("joinError", (data) => { /* not authorized, or board not found */ });Sending a message:
socket.emit("sendMessage", { boardId, text, imageUrl, replyTo });
socket.on("newMessage", (data) => { /* broadcast to the whole room */ });
socket.on("boardError", (data) => { /* not a board member */ });Editing a message (author only):
socket.emit("editMessage", { messageId, newText });
socket.on("messageEdited", (data) => { /* broadcast to the whole room */ });
socket.on("messageError", (data) => { /* not found, deleted, or not the author */ });Deleting a message (author or board admin):
socket.emit("deleteMessage", { messageId });
socket.on("deletedMessage", (data) => { /* { messageId, deletedBy, isAdminDelete } */ });
socket.on("deleteError", (data) => { /* unauthorized */ });- Frontend (next): React + Vite client with Zustand for state management — auth pages, socket connection, board UI, live chat
- Mobile: React Native companion app, built after the web frontend is complete
- Milestone 4: Recoverable delete window (undo-delete within a time limit)
- Milestone 5: Redis pub/sub for horizontal scaling across multiple server instances
- Milestone 6: Load testing, Docker Compose, deployment
Each flagship project in this program follows the same sequence: backend → frontend → mobile, before moving to the next project.
This project is the first of three flagship builds in a structured mentorship track:
- RealTimeBoard — WebSockets, concurrency, state synchronization
- MarketFlow — multi-tenant data modeling, caching, security hardening
- PulseQueue — job queues, retry logic, observability, chaos testing