Real-Time & WebSockets

WebSockets mit Claude Code: Real-Time Anwendungen 2026

Socket.io, native WebSocket API, Rooms, Broadcasting, Reconnection, Authentifizierung — Claude Code baut Real-Time-Chat, Live-Dashboard und Kollaborations-Apps.

📅 6. Mai 2026 ⏱ 11 min Lesezeit 🏷 TypeScript • Socket.io • Node.js

Inhalt

  1. WebSocket Grundlagen & Protokoll
  2. Socket.io Setup: Server & Client
  3. Rooms & Broadcasting
  4. Authentifizierung mit JWT
  5. Real-Time Patterns: Chat, Dashboard, Kollaboration
  6. Scaling & Production

Real-Time-Anwendungen sind 2026 kein Luxus mehr — sie sind Standard. Nutzer erwarten, dass Nachrichten sofort ankommen, Dashboards live aktualisieren und kollaborative Dokumente ohne Reload synchronisieren. WebSockets sind das Fundament dahinter, und Claude Code kann den gesamten Stack — Server, Client, Auth, Rooms und Redis-Scaling — in Minuten aufsetzen.

In diesem Artikel zeigen wir, wie du mit Claude Code produktionsreife WebSocket-Anwendungen entwickelst: vom nativen WS-Protokoll bis zum horizontal skalierten Multi-Server-Setup mit Redis Adapter.

< 1ms Latenz (LAN)
10k+ Concurrent Connections
100% Bidirektional

1. WebSocket Grundlagen & Protokoll

Das WebSocket-Protokoll (RFC 6455) ermöglicht eine persistente, bidirektionale TCP-Verbindung zwischen Browser und Server. Anders als HTTP sendet nicht nur der Client Anfragen — der Server kann jederzeit Daten pushen, ohne dass der Client pollt.

HTTP-Upgrade Handshake

Eine WebSocket-Verbindung beginnt als normaler HTTP-Request. Der Client sendet einen Upgrade: websocket-Header, der Server antwortet mit 101 Switching Protocols. Danach läuft die gesamte Kommunikation über das WS-Binärprotokoll (Frames).

HTTP Upgrade Handshake (vereinfacht)
// Client → Server GET /socket HTTP/1.1 Host: api.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13 // Server → Client HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Natives WebSocket API im Browser

