← Back to projects
Case Study

SyncTalk

SyncTalk is a full-stack real-time chat application where authenticated users can join multiple rooms, exchange messages instantly, and manage their profile across a modern responsive interface.

ReactExpressWebSocketMongoDB
Multi-room messaging
WebSocket live sync
Message lifecycle actions
Cloudinary uploads
SyncTalk preview

Overview

SyncTalk combines a React and TypeScript frontend with a Node.js and Express backend plus a WebSocket layer so room events and chat messages feel instant.

Problem Statement

Most basic chat apps either rely on polling, which is slow and wasteful, or skip production concerns like authentication, persistence, and message lifecycle features.

The goal was to build a chat experience that feels immediate while still supporting the real workflows people expect in production messaging products.

Goal

Build a responsive, maintainable chat system that delivers real-time communication with secure authentication, persistent history, and a modern UX across desktop and mobile.

Solution

The app uses REST APIs for account, profile, and file operations and WebSockets for room events and chat delivery. MongoDB stores users and message history, while Cloudinary handles media uploads. On the client, room-scoped state maps keep unread counts, typing indicators, and messages synchronized in real time.

Features

The chat product focuses on the features people actually use in active rooms.

  • JWT-based signup and login with session restore.
  • Multi-room chat with join, leave, switch room, and unread badges.
  • Real-time room history plus live message broadcast.
  • Message lifecycle support for reply, edit, soft-delete, and reactions.
  • Typing indicators and active users per room.
  • File and image messaging through an authenticated upload endpoint.
  • Profile management for avatar upload, bio or status update, and password change.
  • Responsive UI with sidebar navigation and contextual message actions.

Tech Stack

The stack is intentionally simple and controllable so the real-time protocol stays easy to reason about.

  • Frontend: React 19, TypeScript, Vite, Tailwind CSS tokens.
  • Backend: Node.js, Express 5, ws, TypeScript.
  • Database: MongoDB plus Mongoose.
  • APIs and services: Cloudinary for media storage, browser WebSocket API, and Fetch API.
  • Deployment tooling: split deployment with a static frontend host and a Node backend host plus CORS and origin controls.

Architecture / Workflow

The app separates REST concerns from realtime event handling so each layer can stay focused.

  • Express routes handle /api/auth, /api/user, and /api/upload while the WebSocket server attaches to the same HTTP server.
  • React local state stores room-scoped maps for messages, unread counts, typing users, and online users.
  • REST is used for auth, profile, and uploads; WS message types drive join, chat, and typing events.
  • JWT is issued at login and signup, then validated on both REST middleware and the WebSocket handshake.
  • History queries are limited and sorted for efficient room loads.
  • The root app orchestrates auth, session, and socket state while ChatRoom holds the interaction-heavy UI.

Challenges Faced

Realtime systems expose edge cases quickly, so the project focused on consistency and recovery.

  • Keeping multi-room state consistent during reconnects and token-expiry events.
  • Synchronizing message mutations like edit, delete, and reaction across all connected clients.
  • Combining REST file upload with WebSocket message delivery in one user action.
  • Preventing invalid payloads and unauthorized access paths across both HTTP and WS layers.

Performance & Optimization

The implementation avoids polling and keeps updates targeted to the active room.

  • Real-time push model with WebSockets avoids polling overhead.
  • Room-scoped broadcasts reduce unnecessary client updates.
  • History queries are capped to keep room loads fast.
  • Incremental state updates by room minimize broad re-renders.
  • Responsive layout and keyboard-first interactions improve usability.
  • Upload limits and MIME filtering prevent expensive or invalid media operations.

UI/UX Decisions

The interface is designed to help active conversations remain easy to scan and control.

  • Dark-first interface with clear visual hierarchy for long chat sessions.
  • Distinct sent and received message styling for scanning speed.
  • Sidebar room model with unread counters for multi-room awareness.
  • Inline reply and edit context to reduce interaction friction.
  • Typing and presence indicators to make conversations feel live.

Security Considerations

Authentication and ownership checks sit on every important boundary.

  • Password hashing with bcrypt before persistence.
  • JWT auth for protected REST routes and WebSocket upgrade validation.
  • Input validation for usernames, passwords, status, and payload structure.
  • Multer file-size caps plus MIME allowlists for upload safety.
  • CORS allowlist strategy with explicit origin controls.
  • Server-side ownership checks for message edit and delete operations.

Results / Outcomes

The result is a production-style chat architecture that feels instant without compromising persistence or auth.

  • Delivered persistent history and authenticated real-time messaging.
  • Improved feature depth beyond basic chat with rooms, reactions, replies, profile, and media.
  • Established a clean separation between REST responsibilities and event-driven communication.

What I Learned

This project made the state-sync problems of realtime apps much more concrete.

  • How to design reliable state sync across REST and WebSocket boundaries.
  • How to model chat message lifecycle events for collaborative consistency.
  • Why strong validation and auth checks matter more in real-time systems than in standard CRUD apps.
  • How to structure frontend state per domain or room for scalability and maintainability.

Future Improvements

The current version is solid, but a few production-hardening steps would make it scale better.

  • Add message pagination and virtualization for very large room histories.
  • Move token transport from query params to a stricter WebSocket auth strategy.
  • Introduce Redis pub/sub for horizontal scaling across WebSocket instances.
  • Add rate limiting, abuse controls, and stronger audit logging.
  • Add delivery and read receipts with richer presence semantics.

Deployment

The app is built for split deployment so the frontend and real-time backend can evolve independently.

  • Frontend: Vite static build on a static host such as Vercel or Netlify.
  • Backend: Node and Express with WebSocket service on a Node host such as Railway or Render.
  • Data and media: MongoDB and Cloudinary for file and image storage.
  • Environment variables control the backend URL, WebSocket URL, database URI, JWT secret, Cloudinary keys, and client origin.