A real-time social platform with group chat, direct messaging, video calling, and ephemeral stories. Built with Socket.IO for messaging and Stream Video SDK for WebRTC calls.
Integrated Socket.IO for bidirectional messaging and Stream Video SDK for WebRTC peer-to-peer video calls within a unified React client.

Building a chat app is straightforward. Building one with video calls, stories, and real-time presence — all sharing the same React client and backend — requires careful separation of concerns between Socket.IO events, REST API calls, and the Stream Video SDK lifecycle.
React with Zustand for global socket state. Stream Video SDK's React hooks manage the call UI. Tailwind CSS for responsive layout.
Node.js + Express for REST endpoints. A separate Socket.IO server instance runs alongside Express, sharing the same HTTP server. JWT validates socket connection at handshake.
MongoDB with three main collections: users, messages (indexed on conversationId), and stories (filtered by createdAt TTL).
Socket.IO rooms ensure events are only broadcast to relevant users, not all connected clients. Message history uses MongoDB's conversationId index for efficient fetch.
JWT token is validated during Socket.IO handshake before the connection is accepted. Unauthenticated socket connections are rejected at the server.
Implementing WebRTC from scratch means managing SDP offers, ICE candidates, TURN server configuration, and reconnection logic. Stream handles all of this and provides tested React hooks.
Introduces a third-party dependency and requires a Stream account with API keys. Adds ongoing cost if scaled to many concurrent calls.
The socket connection is created once but needs to be accessible from the chat list, message thread, and presence indicator — all in different parts of the component tree. Zustand avoids prop drilling and Context re-render issues.
Requires discipline to keep socket slice separate from UI state to avoid accidental re-renders on every socket event.
When integrating two real-time systems (Socket.IO + WebRTC SDK), isolate each one in its own state slice and define clear ownership. Letting them share state leads to race conditions that are extremely hard to debug.