client.ts — Natives Browser WebSocket
const ws = new WebSocket('wss://api.example.com/socket'); ws.onopen = () => { console.log('Verbindung hergestellt'); ws.send(JSON.stringify({ type: 'ping', timestamp: Date.now() })); }; ws.onmessage = (event) => { const data = JSON.parse(event.data); console.log('Empfangen:', data); }; ws.onclose = (event) => { console.log(`Verbunden getrennt: Code ${event.code}`); // Auto-Reconnect nach 3 Sekunden setTimeout(() => reconnect(), 3000); }; ws.onerror = (error) => console.error('WS Fehler:', error); // Ping/Pong Keepalive setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); } }, 30_000);

Native WS-Library (Node.js Server)

server.ts — ws Library
import { WebSocketServer, WebSocket } from 'ws'; import { createServer } from 'http'; const httpServer = createServer(); const wss = new WebSocketServer({ server: httpServer }); wss.on('connection', (socket, request) => { const clientIp = request.socket.remoteAddress; console.log(`Neuer Client: ${clientIp}`); socket.on('message', (rawData) => { try { const msg = JSON.parse(rawData.toString()); if (msg.type === 'ping') { socket.send(JSON.stringify({ type: 'pong', ts: Date.now() })); return; } // Broadcast an alle anderen Clients wss.clients.forEach((client) => { if (client !== socket && client.readyState === WebSocket.OPEN) { client.send(JSON.stringify(msg)); } }); } catch (e) { socket.send(JSON.stringify({ error: 'Ungültige JSON-Payload' })); } }); socket.on('close', () => console.log('Client getrennt')); }); httpServer.listen(3000, () => console.log('WS-Server auf Port 3000'));

Vergleich: WebSockets vs. SSE vs. Long-Polling

Merkmal WebSockets SSE Long-Polling
Bidirektional Ja Nein (nur Server→Client) Eingeschränkt
Latenz Sehr niedrig Niedrig Hoch
Overhead Minimal (Frames) Mittel Hoch (HTTP-Headers)
Browser-Support Exzellent Gut (kein IE) Universal
Load Balancer Sticky Sessions nötig Sticky Sessions Standard HTTP

Empfehlung: Für Chat, Spiele und Live-Kollaboration → WebSockets. Für Notifications und Feeds → SSE. Long-Polling nur als Legacy-Fallback oder bei strikten Firewall-Regeln.

2. Socket.io Setup: Server & Client

Socket.io ist die produktionsreife Abstraktionsschicht über WebSockets. Es bietet automatisches Reconnect, Namespaces, Rooms, Middleware-Support und einen transparenten Fallback auf Long-Polling (z.B. in restriktiven Unternehmensnetzwerken).

Installation

npm install socket.io socket.io-client npm install -D @types/node typescript tsx

Server Setup

server/index.ts
import { createServer } from 'http'; import { Server, Socket } from 'socket.io'; interface ServerToClientEvents { message: (payload: { from: string; text: string; ts: number }) => void; userJoined: (username: string) => void; userLeft: (username: string) => void; } interface ClientToServerEvents { sendMessage: (text: string) => void; joinRoom: (room: string) => void; } interface SocketData { userId: string; username: string; } const httpServer = createServer(); const io = new Server<ClientToServerEvents, ServerToClientEvents, {}, SocketData>(httpServer, { cors: { origin: process.env.ALLOWED_ORIGIN || 'http://localhost:5173', methods: ['GET', 'POST'], }, pingTimeout: 60_000, pingInterval: 25_000, }); io.on('connection', (socket: Socket) => { console.log(`Client verbunden: ${socket.id}`); socket.on('sendMessage', (text) => { const payload = { from: socket.data.username || 'Anonym', text, ts: Date.now(), }; // An alle in allen Rooms dieses Sockets senden io.emit('message', payload); }); socket.on('disconnect', () => { console.log(`Client getrennt: ${socket.id}`); }); }); httpServer.listen(3001, () => { console.log('Socket.io Server läuft auf :3001'); });

Client Setup (React + TypeScript)

client/useSocket.ts
import { useEffect, useRef, useState } from 'react'; import { io, Socket } from 'socket.io-client'; export function useSocket(url: string) { const socketRef = useRef<Socket | null>(null); const [isConnected, setIsConnected] = useState(false); const [messages, setMessages] = useState<{ from: string; text: string }[]>([]); useEffect(() => { socketRef.current = io(url, { autoConnect: true, reconnection: true, reconnectionDelay: 1000, reconnectionDelayMax: 10_000, reconnectionAttempts: Infinity, }); const socket = socketRef.current; socket.on('connect', () => { setIsConnected(true); console.log(`Verbunden: ${socket.id}`); }); socket.on('disconnect', () => setIsConnected(false)); socket.on('message', (payload) => { setMessages((prev) => [...prev, payload]); }); return () => { socket.disconnect(); }; }, [url]); const sendMessage = (text: string) => { socketRef.current?.emit('sendMessage', text); }; return { isConnected, messages, sendMessage }; }
Claude Code Prompt

Socket.io Projekt bootstrap

Prompt: "Erstelle ein Socket.io-Projekt mit TypeScript, Express-Backend, React-Frontend und vollständiger Type-Safety für Server- und Client-Events. Inkl. CORS-Konfiguration und Reconnect-Logik."

3. Rooms & Broadcasting

Rooms sind Socketio's Mechanismus, um Clients in logische Gruppen einzuteilen. Ein Client kann in mehreren Rooms gleichzeitig sein. Nachrichten können gezielt an einzelne Rooms, an alle außer dem Sender oder an alle Verbundenen gesendet werden.

Room Management

Emit-Varianten im Überblick

Rooms implementieren

server/rooms.ts
import { Server, Socket } from 'socket.io'; interface JoinRoomPayload { room: string; username: string; } interface MessagePayload { room: string; text: string; } export function registerRoomHandlers(io: Server, socket: Socket) { // Room beitreten socket.on('joinRoom', async ({ room, username }: JoinRoomPayload) => { await socket.join(room); socket.data.username = username; socket.data.currentRoom = room; // Alle anderen im Room informieren socket.to(room).emit('userJoined', { username, socketId: socket.id, ts: Date.now(), }); // Aktuelle Room-Mitglieder an neuen User senden const sockets = await io.in(room).fetchSockets(); const members = sockets.map((s) => ({ id: s.id, username: s.data.username, })); socket.emit('roomMembers', members); console.log(`${username} trat Room "${room}" bei (${sockets.length} Mitglieder)`); }); // Nachricht an Room senden socket.on('roomMessage', ({ room, text }: MessagePayload) => { if (!socket.rooms.has(room)) { socket.emit('error', { msg: 'Du bist nicht in diesem Room' }); return; } io.to(room).emit('message', { from: socket.data.username, text, room, ts: Date.now(), }); }); // Private Nachricht (1:1) socket.on('privateMessage', ({ toSocketId, text }: { toSocketId: string; text: string }) => { socket.to(toSocketId).emit('privateMessage', { from: socket.data.username, text, ts: Date.now(), }); }); // Room verlassen socket.on('leaveRoom', async (room: string) => { await socket.leave(room); io.to(room).emit('userLeft', { username: socket.data.username, ts: Date.now(), }); }); }

4. Authentifizierung mit JWT

Produktive WebSocket-Apps brauchen sichere Authentifizierung. Der empfohlene Ansatz: JWT im socket.handshake.auth-Objekt übergeben und in einer Socket.io-Middleware validieren. Fehlende oder ungültige Tokens führen zum Disconnect.

JWT Auth Middleware (Server)

server/middleware/auth.ts
import { Server, Socket } from 'socket.io'; import jwt from 'jsonwebtoken'; interface JwtPayload { sub: string; username: string; roles: string[]; } const JWT_SECRET = process.env.JWT_SECRET!; export function applyAuthMiddleware(io: Server) { io.use(async (socket: Socket, next) => { const token = socket.handshake.auth?.token as string | undefined; if (!token) { return next(new Error('Authentifizierung erforderlich')); } try { const payload = jwt.verify(token, JWT_SECRET) as JwtPayload; // User-Daten am Socket speichern socket.data.userId = payload.sub; socket.data.username = payload.username; socket.data.roles = payload.roles; next(); } catch (err) { if (err instanceof jwt.TokenExpiredError) { return next(new Error('Token abgelaufen — bitte neu einloggen')); } return next(new Error('Ungültiges Token')); } }); }

Token im Client übergeben

client/socket.ts
import { io } from 'socket.io-client'; function createAuthenticatedSocket(token: string) { return io('https://api.example.com', { auth: { token }, reconnection: true, // Token bei Reconnect erneuern reconnectionDelay: 1000, }); } // Bei Token-Ablauf: neuen Token holen und reconnecten const socket = createAuthenticatedSocket(localStorage.getItem('auth_token') || ''); socket.on('connect_error', async (err) => { if (err.message.includes('abgelaufen')) { // Refresh Token → neues JWT holen const newToken = await refreshAuthToken(); localStorage.setItem('auth_token', newToken); socket.auth = { token: newToken }; socket.connect(); } });

Role-Based Guards

server/guards.ts
import { Socket } from 'socket.io'; export function requireRole(role: string) { return (socket: Socket, next: (err?: Error) => void) => { const roles: string[] = socket.data.roles || []; if (!roles.includes(role)) { socket.disconnect(); return; } next(); }; } // Usage im Event-Handler: io.on('connection', (socket) => { socket.on('adminBroadcast', (msg) => { if (!socket.data.roles?.includes('admin')) { socket.emit('error', { msg: 'Keine Berechtigung' }); return; } io.emit('systemMessage', msg); }); });

Sicherheitshinweis: JWT-Secret niemals im Frontend exponieren. Tokens mit kurzer Lebensdauer (15 min) + Refresh-Token-Mechanismus nutzen. Sensitive Events immer serverseitig validieren — Client-seitige Checks sind nur UX, keine Sicherheit.

5. Real-Time Patterns: Chat, Dashboard & Kollaboration

Pattern 1

Real-Time Chat mit Message-History

server/chat.ts
import { Server, Socket } from 'socket.io'; import { Redis } from 'ioredis'; const redis = new Redis(process.env.REDIS_URL!); const MSG_TTL = 60 * 60 * 24 * 7; // 7 Tage interface ChatMessage { id: string; room: string; from: string; text: string; ts: number; } export function registerChatHandlers(io: Server, socket: Socket) { // Letzte 50 Nachrichten beim Join laden socket.on('joinRoom', async (room: string) => { await socket.join(room); const history = await redis.lrange(`chat:${room}:messages`, -50, -1); const parsed: ChatMessage[] = history.map((m) => JSON.parse(m)); socket.emit('messageHistory', parsed); }); socket.on('sendMessage', async ({ room, text }: { room: string; text: string }) => { const msg: ChatMessage = { id: crypto.randomUUID(), room, from: socket.data.username, text: text.slice(0, 2000), // Längen-Limit ts: Date.now(), }; // In Redis persistieren const key = `chat:${room}:messages`; await redis.rpush(key, JSON.stringify(msg)); await redis.expire(key, MSG_TTL); // An alle im Room broadcasten io.to(room).emit('message', msg); }); // Typing-Indicator socket.on('typing', (room: string) => { socket.to(room).emit('userTyping', { username: socket.data.username }); }); }
Pattern 2

Live-Collaboration: Cursor-Tracking

server/collaboration.ts
interface CursorPosition { userId: string; username: string; x: number; y: number; color: string; } const USER_COLORS = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6']; const cursors = new Map<string, CursorPosition>(); export function registerCollabHandlers(io: Server, socket: Socket) { const color = USER_COLORS[cursors.size % USER_COLORS.length]; socket.on('joinDocument', (docId: string) => { socket.join(`doc:${docId}`); // Bestehende Cursors an neuen User senden socket.emit('cursors', Array.from(cursors.values())); }); socket.on('cursorMove', ({ docId, x, y }: { docId: string; x: number; y: number }) => { const position: CursorPosition = { userId: socket.data.userId, username: socket.data.username, x, y, color, }; cursors.set(socket.id, position); // Throttled: nur alle 50ms broadcasten (serverseitig) socket.to(`doc:${docId}`).emit('cursorUpdate', position); }); socket.on('disconnect', () => { cursors.delete(socket.id); io.emit('cursorRemoved', socket.data.userId); }); }
Pattern 3

Live-Dashboard: Server-Metriken in Echtzeit

server/metrics.ts
import os from 'os'; import { Server } from 'socket.io'; interface SystemMetrics { cpuLoad: number[]; memUsed: number; memTotal: number; uptime: number; connections: number; ts: number; } export function startMetricsBroadcast(io: Server) { setInterval(async () => { const sockets = await io.fetchSockets(); const metrics: SystemMetrics = { cpuLoad: os.loadavg(), memUsed: process.memoryUsage().heapUsed, memTotal: os.totalmem(), uptime: process.uptime(), connections: sockets.length, ts: Date.now(), }; // Nur an Dashboard-Room senden (Admin-only) io.to('dashboard').emit('metrics', metrics); }, 2000); // Alle 2 Sekunden } // Client: Dashboard abonnieren const socket = io('https://api.example.com', { auth: { token } }); socket.emit('joinRoom', 'dashboard'); socket.on('metrics', (data: SystemMetrics) => { updateChart(data); // z.B. Chart.js oder Recharts });

6. Scaling & Production

Ein einzelner Node.js-Prozess kommt an seine Grenzen. Für produktive Anwendungen mit horizontalem Scaling (mehrere Server-Instanzen hinter einem Load Balancer) braucht Socket.io einen Redis Adapter, der Events zwischen den Instanzen synchronisiert.

Skalierung

Das Problem ohne Adapter

Client A ist auf Server 1, Client B auf Server 2. Client A sendet eine Nachricht → nur Clients auf Server 1 empfangen sie. Client B sieht nichts. Der Redis Adapter löst das durch Pub/Sub zwischen den Instanzen.

Redis Adapter Setup

server/redis-adapter.ts
import { Server } from 'socket.io'; import { createAdapter } from '@socket.io/redis-adapter'; import { createClient } from 'redis'; export async function setupRedisAdapter(io: Server) { const pubClient = createClient({ url: process.env.REDIS_URL }); const subClient = pubClient.duplicate(); await Promise.all([pubClient.connect(), subClient.connect()]); io.adapter(createAdapter(pubClient, subClient)); console.log('Redis Adapter aktiv — Multi-Instance Scaling möglich'); // Graceful Shutdown process.on('SIGTERM', async () => { await Promise.all([pubClient.quit(), subClient.quit()]); io.close(); }); }

Nginx: Sticky Sessions & WebSocket Proxy

nginx.conf
upstream socketio_backend { # Sticky Sessions via IP-Hash (alternativ: least_conn) ip_hash; server 127.0.0.1:3001; server 127.0.0.1:3002; server 127.0.0.1:3003; } server { listen 443 ssl http2; server_name api.example.com; location /socket.io/ { proxy_pass http://socketio_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 86400s; proxy_send_timeout 86400s; } }

Socket.io Admin UI

server/admin.ts
import { Server } from 'socket.io'; import { instrument } from '@socket.io/admin-ui'; import bcrypt from 'bcryptjs'; export function enableAdminUI(io: Server) { instrument(io, { auth: { type: 'basic', username: 'admin', password: bcrypt.hashSync(process.env.ADMIN_PASSWORD!, 10), }, readonly: false, namespaceName: '/admin', }); console.log('Socket.io Admin UI: https://admin.socket.io'); }
Production Checklist

Vor dem Go-Live

Horizontal Scaling: PM2 Cluster Mode

ecosystem.config.js
module.exports = { apps: [{ name: 'websocket-server', script: 'dist/index.js', instances: 'max', // Alle CPU-Kerne nutzen exec_mode: 'cluster', env: { NODE_ENV: 'production', REDIS_URL: 'redis://localhost:6379', }, watch: false, max_restarts: 10, restart_delay: 4000, }] };

Claude Code und WebSockets: Mit dem richtigen Prompt generiert Claude Code den kompletten Stack — Socket.io Server mit Auth-Middleware, Redis Adapter, React-Hooks für den Client, Nginx-Konfiguration und PM2-Setup — in einem einzigen Session. Iteriere mit natürlichsprachigen Prompts statt stundenlanger Dokumentation.

Real-Time Apps mit Claude Code bauen

Starte kostenlos und lass Claude Code deinen WebSocket-Stack generieren — von der ersten Verbindung bis zum Redis-Adapter in Production.

Kostenlos starten →

Kein Kreditkarte erforderlich • Sofort starten

Weitere Artikel