Sofortantwort
Server-Sent Events einfach erklärt
Server sendet kontinuierlich Daten an den Browser – ideal für KI-Streaming-Antworten.
- Kurz gesagt
- Einweg-Kommunikation: Server → Client (kein Client → Server über dieselbe Verbindung)
- Typischer Einsatz
- LLM-Token-Streaming, Live-Dashboards, Benachrichtigungen
- Wichtig zu wissen
- Ideal für KI-Streaming: Token für Token in Echtzeit an den Browser senden
Server-Sent Events im Überblick
Klassische HTTP-Anfragen sind Request-Response: Der Client fragt, der Server antwortet, die Verbindung ist geschlossen. Für Echtzeit-Updates müsste der Client ständig neu anfragen (Polling) – ineffizient.
SSE hält die Verbindung offen: Der Server kann jederzeit neue Daten senden, ohne dass der Client erneut fragt. Das ist der Grund, warum ChatGPT Tokens einzeln erscheinen statt alles auf einmal: Das Modell streamt über SSE, Token für Token.
SSE vs. WebSockets vs. Polling:
| Aspekt | Polling | SSE | WebSockets |
|---|---|---|---|
| Richtung | Client → Server | Server → Client | Bidirektional |
| Protokoll | HTTP | HTTP | WS |
| Reconnect | Manuell | Automatisch | Manuell |
| Komplexität | Niedrig | Niedrig | Mittel |
| Use Case | Einfache Updates | Streaming, Feeds | Chat, Games |
Technisch betrachtet
Server-Implementierung (Node.js / Express)
app.get('/api/generate', async (req, res) => {
const { prompt } = req.query;
// SSE-Headers setzen
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// LLM-Stream
const stream = await openai.chat.completions.create({
model: 'gpt-5',
messages: [{ role: 'user', content: prompt }],
stream: true,
});
for await (const chunk of stream) {
const token = chunk.choices[0]?.delta?.content || '';
if (token) {
res.write(`data: ${JSON.stringify({ token })}\n\n`);
}
}
res.write('data: [DONE]\n\n');
res.end();
});
Client-Implementierung (Browser)
const source = new EventSource(`/api/generate?prompt=${encodeURIComponent(prompt)}`);
let output = '';
source.onmessage = (event) => {
if (event.data === '[DONE]') {
source.close();
return;
}
const { token } = JSON.parse(event.data);
output += token;
document.getElementById('output').textContent = output;
};
source.onerror = () => source.close();
SSE-Datenformat
id: 1
event: token
data: {"token": "Hallo"}
id: 2
event: token
data: {"token": " Welt"}
data: [DONE]