---
term: 'GraphQL Subscriptions vs. WebSockets in verwalteten Backends'
seoTitle: 'GraphQL Subscriptions vs. WebSockets: Protokoll vs. Transport'
headline: 'GraphQL Subscriptions vs. WebSockets: Was wird hier eigentlich verglichen?'
slug: graphql-subscriptions-vs-websockets
category: api-realtime
shortDefinition: 'Eine GraphQL-Subscription ist ein typisierter, im Schema definierter Ereignisstrom; ein WebSocket ist der rohe Transport, auf dem sie meist läuft.'
relatedTerms:
  - graphql
  - websockets-real-time-sync
  - real-time-live-queries
  - sse-vs-websockets-vs-polling
contrastsWith:
  - websockets-real-time-sync
aboutTerms:
  - 'GraphQL Subscriptions'
  - 'WebSockets'
faq:
  - question: 'Sind GraphQL-Subscriptions dasselbe wie WebSockets?'
    answer: 'Nein – sie liegen in verschiedenen Schichten. Ein WebSocket ist ein Transport: eine dauerhafte, vollduplexe Byte-Leitung ohne jede Meinung darüber, was sie durchquert. Eine GraphQL-Subscription ist ein Protokoll und ein Vertrag darüber: eine im Schema definierte Operation, deren Ereignisse typisiert, validiert und exakt so geformt ankommen wie jede andere GraphQL-Antwort. Beide direkt zu vergleichen heißt, eine Straße mit einer Buslinie zu vergleichen.'
  - question: 'Welches Protokoll verwenden GraphQL-Subscriptions?'
    answer: 'Meistens graphql-ws, das moderne Subprotokoll für GraphQL über WebSocket: Der Client öffnet einen Socket, sendet connection_init, erhält connection_ack und startet dann Operationen mit subscribe-Nachrichten; der Server streamt next-Payloads pro Subscription-ID, und eine der beiden Seiten beendet mit complete. Ein älteres Subprotokoll aus der Frühzeit des Ökosystems ist weiterhin im Umlauf – deshalb müssen Client und Server sich darauf einigen, welches sie sprechen.'
  - question: 'Können GraphQL-Subscriptions stattdessen über Server-Sent Events laufen?'
    answer: 'Ja – der Subscription-Vertrag ist transportunabhängig, und SSE ist ein legitimer Träger, der an Verbreitung gewinnt. Da Subscription-Verkehr weit überwiegend vom Server zum Client fließt, passt ein einseitiger HTTP-Stream von Natur aus, schont gewöhnliche Proxys und die HTTP-Semantik und bringt automatische Wiederverbindung gratis mit. WebSockets bleiben in den meisten Werkzeugen der Standard, aber "Subscriptions brauchen WebSockets" ist Folklore, keine Tatsache.'
  - question: 'Wann sollte ich rohe WebSockets statt GraphQL-Subscriptions verwenden?'
    answer: 'Wenn der Verkehr aufhört, wie typisierte API-Ereignisse auszusehen: binäre Frames (Audio, Spielzustand, Sensorströme), sehr hohe Nachrichtenraten, bei denen Schemavalidierung und JSON-Hülle pro Ereignis echten Durchsatz kosten, oder Protokolle mit eigener Semantik – Cursor, Deltas, Bestätigungen –, die sich an der Subscription-Form reiben. Wenn Sie ohnehin nicht in ein GraphQL-Schema investiert haben, erspart ein roher Socket Ihnen auch, eines allein für Ereignisse einzuführen.'
  - question: 'Skalieren GraphQL-Subscriptions?'
    answer: 'Der Transport skaliert wie jede WebSocket-Flotte – Verbindungszustand, Routing mit Affinität und ein Pub/Sub-Backplane zwischen den Servern. Die Subscription-Schicht bringt eine eigene Achse mit: Jedes Ereignis kann pro Abonnent aufgelöst und gefiltert werden, sodass ein stark genutztes Topic mit vielen Abonnenten die Resolver-Arbeit vervielfacht. Verwaltete Plattformen übernehmen die Flotte; Schemaentwurf und die Disziplin beim Filtern pro Abonnent bleiben Ihre Sache.'
  - question: 'Sind GraphQL-Subscriptions dasselbe wie Live Queries?'
    answer: 'Nahe Verwandte mit unterschiedlichen Auslösern. Subscriptions feuern bei benannten Ereignissen, die Sie explizit verdrahten – eine Mutation veröffentlicht auf ein Topic, Abonnenten empfangen. Live Queries feuern bei Änderungen der Ergebnismenge einer Query – keine Ereignisverdrahtung, jeder Schreibpfad automatisch abgedeckt. Beide laufen üblicherweise über WebSockets. Eine Live-Query-Schicht tauscht den schemagetriebenen Ereignisentwurf gegen automatische Änderungserkennung auf den Daten selbst.'
  - question: 'Warum brauchen Subscriptions überhaupt ein Subprotokoll?'
    answer: 'Weil ein nackter WebSocket nur geordnete Bytes sind. In dem Moment, in dem zwei Parteien mehrere Subscriptions über einen Socket multiplexen, Ereignisse zu Operationen zuordnen, die Authentifizierung aushandeln, Fehler signalisieren und Streams sauber beenden müssen, brauchen sie ein Nachrichten-Framing und Regeln – die früher jedes Team schlecht erfunden hat, jedes in seiner eigenen inkompatiblen Version. graphql-ws standardisiert genau diese Schicht, damit Clients und Server zusammenarbeiten können.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Subscriptions — graphql.org'
    url: 'https://graphql.org/learn/subscriptions/'
  - name: 'GraphQL over WebSocket Protocol (graphql-ws)'
    url: 'https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md'
  - name: 'RFC 6455 — The WebSocket Protocol'
    url: 'https://datatracker.ietf.org/doc/html/rfc6455'
  - name: 'The WebSocket API — MDN Web Docs'
    url: 'https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API'
  - name: 'GraphQL — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/GraphQL'
  - name: 'WebSocket — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/WebSocket'
