GraphQL Subscriptions vs. WebSockets: ¿qué se está comparando en realidad?

Actualizado: septiembre de 2026

Una GraphQL subscription es un stream de eventos tipado y definido por el schema; un WebSocket es el transporte crudo sobre el que suele viajar. El “vs.” del título es una confusión de capas que vale la pena deshacer antes de tomar cualquier decisión: las subscriptions no son una alternativa a los WebSockets — son una de las cosas que puedes correr sobre uno, igual que HTTP corre sobre TCP. La elección real es entre un protocolo tipado que alguien ya especificó y una tubería cruda cuyo protocolo inventas tú.

Puntos clave

PreguntaRespuesta
WebSocketUna tubería de bytes persistente y full-duplex — sin semántica de mensajes incluida
GraphQL subscriptionUn stream de eventos definido por el schema, tipado y validado como cualquier respuesta GraphQL
Su relaciónCapas, no rivales — las subscriptions viajan sobre WebSockets (o SSE) vía un sub-protocolo
El sub-protocolographql-ws: handshake init/ack, ids que multiplexan operaciones, frames next/complete
La decisión realProtocolo tipado de estante vs. socket crudo más un protocolo que ahora es tuyo

Las capas, en código

Lo que una subscription realmente es en el cable — una conversación graphql-ws dentro de un WebSocket:

// cliente → servidor, después de que el socket abre
{ "type": "connection_init", "payload": { "authToken": "…" } }
// servidor → cliente
{ "type": "connection_ack" }
// el cliente inicia una operación — el id multiplexa esta subscription
{ "type": "subscribe", "id": "1", "payload": {
    "query": "subscription { orderUpdated(status: PREPARING) { id status eta } }" } }
// el servidor transmite eventos tipados, un frame por ocurrencia
{ "type": "next", "id": "1", "payload": { "data": { "orderUpdated": { "id": "o42", "status": "READY", "eta": null } } } }
// cualquiera de los dos lados cierra el stream
{ "type": "complete", "id": "1" }

Y lo que la mayor parte del código de aplicación escribe en realidad — una subscription tipada con toda la pila gestionada:

// 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

Una pila, tres capas

GraphQL subscriptions en capas sobre un transporte WebSocketEl código de aplicación consume eventos tipados. Debajo, un protocolo de subscription como graphql-ws maneja el handshake, la multiplexación y el ciclo de vida del stream. Debajo de eso, la capa de transporte suele ser un WebSocket y a veces Server-Sent Events. El transporte mueve bytes; el protocolo les da significado; el schema les da tipos.

Código de aplicación
eventos tipados, payloads con la forma del schema

Protocolo de subscription — graphql-ws
connection_init/ack · subscribe · next · complete

Transporte — normalmente WebSocket, a veces SSE
conexión persistente, entrega ordenada

TCP/IP

El código de aplicación consume eventos tipados. Debajo, un protocolo de subscription como graphql-ws maneja el handshake, la multiplexación y el ciclo de vida del stream. Debajo de eso, la capa de transporte suele ser un WebSocket y a veces Server-Sent Events. El transporte mueve bytes; el protocolo les da significado; el schema les da tipos.

La capa WebSocket (RFC 6455) promete exactamente esto: un stream persistente, full-duplex y ordenado de frames de texto o binarios, alcanzable desde cualquier navegador mediante una API pequeña. No dice nada sobre lo que un mensaje significa — sin correlación solicitud/respuesta, sin convención de autenticación, sin señalización de errores, sin manera de correr dos streams lógicos por un socket. Cada proyecto de socket crudo vuelve a decidir todo eso.

La capa de subscription es precisamente ese conjunto de decisiones faltantes, estandarizado: connection_init/connection_ack lleva la autenticación; los id por operación multiplexan muchas subscriptions en un socket; los frames next entregan payloads que son respuestas GraphQL ordinarias — tipadas por el schema, validadas, introspectables, consumidas por la misma maquinaria de cliente que las queries y las mutations; complete cierra un stream sin matar a sus vecinos. Una nota histórica importa en la práctica: un sub-protocolo más antiguo de los inicios del ecosistema sigue desplegado, y un cliente que habla uno con un servidor que habla el otro falla de formas confusamente silenciosas — fija el sub-protocolo explícitamente en ambos extremos.

Y como el contrato es de mensajes, no de sockets, el transporte de abajo es intercambiable — la misma semántica de subscription corre cada vez más sobre Server-Sent Events, que encaja con la forma abrumadoramente unidireccional del tráfico de subscriptions y hereda la tolerancia a proxies y la reconexión automática de SSE. “Subscriptions vs. WebSockets” se disuelve al tocarlo: una es contrato, el otro es portador.

GraphQL subscriptions vs. WebSockets crudos

