Project 5~90 minutesGuided Practice
Real-time Chat
Interview Scenario: "Build a simple chat application. Messages should appear in real-time without refreshing the page. Multiple users should be able to chat together."
WebSockets vs HTTP
- HTTP: Client asks, server responds, connection closes
- WebSocket: Connection stays open, both sides can send anytime
Use WebSockets for: real-time updates, live notifications, collaborative editing, or any feature where the server needs to push data to the client.
Loading...
Backend with WebSockets
Terminal
1pip install websockets
backend/main.py
1from fastapi import FastAPI, WebSocket, WebSocketDisconnect2from fastapi.middleware.cors import CORSMiddleware3from datetime import datetime4from typing import List5import json67app = FastAPI()89app.add_middleware(10 CORSMiddleware,11 allow_origins=["http://localhost:3000"],12 allow_credentials=True,13 allow_methods=["*"],14 allow_headers=["*"],15)161718class ConnectionManager:19 def __init__(self):20 self.active_connections: List[WebSocket] = []2122 async def connect(self, websocket: WebSocket):23 await websocket.accept()24 self.active_connections.append(websocket)2526 def disconnect(self, websocket: WebSocket):27 self.active_connections.remove(websocket)2829 async def broadcast(self, message: dict):30 """Send message to ALL connected clients"""31 for connection in self.active_connections:32 await connection.send_json(message)333435manager = ConnectionManager()363738@app.websocket("/ws/chat")39async def websocket_endpoint(websocket: WebSocket):40 await manager.connect(websocket)4142 await manager.broadcast({43 "type": "system",44 "message": "A new user joined the chat",45 "timestamp": datetime.now().isoformat()46 })4748 try:49 while True:50 data = await websocket.receive_json()51 await manager.broadcast({52 "type": "message",53 "username": data.get("username", "Anonymous"),54 "message": data.get("message", ""),55 "timestamp": datetime.now().isoformat()56 })57 except WebSocketDisconnect:58 manager.disconnect(websocket)59 await manager.broadcast({60 "type": "system",61 "message": "A user left the chat",62 "timestamp": datetime.now().isoformat()63 })
Frontend WebSocket Client
frontend/app/page.tsx
1"use client"23import { useState, useEffect, useRef } from "react"45interface Message {6 type: "message" | "system"7 username?: string8 message: string9 timestamp: string10}1112export default function Chat() {13 const [messages, setMessages] = useState<Message[]>([])14 const [input, setInput] = useState("")15 const [username, setUsername] = useState("")16 const [isConnected, setIsConnected] = useState(false)17 const wsRef = useRef<WebSocket | null>(null)18 const messagesEndRef = useRef<HTMLDivElement>(null)1920 useEffect(() => {21 const ws = new WebSocket("ws://localhost:8000/ws/chat")2223 ws.onopen = () => {24 setIsConnected(true)25 }2627 ws.onmessage = (event) => {28 const message = JSON.parse(event.data)29 setMessages(prev => [...prev, message])30 }3132 ws.onclose = () => {33 setIsConnected(false)34 }3536 wsRef.current = ws3738 return () => ws.close()39 }, [])4041 useEffect(() => {42 messagesEndRef.current?.scrollIntoView({ behavior: "smooth" })43 }, [messages])4445 const sendMessage = (e: React.FormEvent) => {46 e.preventDefault()47 if (!input.trim() || !wsRef.current) return4849 wsRef.current.send(JSON.stringify({50 username: username || "Anonymous",51 message: input52 }))5354 setInput("")55 }5657 return (58 <main className="min-h-screen p-8 max-w-2xl mx-auto">59 <div className="flex items-center justify-between mb-4">60 <h1 className="text-2xl font-bold">Chat Room</h1>61 <span className={`text-sm ${isConnected ? "text-green-500" : "text-red-500"}`}>62 {isConnected ? "● Connected" : "● Disconnected"}63 </span>64 </div>6566 <input67 type="text"68 value={username}69 onChange={(e) => setUsername(e.target.value)}70 placeholder="Your name"71 className="w-full border p-2 rounded mb-4"72 />7374 <div className="h-96 overflow-y-auto border rounded p-4 mb-4 bg-gray-50">75 {messages.map((msg, i) => (76 <div key={i} className={`mb-2 ${msg.type === "system" ? "text-gray-500 italic" : ""}`}>77 {msg.type === "message" ? (78 <>79 <span className="font-bold">{msg.username}: </span>80 <span>{msg.message}</span>81 </>82 ) : (83 <span>{msg.message}</span>84 )}85 </div>86 ))}87 <div ref={messagesEndRef} />88 </div>8990 <form onSubmit={sendMessage} className="flex gap-2">91 <input92 type="text"93 value={input}94 onChange={(e) => setInput(e.target.value)}95 placeholder="Type a message..."96 className="flex-1 border p-2 rounded"97 disabled={!isConnected}98 />99 <button100 type="submit"101 disabled={!isConnected}102 className="bg-blue-500 text-white px-4 py-2 rounded disabled:bg-gray-400"103 >104 Send105 </button>106 </form>107 </main>108 )109}
The useRef Pattern
We use
useRef for the WebSocket because we need to access the same connection instance across renders without triggering re-renders. useState would cause issues here.Alternatives to WebSockets
If asked, mention these alternatives:
- Server-Sent Events (SSE) — one-way server→client, simpler
- Long Polling — HTTP fallback, less efficient
- Socket.io — library that handles fallbacks automatically
Exercises
Final Challenge
The last project is a full interview simulation. You'll build a mini e-commerce app in 2 hours, just like a real coding interview.
Project 6: E-Commerce Mini