cta:
  title: 'Echtzeit, ohne eine der beiden Schichten zu besitzen'
  text: 'Back4app verbindet eine automatisch generierte GraphQL-API mit Live Queries – typisierte, berechtigungsgeprüfte Abonnements über eine verwaltete WebSocket-Flotte. Schema definieren, Query abonnieren und die gesamte Protokollarbeit überspringen.'
  linkText: 'Kostenlos starten'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-09-28'
translationKey: graphql-subscriptions-vs-websockets
---

**Eine GraphQL-Subscription ist ein typisierter, im Schema definierter Ereignisstrom; ein WebSocket ist der rohe Transport, auf dem sie meist läuft.** Das "vs." im Titel verbirgt eine Schichtenverwechslung, die man besser auflöst, bevor irgendeine Entscheidung fällt: Subscriptions sind keine *Alternative* zu WebSockets – sie sind eines der Dinge, die man *über* einen WebSocket laufen lassen kann, so wie HTTP über TCP läuft. Die echte Wahl steht zwischen einem typisierten Protokoll, das jemand anders spezifiziert hat, und einer rohen Leitung, deren Protokoll Sie erfinden.

## Das Wichtigste in Kürze

| Frage | Antwort |
| --- | --- |
| WebSocket | Eine dauerhafte, vollduplexe Byte-Leitung – ohne jede Nachrichtensemantik |
| GraphQL-Subscription | Ein im Schema definierter Ereignisstrom, typisiert und validiert wie jede GraphQL-Antwort |
| Ihr Verhältnis | Geschichtet, nicht rivalisierend – Subscriptions laufen über WebSockets (oder SSE) via Subprotokoll |
| Das Subprotokoll | graphql-ws: init/ack-Handshake, IDs multiplexen Operationen, next/complete-Frames |
| Die eigentliche Entscheidung | Typisiertes Protokoll von der Stange vs. roher Socket + ein Protokoll, das nun Ihnen gehört |

## Die Schichtung, in Code

Was eine Subscription auf der Leitung tatsächlich ist – eine [graphql-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md)-Konversation innerhalb eines WebSockets:

```json
// Client → Server, nachdem der Socket geöffnet ist
{ "type": "connection_init", "payload": { "authToken": "…" } }
// Server → Client
{ "type": "connection_ack" }
// Der Client startet eine Operation – die id multiplext diese Subscription
{ "type": "subscribe", "id": "1", "payload": {
    "query": "subscription { orderUpdated(status: PREPARING) { id status eta } }" } }
// Der Server streamt typisierte Ereignisse, ein Frame pro Vorkommen
{ "type": "next", "id": "1", "payload": { "data": { "orderUpdated": { "id": "o42", "status": "READY", "eta": null } } } }
// Eine der beiden Seiten beendet den Stream
{ "type": "complete", "id": "1" }
```

