Tutorial: Crea una app de chat sencilla con un centro de chat

En este tutorial, construyes un flujo de chat sencillo en tiempo real usando un hub de chat Web PubSub.

Tú:

  • Configura un servidor backend para emitir URLs de acceso a los clientes
  • Conecta clientes con un centro de chat Web PubSub
  • Creación de una sala
  • Enviar y recibir mensajes
  • Gestionar la membresía de la sala

Al final, tienes una experiencia de chat de trabajo respaldada por Azure Web PubSub.

Prerequisites

  • Una suscripción de Azure
  • Node.js 18 o posterior

Crea un recurso Web PubSub con un centro de chat

Crea un recurso de Azure Web PubSub y configura un chat hub llamado demo-chat.

Instalación de dependencias

Dependencias de servidor

npm install express @azure/web-pubsub @azure/web-pubsub-express

Dependencias del cliente

npm install @azure/web-pubsub-chat-client

Paso 1: Crear el servidor backend

El servidor backend es responsable de:

  • Autenticación de usuarios
  • Emisión de URLs de acceso al cliente

Código del servidor

import express from 'express';
import { WebPubSubServiceClient } from '@azure/web-pubsub';
import { WebPubSubEventHandler } from '@azure/web-pubsub-express';

const hubName = 'demo-chat';
const port = process.env.PORT || 3000;

const connectionString = process.env.WEB_PUBSUB_CONNECTION_STRING;
if (!connectionString) {
  throw new Error('WEB_PUBSUB_CONNECTION_STRING is not set');
}

const app = express();

const serviceClient = new WebPubSubServiceClient(
  connectionString,
  hubName,
  { allowInsecureConnection: true }
);

Por qué existe este paso

Aunque el servicio Web PubSub soporta conexiones anónimas, el patrón de producción más común utiliza un modelo de acceso emitido por el servidor.

Tu servidor genera una URL de acceso al cliente con límite de tiempo que codifica la identidad del usuario y los permisos. Este enfoque:

  • Mantiene las credenciales seguras
  • Permite que tu aplicación controle la autenticación y la autorización
  • Está en línea con las expectativas de seguridad empresarial

Paso 2: Añadir un punto final de negociación

El punto final de negociación devuelve una URL de acceso al cliente que los clientes de chat usan para conectarse.

app.get('/negotiate', async (req, res) => {
  console.log(`received negotiate request: ${JSON.stringify(req.query)}`);

  const userId = req.query.userId;
  if (!userId) {
    return res.status(400).send('Missing userId');
  }

  const token = await serviceClient.getClientAccessToken({
    userId,
  });

  res.json({
    url: token.url,
  });
});

Por qué existe este paso

Los clientes de chat deben conectarse como un usuario específico.

El punto de conexión de negociación es donde tu aplicación:

  • Asigna una identidad a nivel de aplicación a un usuario de chat
  • Emite una URL de acceso temporal de alcance limitado
  • Decide quién puede conectar

En producción, este punto final suele ser:

  • Verifica la autenticación (cookies, encabezados, tokens)
  • Hace cumplir las normas de autorización

Paso 3: Iniciar el servidor

app.listen(port, () => {
  console.log(`Server listening at http://localhost:${port}`);
});

Tu backend ya está listo para aceptar conexiones de clientes.

Paso 4: Conecta a los clientes con el chat hub

En el cliente, busca las URLs de acceso del servidor e inicia sesión en el demo-chat hub.

import { ChatClient } from '@azure/web-pubsub-chat-client';

// Fetch a fresh client access URL from the negotiate endpoint.
const getClientAccessUrl = (userId) =>
  fetch(`/negotiate?userId=${userId}`)
    .then(r => r.json())
    .then(d => d.url);

// Option 1: start with a one-time client access URL.
const alice = await ChatClient.start(await getClientAccessUrl('alice'));
console.log(`Started as: ${alice.userId}`);

