---
term: 'GraphQL Subscriptions vs. WebSockets'
seoTitle: 'GraphQL Subscriptions vs. WebSockets: Protocolo vs. Transporte'
headline: 'GraphQL Subscriptions vs. WebSockets: o que está sendo comparado, afinal?'
slug: graphql-subscriptions-vs-websockets
category: api-realtime
shortDefinition: 'Uma GraphQL subscription é um stream de eventos tipado e definido pelo schema; um WebSocket é o transporte cru sobre o qual ela costuma rodar.'
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: 'GraphQL subscriptions e WebSockets são a mesma coisa?'
    answer: 'Não — elas vivem em camadas diferentes. Um WebSocket é transporte: um cano de bytes persistente e full-duplex, sem opinião sobre o que trafega por ele. Uma GraphQL subscription é protocolo e contrato por cima: uma operação definida no schema cujos eventos chegam tipados, validados e no mesmo formato de qualquer outra resposta GraphQL. Comparar as duas diretamente é comparar uma estrada com uma linha de ônibus.'
  - question: 'Qual protocolo as GraphQL subscriptions usam?'
    answer: 'Normalmente o graphql-ws, o sub-protocolo moderno de GraphQL sobre WebSocket: o cliente abre o socket, envia connection_init, recebe connection_ack e então inicia operações com mensagens subscribe; o servidor transmite payloads next por id de subscription e qualquer um dos lados encerra com complete. Um sub-protocolo mais antigo do início do ecossistema ainda circula, e é por isso que cliente e servidor precisam concordar sobre qual dos dois falam.'
  - question: 'GraphQL subscriptions podem rodar sobre Server-Sent Events?'
    answer: 'Podem — o contrato de subscription é agnóstico de transporte, e SSE é um carregador legítimo, cada vez mais popular. Como o tráfego de subscription é esmagadoramente do servidor para o cliente, um stream HTTP unidirecional encaixa naturalmente, mantém proxies comuns e a semântica HTTP satisfeitos e traz reconexão automática de graça. WebSockets seguem como padrão na maior parte do ferramental, mas "subscriptions exigem WebSockets" é folclore, não fato.'
  - question: 'Quando usar WebSockets crus em vez de GraphQL subscriptions?'
    answer: 'Quando o tráfego deixa de parecer evento de API tipado: frames binários (áudio, estado de jogo, streams de sensores), taxas de mensagem muito altas em que validar schema e envelopar em JSON por evento custam throughput real, ou protocolos que precisam de semântica própria — cursores, deltas, acknowledgments — que brigam com o formato de subscription. Se você ainda não investiu em um schema GraphQL, o socket cru também evita importar um só por causa dos eventos.'
  - question: 'GraphQL subscriptions escalam?'
    answer: 'O transporte escala como qualquer frota de WebSockets — estado de conexão, roteamento com afinidade e um backplane de pub/sub entre servidores. A camada de subscription acrescenta um eixo próprio: cada evento pode ser resolvido e filtrado por assinante, então um tópico quente com muitos assinantes multiplica o trabalho dos resolvers. Plataformas gerenciadas absorvem a frota; o design do schema e a disciplina de filtro por assinante continuam sendo seus.'
  - question: 'GraphQL subscriptions são a mesma coisa que live queries?'
    answer: 'Primas próximas, com gatilhos diferentes. Subscriptions disparam em eventos nomeados que você conecta explicitamente — uma mutation publica, os assinantes recebem. Live queries disparam quando o conjunto de resultados de uma consulta muda — sem fiação de eventos, todo caminho de escrita coberto automaticamente. As duas costumam rodar sobre WebSockets. Uma camada de live query troca o design de eventos guiado por schema pela detecção automática de mudanças nos próprios dados.'
  - question: 'Por que subscriptions precisam de um sub-protocolo?'
    answer: 'Porque um WebSocket puro é só bytes ordenados. No momento em que duas partes precisam multiplexar várias subscriptions em um socket, correlacionar eventos com operações, negociar autenticação, sinalizar erros e encerrar streams com limpeza, elas precisam de framing de mensagens e regras — que todo time já inventou mal, uma versão incompatível por vez. O graphql-ws padroniza exatamente essa camada para que clientes e servidores interoperem.'
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: 'Tempo real sem ser dono de nenhuma das camadas'
  text: 'O Back4app combina uma API GraphQL gerada automaticamente com Live Queries — subscriptions tipadas e com permissões verificadas sobre uma frota gerenciada de WebSockets. Defina o schema, inscreva-se na consulta e pule a engenharia de protocolo por completo.'
  linkText: 'Comece grátis'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-08-25'
translationKey: graphql-subscriptions-vs-websockets
---

**Uma GraphQL subscription é um stream de eventos tipado e definido pelo schema; um WebSocket é o transporte cru sobre o qual ela costuma rodar.** O "vs." do título é uma confusão de camadas que vale desfazer antes de qualquer decisão: subscriptions não são uma *alternativa* a WebSockets — são uma das coisas que você pode rodar *sobre* um, do jeito que o HTTP roda sobre TCP. A escolha real é entre um protocolo tipado que alguém já especificou e um cano cru cujo protocolo você inventa.

