Saltar al contenido
aprendeaws.dev

Módulo 6 · Serverless/Lección 44

Tiempo real: WebSocket y publicación/suscripción

Mantener una conexión abierta con el navegador para chats, tableros y notificaciones al instante.

⏱ 10 min

Al terminar podrás

  • →Explicar la diferencia entre HTTP y WebSocket
  • →Diseñar una API WebSocket con API Gateway
  • →Elegir entre API Gateway WebSocket, AppSync e IoT Core para tiempo real

En HTTP, el navegador pregunta y el servidor responde. Si el servidor tiene algo nuevo, tiene que esperar a que le pregunten. Para un chat, un tablero de pedidos o una notificación de pago, esa espera se nota. Las aplicaciones en tiempo real necesitan que el servidor pueda hablar primero.

Cuatro formas de enterarse

CriterioPollingLong pollingServer-sent eventsWebSocket
Cómo funcionaEl cliente pregunta cada N segundosEl cliente pregunta y el servidor retiene la respuesta hasta tener algoUna respuesta HTTP que no termina; el servidor envía eventosUna conexión abierta en los dos sentidos
DirecciónCliente → servidorCliente → servidorServidor → clienteAmbas
CostoMuchas peticiones vacíasMenos peticiones, conexiones retenidasUna conexión por clienteUna conexión por cliente, mensajes pequeños
Encaja conDatos que cambian cada pocos minutosAvisos poco frecuentesFeeds, progreso de un trabajo, respuestas de IA en streamingChat, juegos, edición colaborativa

No siempre necesitas WebSocket. Si el dato cambia cada cinco minutos, el polling es más simple de operar. La idea de long polling también aparece en SQS, donde una lectura espera hasta 20 segundos por mensajes. Para server-sent events, Lambda puede enviar la respuesta por partes con response streaming desde una function URL.

API Gateway WebSocket: rutas y conexiones

Una WebSocket API de API Gateway mantiene las conexiones por ti. Tu código no queda corriendo mientras la llamada está abierta: API Gateway invoca tu backend solo cuando pasa algo.

  1. 1

    $connect

    Se ejecuta cuando el cliente abre la conexión. Es el único momento para autorizar: con IAM o un Lambda authorizer. Aquí guardas el connectionId.
  2. 2

    Rutas propias

    API Gateway lee un campo del mensaje, definido en la route selection expression (por ejemplo $request.body.action), y lo envía a la ruta que coincide: enviar, unirse.
  3. 3

    $default

    Recibe los mensajes que no coinciden con ninguna ruta. Sirve para responder un error claro o para manejar todo en una sola función.
  4. 4

    $disconnect

    Se ejecuta cuando la conexión se cierra, en la medida de lo posible. No garantiza entrega, así que no dependas solo de esta ruta para limpiar.

Cada conexión tiene un connectionId. Para hablarle a un cliente, tu backend hace un POST a la URL @connections/connectionId de la API Gateway Management API, firmado con IAM (permiso execute-api:ManageConnections). Con GET consultas el estado y con DELETE cierras la conexión. Las funciones Lambda no recuerdan nada entre invocaciones, así que los connectionId se guardan en DynamoDB, con un TTL para limpiar los que quedaron huérfanos.

Un chat paso a paso

  1. 1

    El cliente se conecta

    El navegador abre wss://…/prod?token=…. Un Lambda authorizer valida el token en $connect.
  2. 2

    Se registra la conexión

    La función de $connect guarda en DynamoDB el connectionId, el usuario y la sala, con un TTL de unas horas.
  3. 3

    Llega un mensaje

    El cliente envía {"action":"enviar","sala":"general","texto":"hola"}. API Gateway lo entrega a la función de la ruta enviar.
  4. 4

    Se reparte a la sala

    La función consulta las conexiones de la sala y llama a @connections para cada una. Si recibe 410 GoneException, borra ese registro.
  5. 5

    El cliente se desconecta

    La función de $disconnect borra el registro. Si no llega a correr, el TTL o el siguiente 410 lo limpian.
