A web-based real-time chat application
A practice project for building real-time web applications with WebSocket technology. Built to understand the challenges of event-driven architecture, persistent connections, and message delivery guarantees at small scale.
Stack
| Layer | Technology | Why |
|---|---|---|
| Backend | Node.js + Express + TypeScript | Integrates well with Socket.io, fullstack TypeScript |
| Real-time | Socket.io | Handles WebSocket connections with fallback support and room management built in |
| Auth | Custom (bcrypt) | Intentional choice to avoid third-party auth and understand the fundamentals |
| Database | PostgreSQL + MongoDB + Cassandra | PostgreSQL for relational data (users, channel/server static information); MongoDB for frequent updates and high write throughput; Cassandra for high volume operations |
| Frontend | React + TypeScript | Fullstack TypeScript across backend and frontend |
| Deployment | Docker | Near full containerization (excluding frontend) |
Features
| Feature | Description |
|---|---|
| Real-Time Messaging | Instant message delivery via WebSockets with no polling |
| Chat Rooms | Users can create and join multiple rooms to organize conversations |
| Direct Messaging | Private one-to-one conversations between users |
| User Authentication | Custom login and registration with bcrypt password hashing |
Architecture
Backend follows a layered structure with controllers, services, and repositories. Socket.io is integrated into the Express server to handle WebSocket connections alongside RESTful API endpoints for authentication and data retrieval. Exposes a RESTful API for user management and chat history, while real-time messaging is handled through Socket.io. Active conversations are held in memory. Messages are persisted asynchronously based on triggers - message count thresholds or time intervals - to avoid blocking the event loop on every write. Authentication in WS is validated during the initial handshake and per operation. Messages are relayed and stored in Cassandra for high write throughput, while MongoDB is used for frequently updated data like user presence and channel metadata. PostgreSQL is reserved for relational data that benefits from strong consistency and complex queries.
Frontend is a React application that connects to the backend via REST for authentication and initial data loading, and establishes a WebSocket connection for real-time messaging after successful authentication. It manages chat rooms and direct messages with a simple UI that updates in real time as messages are sent and received.
Challenges
Designing hybrid architecture - balancing the use of REST for certain operations alongside WebSockets for real-time updates was more complex than expected. Deciding which operations should be RESTful (e.g., authentication, initial data loading) versus WebSocket-based (e.g., message sending, presence updates) required careful consideration of the user experience and technical constraints.
Three-Database Setup - managing what data goes where and ensuring consistency across PostgreSQL, MongoDB, and Cassandra added significant complexity. Each database has different strengths and tradeoffs, and orchestrating them together for a seamless experience was a non-trivial challenge.
What I’d Do Differently
Drop Cassandra. The complexity of managing three different databases outweighed the benefits at this scale. MongoDB alone could have handled the high write throughput use cases with proper schema design and indexing, while PostgreSQL could manage the relational data effectively. Cassandra’s advantages in horizontal scaling and high availability are not necessary for a project of this scope, and the operational overhead was not justified.
File storage and authentication via Firebase. Having already integrated Firebase in Vetly, the natural next step here would be replacing the custom auth and adding Firebase Storage for media - consolidating to a single service rather than introducing AWS S3 alongside a separate auth provider. The custom auth was worth building once to understand the fundamentals, but Firebase handles the edge cases (token refresh, session management, OAuth) more robustly for anything production-facing.