GraphQL subscriptionsWebSockets crudos
CapaProtocolo + sistema de tipos sobre un transporteEl transporte en sí
Contrato de mensajesDefinido por el schema, validado, introspectableLo que inventes y documentes
MultiplexaciónIncluida — ids por operación en un socketTuya de diseñar
Handshake de autenticaciónEstandarizado (payload de connection_init)Tuyo de diseñar
PayloadsJSON, con la forma del schemaTexto y binario, cualquier formato
FiltradoArgumentos en el campo de la subscriptionCódigo de servidor que escribes tú
Overhead por eventoEnvelope JSON + ejecución de resolver~2–14 bytes de framing
EcosistemaClientes GraphQL, codegen, toolingBibliotecas de socket pelado
Mejor cuandoLos eventos son datos de API tipados en una app GraphQLBinario, alta frecuencia o semántica propia

Cuándo los sockets crudos les ganan a las subscriptions tipadas

Los casos honestos existen, solo que son más estrechos de lo que sugiere el entusiasmo por el socket crudo. Payloads binarios — trozos de audio, protocol buffers, estado de juego — viajan de forma nativa en frames WebSocket, pero necesitarían codificarse dentro de un envelope JSON de subscription. Tasa de mensajes — a miles de eventos por segundo por cliente, la ejecución de resolvers y el envelope JSON por evento dejan de ser ruido; un formato de frame compacto y propio es una optimización legítima. Semántica propia — backpressure, acks del cliente, codificación de deltas, cursores reanudables — pertenece a protocolos que diseñas tú, y atornillarla a los frames de subscription pelea con la spec. Y no tener GraphQL para empezar — adoptar schema, resolvers y tooling de cliente solo para tener eventos tipados es la cola moviendo al perro; un socket crudo con un formato de mensajes documentado es más pequeño. La trampa corre también en sentido contrario: los equipos que eligen sockets crudos para eventos de API tipados con forma JSON terminan escribiendo a mano multiplexación, handshakes de autenticación y semántica de reconexión — un graphql-ws peor, un equipo incompatible a la vez.

Casos de uso comunes

  • Streams de pedidos y estados — “avísame cuando este pedido cambie”: datos de API tipados, tasa baja, el punto dulce de las subscriptions.
  • Presencia y comentarios colaborativos — subscriptions en apps GraphQL que ya son dueñas del schema; los eventos son solo más schema.
  • Cotizaciones financieras y dashboards — subscriptions mientras los payloads sigan con forma JSON; sockets crudos cuando la tasa de ticks exige frames compactos.
  • Chat — cualquiera de las dos capas funciona; el voto decisivo suele ser si la app es GraphQL-first, ya que las live queries cubren el mismo terreno sin cableado de eventos.
  • Estado multijugador y media — binario, alta frecuencia, crítico en latencia: territorio de WebSocket crudo, con protocolo y todo.

¿Deberías usar GraphQL subscriptions o WebSockets crudos? Matriz de decisión

Tu situaciónUsa
La app ya habla GraphQL; los eventos son datos de API tipadosGraphQL subscriptions
Frames binarios, o miles de eventos/s por clienteWebSockets crudos
Necesitas semántica propia — acks, deltas, cursores, backpressureWebSockets crudos, con el protocolo documentado
Sin inversión en GraphQL, feed de eventos simpleSocket crudo con un pequeño protocolo de frames — o SSE
Los eventos son “los resultados de esta consulta cambiaron”Una capa de live query — sin cableado de eventos alguno
Proxies estrictos, infraestructura solo HTTPSubscriptions sobre SSE
Equipo pequeño, sin apetito por ser dueño de un protocoloSubscriptions tipadas en un backend gestionado

Limitaciones y trade-offs

  • Las subscriptions heredan la operación de los WebSockets. Apilar capas agrega significado, no magia: el estado de conexión, el enrutamiento con afinidad, los heartbeats y un backplane de pub/sub entre servidores siguen siendo la realidad de despliegue por debajo.
  • La reconexión sigue perdiendo eventos. graphql-ws define streams, no reanudación: un socket caído significa frames perdidos, y ponerse al día (reconsultar y luego resuscribirse) es lógica de aplicación en cualquiera de las dos pilas.
  • La resolución por suscriptor cuesta. Las subscriptions filtradas pueden ejecutar trabajo de resolvers y de permisos por evento y por suscriptor — un topic caliente con miles de oyentes lo multiplica; diseña los filtros del lado del servidor y estrechos.
  • Circulan dos sub-protocolos. Los protocolos legado y moderno de GraphQL sobre WebSocket son mutuamente ininteligibles; los extremos desparejados fallan en silencio. Fija las versiones explícitamente.
  • Los envelopes tipados cobran throughput. La serialización JSON y la validación de schema por evento son invisibles a decenas de eventos por segundo y dominantes a miles — mide antes de asumir en cualquier dirección.