// Ruta "enviar": reenvía el mensaje a toda la sala (esquema)
const conexiones = await buscarConexionesDeLaSala(sala); // DynamoDB

await Promise.all(conexiones.map(async (c) => {
  try {
    await api.send(new PostToConnectionCommand({
      ConnectionId: c.connectionId,
      Data: JSON.stringify({ de: usuario, texto }),
    }));
  } catch (e) {
    // 410 GoneException: el cliente ya se fue
    if (e.name === "GoneException") await borrarConexion(c.connectionId);
  }
}));

Para salas de miles de personas, ese bucle se vuelve lento. Pasa el reparto a una cola o a SNS con varias funciones en paralelo, o usa AppSync Events, que hace el broadcast por ti.

Los límites que diseñan por ti

2 h

Duración máxima de una conexión WebSocket

10 min

Tiempo sin tráfico antes de cerrarla

128 KB

Tamaño máximo de un mensaje (en marcos de 32 KB)

500

Conexiones nuevas por segundo por cuenta y región, ampliable

Fuente: Amazon API Gateway Developer Guide, «Quotas for configuring and running a WebSocket in API Gateway». Revisado en noviembre de 2026. Ni la duración ni el tiempo de inactividad se pueden aumentar.

Las consecuencias: el cliente envía un ping cada pocos minutos para no cruzar los 10 de silencio, y se reconecta solo, con espera creciente y jitter, cuando la conexión se corta. API Gateway no limita las conexiones simultáneas; las limita el ritmo de conexiones nuevas. La integración con tu backend tiene un máximo de 29 segundos por mensaje.

AppSync e IoT Core: otras dos puertas

  • API Gateway WebSocket API

    Control total del protocolo: tú defines rutas y mensajes, y guardas y recorres las conexiones.
  • AWS AppSync Events

    WebSocket de publicación y suscripción administrado. Publicas en canales agrupados en namespaces, por HTTP o WebSocket, y AppSync entrega a todos los suscriptores. No guardas conexiones.
  • AWS AppSync GraphQL

    Las subscriptions de GraphQL avisan a los clientes cuando una mutation cambia los datos. Encaja si tu API ya es GraphQL.
  • AWS IoT Core

    Broker MQTT para dispositivos: sensores, vehículos, máquinas. Admite MQTT 3.1.1 y 5, QoS 0 y 1 (no QoS 2), certificados X.509 por dispositivo y MQTT sobre WebSocket.
Si tu caso esUsa
Un chat o juego con lógica propia en cada mensajeAPI Gateway WebSocket API
Avisar a miles o millones de usuarios de un mismo evento: marcadores, alertasAppSync EventsEl reparto es del servicio, no de tu código
Una app con API GraphQL que quiere datos en vivoAppSync GraphQL con subscriptions
Miles de dispositivos con conexión inestable que envían telemetríaAWS IoT CoreMQTT está hecho para redes poco confiables

Quiz

Pon a prueba lo que aprendiste

0 de 5 correctas · 5 sin responder

  1. 1.Una app de soporte necesita un chat entre clientes y agentes, sin servidores que administrar y con lógica propia para cada mensaje. ¿Qué eliges?

  2. 2.Al enviar mensajes con @connections, la función recibe errores 410 GoneException para algunos connectionId. ¿Qué significa y qué haces?

  3. 3.Los usuarios de un tablero en vivo reportan que la conexión se cae cuando nadie envía nada por un rato. ¿Qué cambias?

  4. 4.Una app deportiva debe enviar cada gol a millones de usuarios conectados. El equipo no quiere guardar ni recorrer conexiones. ¿Qué usa?

  5. 5.50.000 sensores agrícolas con conexión celular inestable envían lecturas cada minuto y reciben comandos. ¿Qué servicio encaja?