// Option 2: start with a credential so the client can refresh the URL itself.
const charlie = await ChatClient.start({
  getClientAccessUrl: () => getClientAccessUrl('charlie'),
});
console.log(`Started as: ${charlie.userId}`);

Por qué existe este paso

El hub de chat de Web PubSub se basa en el modelo de conexión Web PubSub. Este paso de autenticación:

  • Establece una conexión en tiempo real
  • Asocia la conexión con la identidad de un usuario
  • Permite funciones específicas del chat como salas e historial de mensajes

Paso 5: Escucha los eventos de chat

Regístrate a los oyentes para recibir actualizaciones en tiempo real.

alice.on('message', (event) => {
  const msg = event.message;
  console.log(`Alice received: ${msg.createdBy}: ${msg.content.text}`);
});

alice.on('room-joined', (event) => {
  console.log(`Alice joined room: ${event.room.title}`);
});

charlie.on('message', (event) => {
  const msg = event.message;
  console.log(`Charlie received: ${msg.createdBy}: ${msg.content.text}`);
});

charlie.on('room-joined', (event) => {
  console.log(`Charlie joined room: ${event.room.title}`);
});

El cliente de chat es un emisor de eventos. Además de message y room-joined, puedes detectar room-left, member-joined, member-left, started y stopped. Úsalo off con los mismos argumentos para eliminar a un oyente.

Por qué existe este paso

El chat es inherentemente impulsado por eventos. Estos oyentes permiten a tu aplicación:

  • Reaccionar a los mensajes entrantes
  • Actualiza la interfaz cuando los usuarios se unan a las salas
  • Mantente sincronizado en varios dispositivos o pestañas del navegador

Paso 6: Crea una habitación y envía mensajes

Crea una sala y añade miembros iniciales:

const room = await alice.createRoom('My Room', ['charlie']);

Envía un mensaje a la sala:

await alice.sendToRoom(room.roomId, 'Hello!');

Los mensajes se entregan en tiempo real a todos los miembros de la sala.

Por qué existe este paso

Las salas ofrecen estructura para el chat:

  • Definen quién recibe los mensajes.
  • Mantienen el historial de mensajes.
  • Permiten que el chat escale más allá de la mensajería uno a uno.

Paso 7: Obtener el historial de mensajes

Recuperar mensajes anteriores de una sala. listRoomMessages devuelve un iterador asíncrono paginado, para que puedas iterar directamente sobre cada mensaje:

for await (const msg of alice.listRoomMessages(room.roomId)) {
  console.log(`${msg.createdBy}: ${msg.content.text}`);
}

O cargar el historial de una página cada vez (por ejemplo, "cargar 50 y luego 50 más al desplazarse hacia arriba"):

const pages = alice.listRoomMessages(room.roomId).byPage({ maxPageSize: 50 });
const firstPage = await pages.next();
const messages = firstPage.value ?? [];

Por qué existe este paso

Los clientes recién conectados suelen necesitar contexto.

El historial de mensajes permite que tu aplicación:

  • Mostrar mensajes existentes
  • Reanudar el chat tras reconectarnos
  • Soporte para uso multidispositivo

Paso 8: Gestionar a los miembros de la sala

Añadir un usuario a una sala:

await alice.addUserToRoom(room.roomId, 'bob');

Sacar a un usuario de una sala:

await alice.removeUserFromRoom(room.roomId, 'bob');

Los cambios de membresía entran en vigor de inmediato.

Paso 9: Limpiar

Cuando el cliente ya no necesita recibir mensajes:

await alice.stop();
await charlie.stop();

Lo que ha creado

En esta guía de inicio rápido, aprenderá a:

  • Emitieron URLs de acceso seguro al cliente desde un servidor
  • Conecté clientes a un centro de chat
  • Creé y me uní a salas de chat
  • Mensajes enviados y recibidos en tiempo real
  • Historial de mensajes cargados
  • Pertenencia administrada a salas

Todo ello sin gestionar servidores WebSocket, lógica de dispersión o persistencia de mensajes.