Erste Schritte mit Server-Side Events
Entdecke die Möglichkeiten von Server-Side Events (SSE) – diese Technologie ermöglicht es, dass dein Client im Browser Echtzeit-Updates vom Server erhält. In meinem Projekt habe ich SSE erfolgreich eingesetzt und skizziere, wie du es in dein nächstes Node.js + React-Projekt integrieren kannst.

Hintergrund
Im Rahmen eines Projekts stand ich vor der Herausforderung, den Nutzer im Frontend zu informieren, sobald die von ihm/ihr angestoßene Hintergrundaufgabe erledigt wurde. Als erste Lösungsidee kamen mir in der Vergangenheit zwei Ansätze: Push oder Pull – Websockets oder regelmäßiges, clientseitiges Polling. Doch dank eines gemeinsamen Lunches mit einem befreundeten Techniker habe ich von Server-Side Events gehört. Eine gute Alternative, wenn die Rahmenbedingungen stimmen.
Was sind Server-Side Events?
Server-Side Events (auch bekannt als Server Sent Events) sind eine Technologie, die es ermöglicht, dass ein Client im Browser Echtzeit-Updates vom Server erhält. Dies ermöglicht eine effiziente und asynchrone Kommunikation zwischen dem Client und dem Server.
Die grundlegenden Schritte:
- Client-Anfrage: Der Client (z.B. ein Webbrowser) sendet eine Anfrage an den Server, um Ereignisse zu erkunden.
- Server-Antwort: Der Server teilt dem Client mit einer Liste der verfügbaren Ereignisse und einer URL für die Aktualisierung dieser Liste.
- Ereignis-Streaming: Wenn ein neues Ereignis im Server entsteht, informiert der Server den Client darüber und sendet das aktuelle Event direkt an den Client.
Vorteile von SSE
- Effiziente Kommunikation: Durch die asynchrone Übertragung von Daten wird die Last auf dem Network reduziert.
- Echtzeit-Updates: Der Client erhält Echtzeitdaten, was eine effiziente Kommunikation ermöglicht.
- Skalierbarkeit: SSE kann leicht in große Anwendungen integriert werden, da die Last unerheblich ist und die Verbindung zwischen Client und Server automatisch durch den Browser gewährleistet wird – inklusive Reconnect beim Verbindungsverlust.
- Einfachheit der Implementierung: Die Implementierung von SSE ist einfach und verständlich, was sie zu einer beliebten Technologie für Web-Anwendungen macht.
- Flexibilität: SSE kann in verschiedenen Anwendungsbereichen eingesetzt werden, wie z.B. Live-Updates auf Webseiten oder Echtzeit-Datenvisualisierung.
Wie nutze ich SSE in mein Projekt ein?
Einbindung in Node.js
Mithilfe des NPM Pakets better-sse lassen sich in Node.js und Express einfach Routen registrieren, bei denen sich der Client anmelden kann. Je Client wird eine Session gestartet, die an einem Kanal registriert werden kann. Auf einem Kanal publiziere ich Events, die per Broadcast an alle registrierten Clients verschickt werden. Um die Clients bei ihrer Anmeldung bereits mit Daten zu füllen, kann ich in der Route noch die initialen Werte für den jeweiligen Kanal festlegen.
Das dazugehörige Codebeispiel zeigt meine Hilfsmethode, die als Parameter einen Express-Router entgegennimmt und dort die Routen registriert. Des Weiteren habe ich die SSE-Channels in eigene Hilfsklassen ausgelagert.
// ServerSideEventRegisterRoutes.ts
import { createSession } from 'better-sse'
import type { Router } from 'express'
import DashboardChannel from '@/serverSideEvents/DashboardChannel'
const ServerSideEventRegisterRoutes = (app: Router) => {
app.get('/sse', async (req, res) => {
const session = await createSession(req, res)
PrescriptionChannel.prescriptionChannel.register(session)
DashboardChannel.dashboardChannel.register(session)
// initialize sse stream
PrescriptionChannel.publishLatestScanDateTime()
DashboardChannel.publishDashboardData()
})
}
// DashboardChannel.ts
import { createChannel } from 'better-sse'
// here you define your channel data format
type DashboardData = {...}
const dashboardChannel = () => {
const dashboardChannel = createChannel()
let dashboardData: DashboardData = { ... }
// update internal state of dashboard data
const setDashboardData = (updatedDashboardData: Partial<DashboardData>) => {
dashboardData = {
...dashboardData,
...updatedDashboardData,
}
}
// encapsulate logic to fetch dashboard data - alternatively use only the setDashboardData from the callee
const fetchDashboardData = async () => {
setDashboardData({ ... })
publishDashboardData()
}
// trigger to update all registered sessions of the channel "dashboardData"
const publishDashboardData = () => {
dashboardChannel.broadcast(dashboardData, 'dashboardData')
}
// make the methods defined above accesible
return {
dashboardChannel,
setDashboardData,
fetchDashboardData,
publishDashboardData,
}
}
export default dashboardChannel()
Hinweis für Swagger-Ui-Express Nutzer: ihr müsst die SSE Route zusätzlich zu den Swagger-Routen registrieren. Ich habe keinen Weg gefunden, die Routen mit der Swagger TSOA Syntax zu verheiraten.
Einbindung in React Frontend
In meinem Frontend-Code registriere ich mich beim Backend im useEffect-Hook der App()
. Damit stelle ich sicher, dass die Verbindung mit dem Start meiner Applikation hergestellt wird. Wenn ich meinen State mit einem Redux-Store verbinde, kann ich von überall im Komponentenbaum darauf zugreifen und die Daten anzeigen.
// App.ts
function App() {
useEffect(() => {
const eventSource = new EventSource(apiUrl)
// listen to events on the channel "dashboardData"
eventSource.addEventListener('dashboardData', (event: MessageEvent) => {
// ensure to sent valid json from server, else add try+catch here
const eventData = JSON.parse(event.data)
// work with eventData like storing it to store and render it somewhere in your app
})
}, []) // will run once on application start
// ...
}
Fazit
Mit der hier gezeigten Methode habe ich eine einfache Möglichkeit, vom Backend aus meine Clients mit Updates zu versorgen. Mit dem grundlegenden Ansatz, den ich im Artikel gezeigt habe, wird das Event von allen verbundenen Webseiten angezeigt. Dies kann ein gültiger Anwendungsfall sein. Aber vielleicht möchtest du die Nachricht nur dem Benutzer anzeigen, der ursprünglich das Event ausgelöst hat.
Der Ansatz ist, die ID des aktiven Nutzers bei der Anfrage mitzuschicken, später beim Event als Datum ins JSON zu schreiben und im addEventListener
mit der aktuellen ID in der Webseite abzugleichen. Wenn ich die Lösungsidee detaillierter skizzieren soll, melde ich dich gerne bei mir zum digitalen Kaffee oder schreib mir einen Kommentar.