## Principais pontos

| Pergunta | Resposta |
| --- | --- |
| WebSocket | Um cano de bytes persistente e full-duplex — sem semântica de mensagem inclusa |
| GraphQL subscription | Um stream de eventos definido pelo schema, tipado e validado como qualquer resposta GraphQL |
| A relação entre eles | Camadas, não rivais — subscriptions rodam sobre WebSockets (ou SSE) via um sub-protocolo |
| O sub-protocolo | graphql-ws: handshake init/ack, ids multiplexam operações, frames next/complete |
| A decisão de verdade | Protocolo tipado de prateleira vs. socket cru mais um protocolo que agora é seu |

## As camadas, em código

O que uma subscription realmente é no fio — uma conversa [graphql-ws](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) dentro de um WebSocket:

```json
// cliente → servidor, depois que o socket abre
{ "type": "connection_init", "payload": { "authToken": "…" } }
// servidor → cliente
{ "type": "connection_ack" }
// o cliente inicia uma operação — o id multiplexa esta subscription
{ "type": "subscribe", "id": "1", "payload": {
    "query": "subscription { orderUpdated(status: PREPARING) { id status eta } }" } }
// o servidor transmite eventos tipados, um frame por ocorrência
{ "type": "next", "id": "1", "payload": { "data": { "orderUpdated": { "id": "o42", "status": "READY", "eta": null } } } }
// qualquer um dos lados encerra o stream
{ "type": "complete", "id": "1" }
```

E o que a maior parte do código de aplicação de fato escreve — uma subscription tipada com a pilha inteira gerenciada:

**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) }
```

## Uma pilha, três camadas

```mermaid
flowchart TB
  accTitle: GraphQL subscriptions em camadas sobre um transporte WebSocket
  accDescr: O código de aplicação consome eventos tipados. Abaixo dele, um protocolo de subscription como o graphql-ws cuida do handshake, da multiplexação e do ciclo de vida do stream. Abaixo disso, a camada de transporte é normalmente um WebSocket e às vezes Server-Sent Events. O transporte move bytes; o protocolo dá significado a eles; o schema dá tipos.
  A["Código de aplicação<br/>eventos tipados, payloads no formato do schema"] --> P["Protocolo de subscription — graphql-ws<br/>connection_init/ack · subscribe · next · complete"]
  P --> T["Transporte — normalmente WebSocket, às vezes SSE<br/>conexão persistente, entrega ordenada"]
  T --> N["TCP/IP"]