GraphQL subscriptions y WebSockets en Back4app

Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. El apilamiento de este artículo se mapea directo sobre la plataforma: la API GraphQL generada automáticamente cubre las capas de query y mutation a partir de tu schema sin una línea de resolvers, mientras que la capa de tiempo real llega como Live Queries — eventos de subscription tipados y verificados por ACL sobre una flota gestionada de WebSockets, usando el protocolo abierto LiveQuery en lugar de graphql-ws y disparándose por cambios en el conjunto de resultados en vez de eventos cableados a mano. Te quedas con la columna del protocolo tipado de la tabla comparativa — multiplexación, autenticación y manejo de reconexión incluidos — sin operar la flota de sockets ni ser dueño de una especificación de protocolo.

Preguntas frecuentes

¿Las GraphQL subscriptions son lo mismo que los WebSockets?

No — viven en capas diferentes. Un WebSocket es transporte: una tubería de bytes persistente y full-duplex, sin opinión sobre lo que pasa por ella. Una GraphQL subscription es protocolo y contrato por encima: una operación definida en el schema cuyos eventos llegan tipados, validados y con la misma forma que cualquier otra respuesta GraphQL. Compararlas directamente es comparar una carretera con una línea de autobús.

¿Qué protocolo usan las GraphQL subscriptions?

Lo más común es graphql-ws, el sub-protocolo moderno de GraphQL sobre WebSocket: el cliente abre el socket, envía connection_init, recibe connection_ack y luego inicia operaciones con mensajes subscribe; el servidor transmite payloads next por id de subscription y cualquiera de los dos lados cierra con complete. Un sub-protocolo más antiguo de los inicios del ecosistema todavía circula, y por eso cliente y servidor deben acordar cuál de los dos hablan.

¿Pueden las GraphQL subscriptions correr sobre Server-Sent Events?

Sí — el contrato de subscription es agnóstico del transporte, y SSE es un portador legítimo cada vez más popular. Como el tráfico de subscriptions es abrumadoramente del servidor al cliente, un stream HTTP unidireccional encaja de forma natural, mantiene contentos a los proxies comunes y a la semántica HTTP, y trae reconexión automática gratis. Los WebSockets siguen siendo el default en la mayoría del tooling, pero "las subscriptions exigen WebSockets" es folclore, no un hecho.

¿Cuándo deberías usar WebSockets crudos en lugar de GraphQL subscriptions?

Cuando el tráfico deja de parecer eventos de API tipados: frames binarios (audio, estado de juego, streams de sensores), tasas de mensajes muy altas donde validar el schema y envolver en JSON cada evento cuesta throughput real, o protocolos que necesitan semántica propia — cursores, deltas, acknowledgments — que pelean con la forma de la subscription. Si aún no invertiste en un schema GraphQL, el socket crudo también evita importar uno solo por los eventos.

¿Las GraphQL subscriptions escalan?

El transporte escala como cualquier flota de WebSockets — estado de conexión, enrutamiento con afinidad y un backplane de pub/sub entre servidores. La capa de subscription agrega su propio eje: cada evento puede resolverse y filtrarse por suscriptor, así que un topic caliente con muchos suscriptores multiplica el trabajo de los resolvers. Las plataformas gestionadas absorben la flota; el diseño del schema y la disciplina de filtrado por suscriptor siguen siendo tuyos.

¿Las GraphQL subscriptions son lo mismo que las live queries?

Primas cercanas con disparadores distintos. Las subscriptions se disparan con eventos nombrados que cableas explícitamente — una mutation publica a un topic, los suscriptores reciben. Las live queries se disparan cuando cambia el conjunto de resultados de una consulta — sin cableado de eventos, con cada ruta de escritura cubierta automáticamente. Ambas suelen viajar sobre WebSockets. Una capa de live query cambia el diseño de eventos schema-first por detección automática de cambios sobre los datos mismos.

¿Por qué las subscriptions necesitan un sub-protocolo?

Porque un WebSocket pelado es solo bytes ordenados. En cuanto dos partes necesitan multiplexar varias subscriptions en un socket, correlacionar eventos con operaciones, negociar autenticación, señalizar errores y cerrar streams con limpieza, necesitan framing de mensajes y reglas — que cada equipo alguna vez inventó mal, una versión incompatible a la vez. graphql-ws estandariza exactamente esa capa para que clientes y servidores interoperen.

Términos relacionados

Compara con

Lecturas recomendadas

¿Listo para construir tu backend?

Empieza tu proyecto en Back4app en minutos — base de datos, autenticación, APIs y Cloud Code incluidos. Sin tarjeta de crédito.

Escrito y revisado por Back4app Engineering, Back4app Engineering · Publicado el 2026-09-04