Build a Startup Cheatsheet
Realtime (WebSockets, WebRTC)
Use this Build a Startup reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
What "Realtime" Actually Means
A traditional web request is: browser asks, server answers, connection closes. Realtime means the server can push data to the browser without the browser asking first — or two browsers can exchange data with very low latency.
The right realtime technology depends on what you are building:
| Use case | Technology |
|---|---|
| Live notifications, activity feeds | Server-Sent Events or WebSockets |
| Collaborative editing (like Google Docs) | WebSockets + OT or CRDT |
| Live dashboards, data streaming | Server-Sent Events or WebSockets |
| Chat | WebSockets |
| Video/audio calls | WebRTC |
| Multiplayer games | WebSockets or WebRTC (data channels) |
| Database change subscriptions | Supabase Realtime, Convex |
WebSockets
WebSockets provide a full-duplex, persistent connection between a client and server. Either side can send messages at any time.
How it works: 1. Browser initiates an HTTP handshake requesting protocol upgrade 2. Server agrees; connection upgrades to WebSocket (persistent TCP connection) 3. Messages flow in both directions without re-establishing the connection
Use WebSockets when: - You need bidirectional communication (client sends AND server sends) - You need very low latency (chat, multiplayer, collaborative tools) - You need server-to-client push on arbitrary events
Limitations: - Require a persistent server process — serverless functions (Vercel, Cloudflare Workers) do NOT support WebSockets - Each open connection consumes memory on the server - You must handle reconnection logic on the client (sockets drop; networks are unreliable)
Basic WebSocket Server (Node.js)
const { WebSocketServer } = require("ws"); const wss = new WebSocketServer({ port: 8080 }); wss.on("connection", (ws) => { console.log("Client connected"); ws.on("message", (data) => { const message = JSON.parse(data.toString()); // Broadcast to all connected clients wss.clients.forEach((client) => { if (client.readyState === WebSocket.OPEN) { client.send(JSON.stringify({ from: "server", ...message })); } }); }); ws.on("close", () => console.log("Client disconnected")); });
WebSocket Client (Browser)
const ws = new WebSocket("wss://yourapp.com/ws"); ws.onopen = () => { ws.send(JSON.stringify({ type: "join", room: "general" })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); setMessages((prev) => [...prev, data]); }; ws.onclose = () => { // Reconnect logic — exponential backoff setTimeout(() => reconnect(), 1000); };
Socket.IO
Socket.IO is a library built on top of WebSockets that adds:
- Automatic reconnection with exponential backoff
- Rooms (broadcast to a subset of clients)
- Fallback to HTTP long-polling if WebSockets are unavailable
- Event-based API (cleaner than raw
messageevents)
// Server const io = new Server(httpServer); io.on("connection", (socket) => { socket.join("room-42"); socket.to("room-42").emit("user-joined", { id: socket.id }); socket.on("send-message", (data) => { io.to("room-42").emit("new-message", data); }); }); // Client const socket = io("https://yourapp.com"); socket.emit("send-message", { text: "Hello!" }); socket.on("new-message", (data) => console.log(data));
When to use Socket.IO: You want the convenience layer; you need rooms and reconnection out of the box; you are building chat or collaborative features quickly. The overhead is minimal for most use cases.
Server-Sent Events (SSE)
SSE is a one-way channel: the server pushes data to the browser over a persistent HTTP connection. The browser cannot send data back through the same connection.
When to use SSE: - Live notifications, activity feeds, progress updates, streaming AI responses - You only need server-to-client push (no bidirectional communication) - You want to stay on serverless platforms like Vercel — SSE works in Next.js API routes
SSE is simpler than WebSockets for read-only streaming use cases.
// Next.js API route — streaming SSE export async function GET() { const encoder = new TextEncoder(); const stream = new ReadableStream({ start(controller) { const send = (data: object) => { controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}\n\n`)); }; // Send a message every second for 10 seconds let count = 0; const interval = setInterval(() => { send({ type: "update", count }); if (++count >= 10) { clearInterval(interval); controller.close(); } }, 1000); }, }); return new Response(stream, { headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive", }, }); } // Client const source = new EventSource("/api/events"); source.onmessage = (e) => { const data = JSON.parse(e.data); setUpdates((prev) => [...prev, data]); };
SSE is the mechanism behind streaming AI chat responses (ChatGPT-style typing effect).
WebRTC
WebRTC (Web Real-Time Communication) enables peer-to-peer communication between two browsers. Unlike WebSockets (which go through your server), WebRTC data flows directly browser-to-browser after the initial handshake.
What WebRTC enables: - Video calls (camera/microphone streams) - Voice calls - Peer-to-peer file transfer - Low-latency data channels (multiplayer games)
How it works (simplified): 1. Both browsers connect to a signaling server (your server, via WebSockets) to exchange connection metadata (SDP offer/answer) 2. They use STUN/TURN servers to discover their public IP addresses and traverse NATs/firewalls 3. Once connected, media/data flows peer-to-peer (bypassing your server entirely)
STUN servers — help browsers discover their public IP. Google provides free STUN servers: stun:stun.l.google.com:19302.
TURN servers — relay traffic when peer-to-peer is blocked by a firewall. You must run or pay for TURN. Twilio's TURN network and Metered TURN offer hosted options.
When NOT to Build Video Calls from Scratch
WebRTC is notoriously complex. For most startups, use a managed real-time video platform:
| Platform | Free tier | Best for |
|---|---|---|
| Daily.co | 2,000 min/month | Fastest integration; excellent React SDK |
| Livekit | Self-hostable | Open-source; production-grade; WebRTC + SFU |
| Agora | 10,000 min/month | Mobile SDKs; large enterprise ecosystem |
| Twilio Video | Pay-per-use | Reliable; Twilio ecosystem |
| 100ms | 10,000 min/month | Good DX; competitive pricing |
Managed Realtime Platforms
If you want realtime database sync or collaborative state without managing WebSocket infrastructure yourself:
| Platform | Type | Best for |
|---|---|---|
| Supabase Realtime | Postgres CDC subscriptions | Listening to database changes; presence |
| Convex | Reactive queries | Full-stack reactivity; query re-runs on data change |
| Liveblocks | Collaborative state | Google Docs-style collaboration; comments, presence |
| PartyKit | WebSocket rooms | Multiplayer; simple room-based pub/sub |
| Pusher | Managed WebSocket channels | Classic choice; good free tier; easy setup |
| Ably | Pub/sub at scale | High-reliability; enterprise-grade; global |
Supabase Realtime
const channel = supabase .channel("posts-changes") .on("postgres_changes", { event: "INSERT", schema: "public", table: "posts" }, (payload) => { setFeed((prev) => [payload.new, ...prev]); }) .subscribe(); // Cleanup return () => supabase.removeChannel(channel);
Pusher
// Server: trigger an event const pusher = new Pusher({ appId, key, secret, cluster }); await pusher.trigger("chat-room", "new-message", { text, userId }); // Client: subscribe const pusher = new PusherJS(key, { cluster }); const channel = pusher.subscribe("chat-room"); channel.bind("new-message", (data) => { setMessages((prev) => [...prev, data]); });
Pusher free tier: 200 concurrent connections, 200,000 messages/day.
Choosing the Right Approach
| Need | Recommended approach |
|---|---|
| Streaming AI responses | SSE in Next.js API route |
| Live notifications / feed updates | SSE or Supabase Realtime |
| Chat application | Socket.IO on Railway, or Pusher |
| Collaborative editing | Liveblocks (managed) or Yjs + WebSockets |
| Multiplayer game | WebSockets + Socket.IO rooms |
| Video calls | Daily.co or Livekit (managed) |
| Database-driven realtime | Supabase Realtime or Convex |
| Real-time dashboard | SSE or WebSockets from a persistent server |
Deployment Considerations
WebSockets require a persistent server process. This means:
- Vercel functions will NOT work for WebSocket servers (15-second timeout; stateless)
- You need Railway, Render, Fly.io, or a VM to host a WebSocket server
- Alternatively, use a managed service (Pusher, Ably, Liveblocks) that handles the WebSocket infrastructure
SSE works on Vercel — each SSE connection is just a long HTTP response, which Vercel supports.