Skip to main content

websocket-patterns

WebSocket and real-time patterns. Socket.io, native WebSocket, Server-Sent Events, connection management, rooms.

Quellinformationen

Repository
skeletorflet/opencode-kit
Letzte Quellaktivität
3. April 2026 um 15:32
Erkannte Sprache von SKILL.md
Englisch
Sterne
10
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
websocket-patterns
description
WebSocket and real-time patterns. Socket.io, native WebSocket, Server-Sent Events, connection management, rooms.
# WebSocket Patterns > Use the right real-time primitive. Not everything needs a WebSocket. --- ## 1. Real-Time Technology Choice | Technology | Direction | When | |-----------|-----------|------| | **WebSocket** | Bidirectional | Chat, gaming, live collab | | **Server-Sent Events (SSE)** | Server → Client | Live feeds, notifications | | **HTTP Polling** | Client-initiated | Simple, compatibility | | **Long Polling** | Client-initiated | When WebSocket blocked | ``` SSE is simpler than WebSocket for one-way data. Use WebSocket only when client needs to send data frequently. ``` --- ## 2. Socket.io Server ```ts import { Server } from "socket.io"; import { createServer } from "http"; const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: process.env.CLIENT_URL, credentials: true }, pingTimeout: 60_000, pingInterval: 25_000, }); // Authentication middleware io.use(async (socket, next) => { try { const token = socket.handshake.auth.token; const user = await verifyToken(token); socket.data.user = user; next(); } catch { next(new Error("Authentication error")); } }); io.on("connection", (socket) => { const { user } = socket.data; console.log(`${user.id} connected`); // Join room socket.on("join:room", (roomId: string) => { socket.join(roomId); socket.to(roomId).emit("user:joined", { userId: user.id }); }); // Message socket.on("message:send", async ({ roomId, content }) => { const message = await db.message.create({ data: { content, authorId: user.id, roomId }, include: { author: true }, }); io.to(roomId).emit("message:new", message); }); socket.on("disconnect", (reason) => { console.log(`${user.id} disconnected: ${reason}`); }); }); ``` --- ## 3. Socket.io Client (React) ```tsx import { createContext, useContext, useEffect, useState } from "react"; import { io, Socket } from "socket.io-client"; const SocketContext = createContext<Socket | null>(null); export function SocketProvider({ children }: { children: React.ReactNode }) { const [socket, setSocket] = useState<Socket | null>(null); const { token } = useAuth(); useEffect(() => { if (!token) return; const s = io(process.env.NEXT_PUBLIC_WS_URL!, { auth: { token }, transports: ["websocket"], reconnectionAttempts: 5, reconnectionDelay: 1000, }); s.on("connect", () => console.log("Connected")); s.on("connect_error", (err) => console.error("Connection error:", err)); setSocket(s); return () => { s.disconnect(); }; }, [token]); return <SocketContext.Provider value={socket}>{children}</SocketContext.Provider>; } // Usage in component function ChatRoom({ roomId }: { roomId: string }) { const socket = useContext(SocketContext); const [messages, setMessages] = useState<Message[]>([]); useEffect(() => { if (!socket) return; socket.emit("join:room", roomId); socket.on("message:new", (msg) => setMessages(prev => [...prev, msg])); return () => { socket.off("message:new"); socket.emit("leave:room", roomId); }; }, [socket, roomId]); } ``` --- ## 4. Server-Sent Events (SSE) ```ts // Express SSE endpoint app.get("/events", auth, (req, res) => { res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.flushHeaders(); // Send event const send = (event: string, data: unknown) => { res.write(`event: ${event}\\\ data: ${JSON.stringify(data)}\\\ \\\ `); }; // Subscribe to updates const unsubscribe = pubSub.subscribe(`user:${req.user.id}`, (event) => { send(event.type, event.data); }); // Heartbeat to prevent timeout const heartbeat = setInterval(() => res.write(": ping\\\ \\\ "), 30_000); req.on("close", () => { clearInterval(heartbeat); unsubscribe(); }); }); ``` ```tsx // Client SSE useEffect(() => { const es = new EventSource("/events", { withCredentials: true }); es.addEventListener("notification", (e) => { addNotification(JSON.parse(e.data)); }); es.onerror = () => setTimeout(() => es.close(), 1000); // reconnect return () => es.close(); }, []); ``` --- ## 5. Scaling WebSockets ``` Single server: Socket.io in-process Multiple servers: Need sticky sessions + Redis adapter Redis adapter (Socket.io): import { createAdapter } from "@socket.io/redis-adapter"; io.adapter(createAdapter(pubClient, subClient)); → Events now propagate across all server instances → io.to(room).emit() works across servers ``` --- ## 6. Connection Management ``` Reconnection: ├── Exponential backoff (1s, 2s, 4s, 8s, max 30s) ├── Stop retry after N attempts ├── Notify user if offline for extended period └── Re-sync state on reconnect (not just resume) Presence: ├── Track connected sockets per user in Redis ├── Publish presence events on connect/disconnect └── Debounce disconnect (user may have briefly lost connection) ``` --- ## 7. Anti-Patterns | ❌ Don\'t | ✅ Do | |----------|-------| | WebSocket for read-only data | SSE (simpler) | | No auth on WebSocket | Auth in middleware | | Emit to all clients | Use rooms/namespaces | | No reconnect logic | Always implement reconnect | | Store socket connections in DB | Use in-memory + Redis |
Auf GitHub ansehen