```

A camada [WebSocket](/glossary/pt/websockets/) ([RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455)) promete exatamente isto: um stream persistente, full-duplex e ordenado de frames de texto ou binários, acessível de qualquer navegador por [uma API pequena](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API). Ela não diz nada sobre o que uma mensagem *significa* — sem correlação requisição/resposta, sem convenção de autenticação, sem sinalização de erro, sem jeito de rodar dois streams lógicos em um socket. Todo projeto de socket cru redecide tudo isso.

A [camada de subscription](https://graphql.org/learn/subscriptions/) é precisamente esse conjunto de decisões faltantes, padronizado: `connection_init`/`connection_ack` carrega a autenticação; `id`s por operação multiplexam muitas subscriptions em um socket; frames `next` entregam payloads que são respostas GraphQL comuns — tipadas pelo schema, validadas, introspectáveis, consumidas pela mesma maquinaria de cliente que queries e mutations; `complete` encerra um stream sem matar os vizinhos. Uma nota histórica importa na prática: um sub-protocolo mais antigo do início do ecossistema ainda está em produção, e um cliente falando um com um servidor falando o outro falha de formas silenciosas e confusas — fixe o sub-protocolo explicitamente nas duas pontas.

E porque o contrato é de mensagens, não de sockets, o transporte por baixo é trocável — a mesma semântica de subscription roda cada vez mais sobre Server-Sent Events, o que combina com o formato esmagadoramente unidirecional do tráfego de subscription e herda a [tolerância a proxies e a reconexão automática do SSE](/glossary/pt/sse-vs-websockets-vs-polling/). "Subscriptions vs. WebSockets" se dissolve ao toque: uma é contrato, o outro é carregador.

## GraphQL subscriptions vs. WebSockets crus

| | GraphQL subscriptions | WebSockets crus |
| --- | --- | --- |
| Camada | Protocolo + sistema de tipos sobre um transporte | O transporte em si |
| Contrato de mensagem | Definido pelo schema, validado, introspectável | O que você inventar e documentar |
| Multiplexação | Embutida — ids por operação em um socket | Por sua conta |
| Handshake de autenticação | Padronizado (payload do `connection_init`) | Por sua conta |
| Payloads | JSON, no formato do schema | Texto **e binário**, qualquer formato |
| Filtragem | Argumentos no campo da subscription | Código de servidor que você escreve |
| Overhead por evento | Envelope JSON + execução de resolver | ~2–14 bytes de framing |
| Ecossistema | Clientes GraphQL, codegen, ferramental | Bibliotecas de socket cru |
| Melhor quando | Os eventos são dados de API tipados em um app GraphQL | Binário, alta frequência ou semântica própria |

## Quando sockets crus ganham das subscriptions tipadas

Os casos honestos existem, só que são mais estreitos do que o entusiasmo por socket cru sugere. **Payloads binários** — pedaços de áudio, protocol buffers, estado de jogo — trafegam nativamente em frames WebSocket, mas precisariam ser codificados dentro de um envelope JSON de subscription. **Taxa de mensagens** — a milhares de eventos por segundo por cliente, a execução de resolver e o envelope JSON por evento deixam de ser ruído; um formato de frame compacto e próprio é otimização legítima. **Semântica própria** — backpressure, acks do cliente, codificação de deltas, cursores retomáveis — pertence a protocolos que você projeta, e enfiá-los nos frames de subscription briga com a spec. E **não ter GraphQL para começar** — adotar schema, resolvers e ferramental de cliente *só* para ter eventos tipados é o rabo abanando o cachorro; um socket cru com formato de mensagem documentado é menor. A armadilha corre no sentido inverso também: times que escolhem sockets crus para eventos de API tipados e no formato JSON acabam escrevendo à mão multiplexação, handshake de autenticação e semântica de reconexão — um graphql-ws pior, um time incompatível por vez.

## Casos de uso comuns

- **Streams de pedido e status** — "me avise quando este pedido mudar": dados de API tipados, taxa baixa, o ponto ideal das subscriptions.
- **Presença e comentários colaborativos** — subscriptions em apps GraphQL que já são donos do schema; os eventos são só mais schema.
- **Cotações financeiras e dashboards** — subscriptions enquanto os payloads seguem no formato JSON; sockets crus quando a taxa de ticks exige frames compactos.
- **Chat** — qualquer uma das camadas funciona; o voto de minerva costuma ser se o app é GraphQL-first, já que as [live queries](/glossary/pt/live-queries-tempo-real/) cobrem o mesmo terreno sem fiação de eventos.
- **Estado multiplayer e mídia** — binário, alta frequência, sensível a latência: território de WebSocket cru, protocolo e tudo.

## Você deveria usar GraphQL subscriptions ou WebSockets crus? Matriz de decisão

| Sua situação | Use |
| --- | --- |
| O app já fala [GraphQL](/glossary/pt/graphql/); os eventos são dados de API tipados | GraphQL subscriptions |
| Frames binários, ou milhares de eventos/s por cliente | WebSockets crus |
| Você precisa de semântica própria — acks, deltas, cursores, backpressure | WebSockets crus, com o protocolo documentado |
| Nenhum investimento em GraphQL, feed de eventos simples | Socket cru com um pequeno protocolo de frames — ou SSE |
| Os eventos são "os resultados desta consulta mudaram" | Uma camada de live query — sem fiação de eventos nenhuma |
| Proxies rígidos, infraestrutura só HTTP | Subscriptions sobre SSE |
| Time pequeno, sem apetite para ser dono de protocolo | Subscriptions tipadas em um backend gerenciado |

## Limitações e trade-offs

- **Subscriptions herdam a operação de WebSockets.** Empilhar camadas acrescenta significado, não mágica: estado de conexão, roteamento com afinidade, heartbeats e um backplane de pub/sub entre servidores continuam sendo a realidade de deploy por baixo.
- **Reconexão ainda perde eventos.** O graphql-ws define streams, não retomada: um socket que cai significa frames perdidos, e a atualização (reconsultar e depois reinscrever) é lógica de aplicação nas duas pilhas.
- **Resolução por assinante custa.** Subscriptions filtradas podem executar trabalho de resolver e de permissão por evento e por assinante — um tópico quente com milhares de ouvintes multiplica isso; projete filtros no servidor e estreitos.
- **Circulam dois sub-protocolos.** Os protocolos legado e moderno de GraphQL sobre WebSocket são mutuamente ininteligíveis; pontas desencontradas falham em silêncio. Fixe as versões explicitamente.
- **Envelopes tipados cobram throughput.** Serialização JSON e validação de schema por evento são invisíveis a dezenas de eventos por segundo e dominantes a milhares — meça antes de assumir qualquer coisa.

## GraphQL subscriptions e WebSockets no Back4app

O Back4app é uma plataforma open-source de Backend as a Service (BaaS) que combina banco de dados gerenciado, APIs REST e GraphQL geradas automaticamente, autenticação, armazenamento de arquivos e funções serverless com Cloud Code. O empilhamento descrito neste artigo mapeia direto na plataforma: a API GraphQL gerada automaticamente cobre as camadas de query e mutation a partir do seu schema, sem nenhum código de resolver, enquanto a camada de tempo real chega como [Live Queries](/glossary/pt/live-queries-tempo-real/) — eventos de subscription tipados e verificados por ACL sobre uma frota gerenciada de WebSockets, usando o protocolo aberto LiveQuery no lugar do graphql-ws e disparando por mudança no conjunto de resultados em vez de eventos conectados à mão. Você fica com a coluna do protocolo tipado da tabela comparativa — multiplexação, autenticação e tratamento de reconexão inclusos — sem operar a frota de sockets nem ser dono de uma especificação de protocolo.
