GraphQL Subscriptions: So funktionieren sie auf Protokollebene | We.Inc
Was GraphQL Subscriptions sind, die Regel mit genau einem Root-Feld, die graphql-ws-Nachrichten Schritt für Schritt, die SSE-Option und wann Polling die bessere Wahl ist.
Eine GraphQL Subscription ist eine langlebige Operation: Der Client sendet ein Subscription-Dokument einmal, und der Server schickt jedes Mal ein neues Ergebnis, wenn das passende Ereignis eintritt. GraphQL legt den Transport nicht fest. In der Praxis läuft es über WebSockets mit dem graphql-ws-Protokoll oder über Server-Sent Events. Jede Subscription-Operation muss genau ein Root-Feld auswählen.
Subscriptions, Polling und erneutes Abfragen
Ansatz
Wie Updates ankommen
Passt gut für
Subscription
Der Server schickt pro Ereignis ein Ergebnis
Kleine, häufige Änderungen, die Nutzer sofort sehen wollen
Polling
Der Client wiederholt eine Query per Timer
Daten, die sich ab und zu ändern
Neu abfragen nach Aktion
Der Client fragt nach einem Nutzerschritt erneut ab
Ansichten, die sich nur ändern, wenn der Nutzer handelt
Push-Benachrichtigung
Eine Nachricht außerhalb der App
Mobile Nutzer mit geschlossener App
Der Stepper unten spielt eine Subscription über das graphql-ws-Protokoll Nachricht für Nachricht ab. Jeder Schritt zeigt den JSON-Frame, der über den Socket geht, und welche Seite ihn sendet, vom ersten Handshake bis zum abschließenden complete.
Drücke auf Weiter, um die Unterhaltung Frame für Frame durchzugehen.
Lies, wer jeden Frame sendet und was er bedeutet.
Achte auf die Operations-ID: Jeder Frame einer Subscription trägt dieselbe ID.
Mit Zurück oder Neu starten siehst du dir einen Schritt noch einmal an.
Fang mit Polling an und wechsle erst zu einer Subscription, wenn ein paar Sekunden Verzögerung das Erlebnis wirklich stören.
Schicke in jedem Ergebnis ein kleines Ereignis, etwa die neue Nachricht, statt der ganzen Liste, und lass den Client es einarbeiten.
Plane Reconnects ein: Nach einem abgebrochenen Socket muss der Client sich neu anmelden und alles nachladen, was er verpasst hat.
Prüfe die Authentifizierung beim Öffnen der Verbindung und noch einmal in jedem Resolver, denn die Rechte eines Nutzers können sich während einer langen Sitzung ändern.
Häufige Fragen
Warum nur ein Root-Feld pro Subscription?
Jede Subscription entspricht einem Ereignisstrom. graphql.org zeigt, dass zwei Root-Felder in einer Subscription-Operation ungültig sind; schreibe stattdessen zwei benannte Subscriptions.
Welches WebSocket-Subprotokoll verwendet graphql-ws?
Es handelt beim Handshake graphql-transport-ws aus, mit Frame-Typen wie connection_init, subscribe, next und complete. Andere GraphQL-WebSocket-Bibliotheken nutzen eventuell ein anderes Protokoll, prüfe also zuerst, ob Client und Server sich einig sind, bevor du etwas anderes debuggst.
Laufen Subscriptions auch ohne WebSockets?
Ja. Das graphql-sse-Protokoll sendet jedes Ergebnis als Server-Sent Event namens next und endet mit complete. Es funktioniert über einfaches HTTP, was hinter Proxys hilft, die WebSocket-Upgrades blockieren.
Skalieren Subscriptions wie Queries?
Nein. graphql.org weist darauf hin, dass jeder abonnierte Client an eine Serverinstanz gebunden ist und für seine gesamte Lebensdauer Zustand braucht, daher benötigst du zusätzlich ein Pub/Sub-System, um Ereignisse auf mehrere Server zu verteilen.
Was beim Abonnieren passiert
Der Client sendet ein Dokument, das mit dem Schlüsselwort subscription beginnt, ein Root-Feld auswählt und die Felder aufführt, die er in jedem Ereignis haben will. Der Server validiert es einmal und hält es offen.
Wenn etwas ein passendes Ereignis veröffentlicht, etwa eine neue Bewertung, wendet der Server das Selection Set auf dieses Ereignis an und sendet das Ergebnis. Jedes Ergebnis hat die übliche Form mit data und errors, sodass der Client-Code es genauso liest wie eine Query.
Das graphql-ws-Protokoll in Kürze
Nach dem WebSocket-Upgrade sendet der Client connection_init, oft mit einem Auth-Token im Payload, und wartet auf connection_ack. Erst dann darf er subscribe-Frames senden. Jedes subscribe trägt eine eindeutige ID, und jeder next-, error- oder complete-Frame dieser Operation wiederholt sie, sodass viele Subscriptions einen Socket teilen.
Jede Seite kann ping senden, die andere antwortet mit pong. Der Server schließt den Socket mit Code 4408, wenn connection_init nicht rechtzeitig ankommt, und mit 4409, wenn ein Client eine ID wiederverwendet, die noch läuft.
Wann Polling die bessere Wahl ist
Subscriptions brauchen Sticky Connections, eine Pub/Sub-Schicht und sorgfältigen Client-Code für das erneute Abonnieren und Race Conditions. Für ein Dashboard, das sich jede Minute aktualisiert, ist eine Query per Timer einfacher und scheitert glimpflicher.
graphql.org empfiehlt Subscriptions für Daten, die sich oft und in kleinen Schritten ändern, und Polling, Push-Benachrichtigungen oder erneutes Abfragen für langsamere Daten oder mobile Clients mit wenig Bandbreite.
Schema und Client-Code
Ein Subscription-Feld deklarieren
Subscription ist ein Root-Typ wie Query und Mutation. Jedes Feld ist ein Strom, den ein Client öffnen kann.
Mit dem graphql-ws-Client abonnieren
createClient öffnet den Socket erst bei Bedarf, und iterate gibt einen asynchronen Iterator zurück, der pro Ereignis ein Ergebnis liefert.
Die Regel mit genau einem Root-Feld
Zwei Ströme in zwei Operationen aufteilen
Eine einzelne Subscription mit reviewCreated und einem weiteren Root-Feld scheitert an der Validierung. Zwei benannte Operationen funktionieren.
Client sendet connection_init
Wird direkt nach dem Öffnen des Sockets gesendet. Der Payload ist frei gestaltbar; Server lesen daraus oft ein Token.
Server sendet connection_ack
Der Server hat die Verbindung akzeptiert. Vorher würde ein subscribe-Frame den Socket mit 4401 Unauthorized schließen.
Client sendet subscribe
Die ID wählt der Client, und sie muss unter seinen laufenden Operationen eindeutig sein. Der Payload sind die übliche Query und die Variablen.
Server sendet next
Eine Bewertung wurde gepostet, also hat der Server das Selection Set ausgeführt und das Ergebnis unter derselben ID gesendet.
Server sendet erneut next
Ein weiteres Ereignis, ein weiterer Frame. Das wiederholt sich, solange die Subscription offen bleibt.
Client sendet complete
Der Nutzer hat die Seite verlassen, also beendet der Client die Operation. Der Socket kann für andere Subscriptions offen bleiben.