Und was der meiste Anwendungscode tatsächlich schreibt – eine typisierte Subscription mit vollständig verwaltetem Stack:

**JavaScript:**

```javascript
// JavaScript — Back4app JS SDK
// A typed subscription over a managed WebSocket fleet: the protocol,
// reconnects, and fan-out are the platform's problem, not yours
const orders = new Parse.Query('Order');
orders.equalTo('status', 'preparing');

const sub = await orders.subscribe();
sub.on('create', (o) => addCard(o));      // typed event, full object
sub.on('update', (o) => refreshCard(o));
sub.on('leave',  (o) => removeCard(o));   // edited out of the result set

sub.on('close', () => showOfflineBadge()); // socket lifecycle surfaced
```

**Flutter:**

```dart
// Flutter / Dart — Back4app Flutter SDK
// A typed subscription over a managed WebSocket fleet: the protocol,
// reconnects, and fan-out are the platform's problem, not yours
final liveQuery = LiveQuery();
final orders = QueryBuilder<ParseObject>(ParseObject('Order'))
  ..whereEqualTo('status', 'preparing');

final sub = await liveQuery.client.subscribe(orders);
sub.on(LiveQueryEvent.create, (o) => addCard(o));    // typed event
sub.on(LiveQueryEvent.update, (o) => refreshCard(o));
sub.on(LiveQueryEvent.leave, (o) => removeCard(o));  // left the set
```

**Swift:**

```swift
// iOS / Swift — Back4app Swift SDK
// A typed subscription over a managed WebSocket fleet: the protocol,
// reconnects, and fan-out are the platform's problem, not yours
let orders = Order.query("status" == "preparing")

let subscription = orders.subscribeCallback
subscription?.handleEvent { _, event in
  switch event {
  case .created(let o): addCard(o)       // typed event, full object
  case .updated(let o): refreshCard(o)
  case .left(let o):    removeCard(o)    // edited out of the result set
  default:              break
  }
}
```

**Kotlin:**

```kotlin
// Android / Kotlin — Back4app Android SDK
// A typed subscription over a managed WebSocket fleet: the protocol,
// reconnects, and fan-out are the platform's problem, not yours
val client = ParseLiveQueryClient.Factory.getClient()
val orders = ParseQuery.getQuery<ParseObject>("Order")
orders.whereEqualTo("status", "preparing")

val sub = client.subscribe(orders)
sub.handleEvent(SubscriptionHandling.Event.CREATE) { _, o -> addCard(o) }
sub.handleEvent(SubscriptionHandling.Event.UPDATE) { _, o -> refreshCard(o) }
sub.handleEvent(SubscriptionHandling.Event.LEAVE)  { _, o -> removeCard(o) }
```

## Ein Stack, drei Schichten

```mermaid
flowchart TB
  accTitle: GraphQL-Subscriptions über einem WebSocket-Transport geschichtet
  accDescr: Anwendungscode konsumiert typisierte Ereignisse. Darunter übernimmt ein Subscription-Protokoll wie graphql-ws Handshake, Multiplexing und den Lebenszyklus der Streams. Noch darunter ist die Transportschicht meist ein WebSocket und manchmal Server-Sent Events. Der Transport bewegt Bytes; das Protokoll gibt ihnen Bedeutung; das Schema gibt ihnen Typen.
  A["Anwendungscode<br/>typisierte Ereignisse, vom Schema geformte Payloads"] --> P["Subscription-Protokoll — graphql-ws<br/>connection_init/ack · subscribe · next · complete"]
  P --> T["Transport — meist WebSocket, manchmal SSE<br/>dauerhafte Verbindung, geordnete Zustellung"]
  T --> N["TCP/IP"]
```

Die [WebSocket](/glossary/de/websockets/)-Schicht ([RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455)) verspricht genau so viel: einen dauerhaften, vollduplexen, geordneten Strom von Text- oder Binär-Frames, aus jedem Browser über [eine kleine API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) erreichbar. Sie sagt nichts darüber, was eine Nachricht *bedeutet* – keine Zuordnung von Anfrage und Antwort, keine Authentifizierungskonvention, keine Fehlersignalisierung, keine Möglichkeit, zwei logische Ströme über einen Socket zu führen. Jedes Projekt auf rohen Sockets entscheidet all das neu.

