Módulo 3 · Diseño de APIs

Lección 16 — gRPC, GraphQL, WebSockets y SSE

Cuándo REST no basta y qué pagas con cada alternativa.

Publicada
En esta lección
  1. Objetivos
  2. 1. El mapa de protocolos
  3. 2. El caso TicketFlow: disponibilidad en vivo
  4. 3. SSE con Django ASGI (el código que abre la puerta)
  5. 4. Cuando NO conviene nada de esto
  6. Autoevaluación

Stack: DRF + ASGI · Proyecto: TicketFlow Estado: Publicada Prerrequisito: Lección 15 — Validación y OpenAPI


Objetivos

  1. Comparar REST/gRPC/GraphQL con el criterio de quién consume qué (y el coste de cada uno).
  2. Elegir entre WebSockets y SSE para la disponibilidad en vivo de TicketFlow.
  3. Implementar un endpoint SSE con Django ASGI y entender su implicación de concurrencia (la 03 reaparece).

1. El mapa de protocolos

REST/JSONgRPCGraphQLWS/SSE
ModelorecursosRPC tipadografo consultablepush del servidor
ContratoOpenAPI.proto (binario)schema SDLevento
Cliente típicotodo el mundomicroservicio↔microfrontend ricotiempo real
Caché HTTPsí (12)no (HTTP/2 pero bodies binarios)parcial (POST)no
Curvabajamedia (tooling)media-alta (N+1, perms)media

Criterio de elección: REST para la API pública (universo de clientes, caché, depuración con curl); gRPC interno (servicios propios: contratos binarios estrictos, streaming, rendimiento — cuando haya microservicios de verdad, Lección 53); GraphQL solo si el frontend necesita combinar muchos recursos con formas variables y el equipo paga su complejidad (N+1 en resolvers, permisos por campo); WS/SSE para empujar eventos.

La trampa de GraphQL: "un solo endpoint" suena a libertad y es mover el N+1 al servidor (dataloader obligatorio) y el control de acceso a cada campo (21). No es malo: es un coste continuo, no un sprint.

2. El caso TicketFlow: disponibilidad en vivo

El comprador mira el mapa de asientos; los asientos se venden bajo sus pies. Opciones:

  1. Polling: GET /availability cada 3s — simple, HTTP cacheable (12), pero 1000 usuarios mirando = 333 rps de consultas idénticas (con caché Redis aguantable).
  2. SSE (Server-Sent Events): el servidor empuja "asiento vendido" por un stream HTTP unidireccional — una conexión abierta por usuario, push en el momento. Reconnect nativo del navegador (EventSource), texto simple, proxy-friendly.
  3. WebSocket: canal bidireccional — si el usuario también emite (chat de soporte, subastas), sí; para "el servidor avisa", sobra.

Decisión de TicketFlow: SSE para disponibilidad (unidireccional, simple, HTTP/2 friendly) + polling con caché como fallback. WebSocket solo si aparece bidireccionalidad real (subastas).

3. SSE con Django ASGI (el código que abre la puerta)

python
# events/sse.py (consumidor en ASGI — Django con Uvicorn, la 03)
async def availability_stream(request, event_uuid):
    event = await get_event(event_uuid)              # sync_to_async
    channel = f"avail:{event.id}"
    pubsub = redis_client.pubsub()
    await pubsub.subscribe(channel)
    async for message in pubsub.listen():
        if message["type"] == "message":
            yield {"event": "availability", "data": message["data"]}

Costes que debes gestionar: cada stream es una conexión abierta (1000 usuarios = 1000 conexiones — Uvicorn/ASGI las aguanta porque es I/O, la 03); keepalive cada ~15s para atravesar proxies (Nginx: proxy_buffering off en la ruta SSE); eventos y reconnection con Last-Event-ID para no perder ventas entre reconexiones; y el pub/sub (Redis publish desde el servicio que confirma la reserva → todos los streams del evento reciben el push).

4. Cuando NO conviene nada de esto

Si la disponibilidad cambia 10 veces por hora y el mapa se refresca al entrar, el polling con caché es suficiente y cero infraestructura nueva. Cada tecnología de push añade: conexiones persistentes (memoria/limites de LB), reconexiones (duplicados), y un nuevo failure mode. El push se justifica cuando el negocio exige el instante (subasta, subastas de entradas re-sell, colaboración), no porque "esté bien tenerlo en tiempo real".


Autoevaluación

  1. ¿Por qué REST sigue siendo la elección para API pública aunque gRPC sea "más rápido"?
  2. ¿Qué añade GraphQL como coste continuo y qué caso lo justifica?
  3. SSE vs WebSocket para disponibilidad: ¿qué decide la elección? ¿Cuándo WS gana?
  4. ¿Por qué el SSE necesita proxy_buffering off en Nginx y keepalive? ¿Qué pasa sin ellos?
  5. 1000 streams SSE abiertos: ¿qué recurso agota primero y cómo lo dimensionas (conecta con la 03/05)?

Continúa con los ejercicios. Las solutions.md solo tras intentarlo.