Suscripciones GraphQL: cómo funcionan por la red | We.Inc
Qué son las suscripciones GraphQL, la regla de un solo campo raíz, un recorrido paso a paso por los mensajes de graphql-ws, la opción SSE y cuándo el polling es mejor opción.
Una suscripción GraphQL es una operación de larga duración: el cliente envía una vez un documento de suscripción y el servidor envía un resultado nuevo cada vez que ocurre el evento correspondiente. GraphQL no fija el transporte. En la práctica funciona sobre WebSockets, con el protocolo graphql-ws, o sobre Server-Sent Events. Cada operación de suscripción debe seleccionar exactamente un campo raíz.
Suscripciones, polling y volver a consultar
Enfoque
Cómo llegan las actualizaciones
Buen encaje
Suscripción
El servidor envía un resultado por evento
Cambios pequeños y frecuentes que los usuarios esperan ver al instante
Polling
El cliente repite una consulta con un temporizador
Datos que cambian de vez en cuando
Volver a consultar tras una acción
El cliente consulta de nuevo tras un paso del usuario
Pantallas que solo cambian cuando el usuario actúa
Notificación push
Un mensaje fuera de la app
Usuarios móviles con la app cerrada
El reproductor de abajo muestra una suscripción sobre el protocolo graphql-ws, mensaje a mensaje. Cada paso enseña el frame JSON que cruza el socket y qué lado lo envía, desde el primer handshake hasta el complete final.
Pulsa Siguiente para avanzar por la conversación frame a frame.
Lee quién envía cada frame y qué significa.
Fíjate en el id de la operación: todos los frames de una suscripción llevan el mismo id.
Usa Atrás o Reiniciar para volver a ver un paso.
Empieza con polling y pasa a una suscripción solo cuando unos segundos de retraso perjudiquen de verdad la experiencia.
Envía un evento pequeño en cada resultado, como el mensaje nuevo, en lugar de la lista completa, y deja que el cliente lo combine.
Prevé las reconexiones: tras caerse un socket, el cliente debe suscribirse de nuevo y volver a consultar lo que se haya perdido.
Comprueba la autenticación al abrir la conexión y otra vez dentro de cada resolver, ya que los permisos de un usuario pueden cambiar durante una sesión larga.
Preguntas frecuentes
¿Por qué solo un campo raíz por suscripción?
Cada suscripción corresponde a un flujo de eventos. graphql.org muestra que poner dos campos raíz en una sola operación de suscripción no es válido; escribe dos suscripciones con nombre en su lugar.
¿Qué subprotocolo WebSocket usa graphql-ws?
Negocia graphql-transport-ws durante el handshake, con tipos de frame como connection_init, subscribe, next y complete. Otras bibliotecas WebSocket de GraphQL pueden usar un protocolo distinto, así que comprueba que cliente y servidor coinciden antes de depurar cualquier otra cosa.
¿Pueden funcionar las suscripciones sin WebSockets?
Sí. El protocolo graphql-sse envía cada resultado como un Server-Sent Event llamado next y termina con complete. Funciona sobre HTTP normal, lo que ayuda detrás de proxies que bloquean los upgrades a WebSocket.
¿Las suscripciones escalan como las consultas?
No. graphql.org señala que cada cliente suscrito queda ligado a una instancia del servidor y necesita estado durante toda su vida, así que también necesitas un sistema pub/sub para repartir los eventos entre servidores.
Qué ocurre cuando te suscribes
El cliente envía un documento que empieza con la palabra clave subscription, elige un campo raíz y enumera los campos que quiere en cada evento. El servidor lo valida una vez y lo mantiene abierto.
Cuando algo publica un evento que coincide, como una nueva reseña, el servidor ejecuta el conjunto de selección contra ese evento y envía el resultado. Cada resultado tiene la forma normal de data y errors, así que el código del cliente lo lee igual que una consulta.
El protocolo graphql-ws en resumen
Tras el upgrade a WebSocket, el cliente envía connection_init, a menudo con un token de autenticación en su payload, y espera connection_ack. Solo entonces puede enviar frames subscribe. Cada subscribe lleva un id único, y todos los frames next, error o complete de esa operación lo repiten, así que muchas suscripciones comparten un mismo socket.
Cualquiera de los dos lados puede enviar ping y el otro responde pong. El servidor cierra el socket con el código 4408 si connection_init no llega a tiempo, y con 4409 cuando un cliente reutiliza un id que sigue en ejecución.
Cuándo el polling es mejor opción
Las suscripciones necesitan conexiones persistentes, una capa pub/sub y código de cliente cuidadoso para volver a suscribirse y evitar condiciones de carrera. Para un panel que se actualiza cada minuto, una consulta con temporizador es más sencilla y falla con más elegancia.
graphql.org recomienda suscripciones para datos que cambian a menudo y en pasos pequeños, y polling, notificaciones push o volver a consultar para datos más lentos o clientes móviles con poco ancho de banda.
Esquema y código del cliente
Declarar un campo de suscripción
Subscription es un tipo raíz como Query y Mutation. Cada campo es un flujo que un cliente puede abrir.
Suscribirse con el cliente de graphql-ws
createClient abre el socket de forma diferida, e iterate devuelve un iterador asíncrono que produce un resultado por evento.
La regla de un solo campo raíz
Separa dos flujos en dos operaciones
Una sola suscripción que incluya reviewCreated y otro campo raíz falla la validación. Dos operaciones con nombre sí funcionan.
El cliente envía connection_init
Se envía justo después de abrir el socket. El payload es libre; los servidores suelen leer un token de él.
El servidor envía connection_ack
El servidor aceptó la conexión. Antes de esto, un frame subscribe cerraría el socket con 4401 Unauthorized.
El cliente envía subscribe
El id lo elige el cliente y debe ser único entre sus operaciones en curso. El payload es la consulta y las variables de siempre.
El servidor envía next
Se publicó una reseña, así que el servidor ejecutó el conjunto de selección y envió el resultado con el mismo id.
El servidor vuelve a enviar next
Otro evento, otro frame. Esto se repite mientras la suscripción siga abierta.
El cliente envía complete
El usuario salió de la página, así que el cliente detiene la operación. El socket puede seguir abierto para otras suscripciones.