Die [Subscription-Schicht](https://graphql.org/learn/subscriptions/) ist genau dieser fehlende Entscheidungssatz, standardisiert: `connection_init`/`connection_ack` trägt die Authentifizierung; `id`s pro Operation multiplexen viele Subscriptions über einen Socket; `next`-Frames liefern Payloads, die gewöhnliche GraphQL-Antworten sind – vom Schema typisiert, validiert, introspizierbar, von derselben Client-Mechanik verarbeitet wie Queries und Mutationen; `complete` beendet einen Stream, ohne seine Nachbarn zu töten. Eine historische Fußnote zählt in der Praxis: Ein älteres Subprotokoll aus der Frühzeit des Ökosystems ist weiterhin im Einsatz, und ein Client, der das eine spricht, scheitert an einem Server, der das andere spricht, auf verwirrend stille Weise – legen Sie das Subprotokoll an beiden Enden explizit fest.

Und weil der Vertrag über Nachrichten läuft, nicht über Sockets, ist der Transport darunter austauschbar – dieselbe Subscription-Semantik läuft zunehmend über Server-Sent Events, was zur weit überwiegend einseitigen Form des Subscription-Verkehrs passt und [die Proxy-Freundlichkeit und automatische Wiederverbindung von SSE](/glossary/de/sse-vs-websockets-vs-polling/) erbt. "Subscriptions vs. WebSockets" löst sich bei Berührung auf: Das eine ist ein Vertrag, das andere ein Träger.

## GraphQL-Subscriptions vs. rohe WebSockets

| | GraphQL-Subscriptions | Rohe WebSockets |
| --- | --- | --- |
| Schicht | Protokoll + Typsystem über einem Transport | Der Transport selbst |
| Nachrichtenvertrag | Im Schema definiert, validiert, introspizierbar | Was immer Sie erfinden und dokumentieren |
| Multiplexing | Eingebaut – IDs pro Operation auf einem Socket | Selbst zu entwerfen |
| Auth-Handshake | Standardisiert (`connection_init`-Payload) | Selbst zu entwerfen |
| Payloads | JSON, vom Schema geformt | Text **und binär**, jedes Format |
| Filterung | Argumente am Subscription-Feld | Servercode, den Sie schreiben |
| Overhead pro Ereignis | JSON-Hülle + Resolver-Ausführung | ~2–14 Byte Framing |
| Ökosystem | GraphQL-Clients, Codegen, Werkzeuge | Nackte Socket-Bibliotheken |
| Am besten, wenn | Ereignisse typisierte API-Daten in einer GraphQL-App sind | Binär, hochfrequent oder eigene Semantik |

## Wenn rohe Sockets typisierte Subscriptions schlagen

Die ehrlichen Fälle sind real, nur enger, als die Begeisterung für rohe Sockets vermuten lässt. **Binäre Payloads** – Audio-Chunks, Protocol Buffers, Spielzustand – reisen nativ in WebSocket-Frames, müssten in einer JSON-Subscription-Hülle aber kodiert werden. **Nachrichtenrate** – bei Tausenden Ereignissen pro Sekunde und Client hören Resolver-Ausführung und JSON-Hülle pro Ereignis auf, Rauschen zu sein; ein kompaktes eigenes Frame-Format ist dann eine legitime Optimierung. **Eigene Semantik** – Backpressure, clientseitige Bestätigungen, Delta-Kodierung, wiederaufnehmbare Cursor – gehört zu Protokollen, die Sie selbst entwerfen, und sie auf Subscription-Frames aufzupfropfen heißt, gegen die Spezifikation zu arbeiten. Und **gar kein GraphQL zu Beginn** – Schema, Resolver und Client-Werkzeuge *nur* für typisierte Ereignisse einzuführen, ist der Schwanz, der mit dem Hund wedelt; ein roher Socket mit dokumentiertem Nachrichtenformat ist kleiner. Die Falle wirkt auch in die andere Richtung: Teams, die für typisierte, JSON-förmige API-Ereignisse rohe Sockets wählen, schreiben am Ende Multiplexing, Auth-Handshakes und Wiederverbindungssemantik von Hand – ein schlechteres graphql-ws, ein inkompatibles Team nach dem anderen.

## Typische Anwendungsfälle

- **Bestell- und Statusströme** – "Sag mir Bescheid, wenn sich diese Bestellung ändert": typisierte API-Daten, niedrige Rate, die Paradedisziplin von Subscriptions.
- **Kollaborative Präsenz und Kommentare** – Subscriptions in GraphQL-Apps, die das Schema ohnehin besitzen; die Ereignisse sind einfach mehr Schema.
- **Finanz-Ticker und Dashboards** – Subscriptions, solange die Payloads JSON-förmig bleiben; rohe Sockets, wenn die Tickrate kompakte Frames verlangt.
- **Chat** – beide Schichten funktionieren; den Ausschlag gibt meist, ob die App GraphQL-first ist, denn [Live Queries](/glossary/de/live-queries-echtzeit/) decken dasselbe Feld ohne Ereignisverdrahtung ab.
- **Multiplayer-Zustand und Medien** – binär, hochfrequent, latenzkritisch: Territorium roher WebSockets, samt Protokoll.

## Sollten Sie GraphQL-Subscriptions oder rohe WebSockets verwenden? Entscheidungsmatrix

| Ihre Situation | Greifen Sie zu |
| --- | --- |
| Die App spricht bereits [GraphQL](/glossary/de/graphql/); Ereignisse sind typisierte API-Daten | GraphQL-Subscriptions |
| Binäre Frames oder Tausende Ereignisse/s pro Client | Rohe WebSockets |
| Sie brauchen eigene Semantik – Bestätigungen, Deltas, Cursor, Backpressure | Rohe WebSockets, Protokoll dokumentiert |
| Keine GraphQL-Investition, einfacher Ereignis-Feed | Roher Socket mit kleinem Frame-Protokoll – oder SSE |
| Ereignisse bedeuten "die Ergebnisse dieser Query haben sich geändert" | Eine Live-Query-Schicht – gar keine Ereignisverdrahtung |
| Strenge Proxys, reine HTTP-Infrastruktur | Subscriptions über SSE |
| Kleines Team, keine Lust, ein Protokoll zu besitzen | Typisierte Subscriptions auf einem verwalteten Backend |

## Grenzen und Trade-offs

- **Subscriptions erben den Betrieb von WebSockets.** Schichtung fügt Bedeutung hinzu, keine Magie: Verbindungszustand, Routing mit Affinität, Heartbeats und ein Pub/Sub-Backplane über Server hinweg bleiben die Betriebsrealität darunter.
- **Wiederverbindungen verlieren weiterhin Ereignisse.** graphql-ws definiert Streams, keine Wiederaufnahme: Ein abgerissener Socket bedeutet verpasste Frames, und das Nachholen (Query erneut ausführen, dann neu abonnieren) ist auf beiden Stacks Anwendungslogik.
- **Auflösung pro Abonnent kostet.** Gefilterte Subscriptions können Resolver- und Berechtigungsarbeit pro Ereignis und Abonnent ausführen – ein stark genutztes Topic mit Tausenden Zuhörern vervielfacht das; entwerfen Sie Filter serverseitig und eng.
- **Zwei Subprotokolle sind im Umlauf.** Das alte und das moderne Protokoll für GraphQL über WebSocket verstehen einander nicht; nicht zusammenpassende Enden scheitern stumm. Legen Sie die Versionen explizit fest.
- **Typisierte Hüllen belasten den Durchsatz.** JSON-Serialisierung und Schemavalidierung pro Ereignis sind bei einigen Dutzend Ereignissen pro Sekunde unsichtbar und bei Tausenden dominant – messen Sie, bevor Sie in die eine oder andere Richtung vermuten.

## GraphQL-Subscriptions und WebSockets mit Back4app

Back4app ist eine Open-Source-Plattform für Backend as a Service (BaaS), die eine verwaltete Datenbank, automatisch generierte REST- und GraphQL-APIs, Authentifizierung, Dateispeicher und Serverless-Funktionen mit Cloud Code kombiniert. Die Schichtung aus diesem Artikel bildet sich direkt auf der Plattform ab: Die automatisch generierte GraphQL-API deckt Query- und Mutationsschicht aus Ihrem Schema ohne eine Zeile Resolver-Code ab, während die Echtzeit-Schicht als [Live Queries](/glossary/de/live-queries-echtzeit/) ausgeliefert wird – typisierte, ACL-geprüfte Abonnement-Ereignisse über eine verwaltete WebSocket-Flotte, die das offene LiveQuery-Protokoll anstelle von graphql-ws nutzen und bei Änderungen der Ergebnismenge auslösen statt bei handverdrahteten Ereignissen. Sie bekommen die Spalte "typisiertes Protokoll" aus der Vergleichstabelle – Multiplexing, Authentifizierung und Wiederverbindung inklusive –, ohne die Socket-Flotte zu betreiben oder eine Protokollspezifikation zu besitzen.
