Construyendo un Chat en Tiempo Real con Socket.IO y Angular 21

image Construyendo un Chat en Tiempo Real con Socket.IO y Angular 21

Tutorial sobre cómo hacer un chat con Socket.IO y Angular 21

Tiempos estimado de lectura: 8 min

  • Backend Node.js + Socket.IO
  • Frontend Angular 21 con Signals y Standalone Components
  • Enfoque en seguridad y escalabilidad

Tabla de contenidos:

Arquitectura mínima y objetivos

Queremos un chat en tiempo real con:

  • Backend Node.js + Socket.IO (WebSocket con fallback). (socket.io)
  • Frontend Angular 21 usando Signals y Standalone Components (angular.io/guide/signals).
  • Código claro, escalable y preparado para integrarse en workflows (por ejemplo con n8n: n8n.io).

Puntos críticos: reconexión, persistencia de historial, autenticación en el handshake, y escalado horizontal (Redis adapter).

1. Servidor Node.js (ESM, Socket.IO)

Servidor básico que reemite mensajes a todos los clientes. Valida siempre payloads y añade ACKs:


// server.ts (ESM)
import http from 'http';
import express from 'express';
import { Server } from 'socket.io';
import cors from 'cors';

const app = express();
app.use(cors());
const server = http.createServer(app);

const io = new Server(server, {
  cors: { origin: 'http://localhost:4200' }
});

io.on('connection', socket => {
  console.log('connected', socket.id);

  socket.on('chat:send', (payload, ack) => {
    // Validación mínima
    if (!payload?.text || typeof payload.text !== 'string') {
      return ack?.({ status: 'error', reason: 'invalid_payload' });
    }
    const msg = { id: socket.id, text: payload.text, ts: Date.now() };
    io.emit('chat:message', msg);
    ack?.({ status: 'ok', id: msg.id, ts: msg.ts });
  });

  socket.on('disconnect', () => console.log('disconnected', socket.id));
});

server.listen(3000);

Buenas prácticas aquí: validar, usar ACKs y no confiar en el cliente para metadatos críticos.

2. Servicio Angular 21 con Signals (client)

Encapsula Socket.IO en un servicio. Usamos un WritableSignal para mensajes; la UI lee la señal directamente sin pipes ni suscripciones manuales.


// src/app/services/chat.service.ts
import { Injectable, signal, WritableSignal } from '@angular/core';
import { io, Socket } from 'socket.io-client';

export interface ChatMessage { id: string; text: string; ts: number }

@Injectable({ providedIn: 'root' })
export class ChatService {
  private socket: Socket;
  public messages: WritableSignal = signal([]);

  constructor() {
    this.socket = io('http://localhost:3000', { transports: ['websocket'] });
    this.socket.on('chat:message', (m: ChatMessage) => {
      this.messages.update(cur => [...cur, m]);
    });
  }

  send(text: string) {
    return new Promise((resolve, reject) => {
      this.socket.emit('chat:send', { text }, (res: any) => {
        res?.status === 'ok' ? resolve(res) : reject(res);
      });
    });
  }

  disconnect() { this.socket.disconnect(); }
}

Signals ofrecen lectura sin async pipes y actualizaciones finas del DOM (ideal para listas largas).

3. Componente Standalone de Angular

Standalone Component que consume el servicio:


// src/app/chat/chat.component.ts (Standalone)
import { Component, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ChatService } from '../services/chat.service';

@Component({
  standalone: true,
  imports: [CommonModule, FormsModule],
  selector: 'app-chat',
  template: `
    
{{ m.id | slice:0:6 }}: {{ m.text }} {{ m.ts | date:'HH:mm:ss' }}
` }) export class ChatComponent { chat = inject(ChatService); text = ''; async send() { const t = this.text.trim(); if (!t) return; try { await this.chat.send(t); this.text = ''; } catch { /* manejar error */ } } }

4. Llevarlo a producción: checklist técnica

  • Escalado: usa Redis adapter para Socket.IO en múltiples instancias (socket.io/docs/v4/using-multiple-nodes).
  • Persistencia: guarda mensajes en DB (Mongo/Postgres) y carga historial en conexión.
  • Autenticación: valida JWT en el handshake con io.use((socket, next) => ...).
  • Rooms & namespaces: separa conversaciones y reduce broadcast innecesario.
  • Observabilidad: métricas de hit-rate, latencia, reconexiones.
  • Seguridad: rate-limiting por socket, sanitización de texto, validación de tamaño.

5. Integración con automatizaciones y agentes

En entornos productivos el chat suele disparar acciones: crear tickets, invocar agentes de IA o lanzar workflows. Aquí entra el valor de orquestación con n8n. En Dominicode Labs documentamos blueprints para:

  • Exponer eventos de chat a n8n via webhook/Redis.
  • Enriquecer mensajes con agentes de IA que clasifican o responden automáticamente.
  • Ejecutar pipelines (crear ticket, notificar Slack, persistir metadatos).

Dominicode Labs tiene plantillas y ejemplos de arquitectura para conectar Socket.IO con workflows productivos sin acoplar la lógica de negocio dentro del servidor de sockets.

Conclusión

Este tutorial sobre cómo hacer un chat con Socket.IO y Angular 21 muestra un camino claro: Signals + Standalone components simplifican el front; Socket.IO gestiona el transporte; Redis, persistencia y autenticación convierten el prototipo en sistema robusto. Si tu objetivo es más que “un chat que funciona” —si quieres que el chat forme parte de procesos automatizados en producción— diseña la integración con workflows y agentes desde el principio.

FAQ

Socket.IO es una biblioteca que permite la comunicación en tiempo real entre clientes y servidores utilizando WebSockets u otros transportes.

Las señales en Angular son una nueva forma de crear y gestionar el estado, permitiendo una comunicación eficiente entre componentes.

Los componentes standalones en Angular son componentes que no dependen de módulos para su funcionamiento, facilitando su reutilización.

La implementación en producción debe considerar aspectos como escalabilidad, persistencia, autenticación y seguridad.

n8n es una herramienta de automatización que permite conectar diferentes aplicaciones de forma visual y sencilla, optimizando flujos de trabajo.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *