Supabase Realtime: El Framework de Orquestación que Elimina la Necesidad de Redis y Message Brokers
Supabase Realtime: cómo construir UIs reactivas y backends event-driven con canales Broadcast, Presence y Postgres Changes. Framework de 5 pasos.
El 90% de los Desarrolladores Usa Supabase Realtime Como un Espejo de Lectura, No Como un Motor de Orquestación
Estáis leyendo cambios de base de datos en tiempo real. Eso está bien. Pero si esa es la única función que le dais a Supabase Realtime, estáis usando un sistema operativo completo como un despertador.
*El problema real no es que Supabase Realtime sea limitado. Es que el 90% de los desarrolladores lo trata como un simple listener de PostgreSQL cuando es en realidad un backbone de event-streaming completo que elimina la necesidad de Redis, Pub/Sub, y servidores WebSocket independientes.*
La comparación entre Supabase vs Firebase 2026 siempre se centra en la base de datos. Pero la diferencia real está en cómo cada plataforma maneja la reactividad. Firebase usa su propio sistema propietario de listeners. Supabase usa Postgres. Y Postgres lleva décadas siendo el estándar para datos transactionales.
Aquí está lo que nadie os cuenta sobre Supabase Realtime.
Por Qué Vuestro Stack de Realtime Es Redundante
La sabiduría convencional dice: "Para features realtime, necesitas un servidor WebSocket separado (Socket.IO, Pusher) o un message broker (Redis, RabbitMQ, Kafka)."
La verdad contrarian: Supabase Realtime con Postgres LISTEN/NOTIFY y streaming basado en WAL elimina esa capa arquitectónica por completo para la mayoría de aplicaciones.
La mayoría de desarrolladores cometen tres errores críticos:
Error 1: Tratar Realtime como capa de visualización, no de escritura. Escribís a la base de datos solo para activar una actualización de UI. Un mensaje Broadcast directo cuesta cero writes.
Error 2: Ignorar los límites de conexiones. 200 usuarios concurrentes por proyecto en el tier gratuito suena restrictivo. Pero la mayoría de apps no necesitan WebSockets para todos los usuarios simultáneamente.
Error 3: No usar Presence. Implementáis lógica de heartbeat manual para detectar usuarios online. Presence hace esto automáticamente con CRDT-based conflict resolution.
❌ Stack tradicional (Firebase approach): WebSocket server + Redis + Database + Message broker
✅ Stack con Supabase Realtime: Solo Postgres + Supabase Realtime channels
La diferencia arquitectónica entre Supabase vs Firebase 2026 no es solo de base de datos. Es de cómo cada plataforma integra el realtime con vuestros datos.
Cómo Funciona el WAL (Write-Ahead Log)
Supabase Realtime usa la replicación lógica de PostgreSQL (logical decoding) para hacer streaming de cambios. Esto significa algo crucial:
El servidor de base de datos empuja cambios al instante en que una transacción hace commit. No hay polling. No hay overhead de consultas periódicas. No hay latencia artificial.
```
Flujo tradicional con polling:
Cliente → "SELECT * FROM tasks WHERE..." → 5 segundos → Base de datos → Resultado
Flujo con WAL-based realtime:
Cliente subscribe → Transacción commit → WAL stream → Cliente recibe cambio → Actualización instantánea
```
postgres_changes en Supabase Realtime es una suscripción al WAL de PostgreSQL. Cuando activáis realtime en una tabla desde el dashboard (Replication > Publication), Postgres comienza a hacer streaming de cambios a través del slot de replicación lógica.
Esto no es una tabla de triggers separada ni un proceso externo. Es PostgreSQL haciendo lo que mejor sabe hacer: gestionar datos transactionales y su propia replicación.
Los Tres Tipos de Canales No Son Iguales
Supabase Realtime ofrece tres tipos de canales que sirven propósitos fundamentalmente distintos:
Postgres Changes: Sincronización de Datos
Este canal hace streaming de cambios en tablas específicas. Es ideal para dashboards que muestran datos actualizados, listas de tareas colaborativas, o feeds que necesitan reflejar el estado actual de la base de datos.
Broadcast: Mensajería Peer-to-Peer
Este canal permite enviar mensajes arbitrarios entre clientes sin escribir en la base de datos. Indicadores de escritura, posiciones de cursor, mensajes de chat, eventos de sincronización ligera. Todo esto puede viajar por un Broadcast channel sin tocar Postgres.
❌ Incorrecto: INSERT en tabla `messages` → Trigger → Edge Function → Actualización de UI para indicador de escritura
✅ Correcto: Broadcast channel → Mensaje directo → Indicador de escritura visible → Cero writes a la base de datos
Presence: Seguimiento de Estado
Presence usa CRDT (Conflict-free Replicated Data Types) para sincronizar estado entre clientes. Detectáis join/leave automáticamente, manejáis metadata por usuario, y el sistema detecta timeouts sin lógica personalizada.
El Patrón de Orquestación Triangular
Aquí está el framework que el 90% de developers ignoran. No es una colección de trucos. Es una arquitectura coherente para construir backends event-driven sin message brokers externos.
Paso 1: Habilitad Realtime en las Tablas Correctas
Id al dashboard de Supabase → Database → Replication → Publication. Seleccionad las tablas donde los cambios deben propagate en tiempo real.
Sin este paso, nada de lo demás funciona. El WAL no fluye hacia Realtime.
```sql
-- Podéis verificar qué tablas tienen realtime habilitado
SELECT * FROM pg_publication_tables WHERE pubname = 'supabase_realtime';
```
Paso 2: Suscribíos a Canales Por Feature, No Por Tabla
Un canal por feature os da flexibilidad para múltiples tipos de eventos. No mezcléis lógica de negocio diferente en el mismo canal.
```javascript
// Crear un canal de Realtime para gestión de tareas
const tasksChannel = supabase.channel('tasks-kanban')
// Suscribirse a cambios en la tabla tasks
tasksChannel.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'tasks',
filter: 'project_id=eq.42'
},
(payload) => {
// payload.new y payload.old contienen el estado antes y después
updateKanbanState(payload)
}
)
.on(
'broadcast',
{ event: 'cursor_position' },
(payload) => {
// Sincronizar posición del cursor sin escribir en BD
updateCollaborativeCursor(payload.user_id, payload.position)
}
)
.on(
'presence',
{ event: 'sync' },
(state) => {
// Sincronizar estado de presencia cuando alguien entra/sale
updateOnlineUsers(Object.values(state.presences))
}
)
.subscribe((status) => {
if (status === 'SUBSCRIBED') {
console.log('Canal de tareas conectado')
}
if (status === 'CHANNEL_ERROR') {
// Reintentar con backoff exponencial
reconnectWithBackoff()
}
})
```
Paso 3: Usad Edge Functions para Publicar en Broadcast Channels
Aquí está el patrón que transforma Realtime de un sistema de lectura en un motor de orquestación:
```javascript
// Supabase Edge Function: Trigger en Edge
import { serve } from 'https://deno.land/std/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req) => {
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
)
const { task_id, action, user_id } = await req.json()
// Ejecutar lógica de negocio
const result = await processTaskAction(task_id, action, user_id)
// Publicar evento en Broadcast para UI reactiva
await supabase.channel('task-events').send({
type: 'broadcast',
event: action,
payload: {
task_id,
result,
timestamp: new Date().toISOString()
}
})
return new Response(JSON.stringify(result))
})
async function processTaskAction(task_id, action, user_id) {
// Lógica de negocio completa
// Este patrón permite workflows server-side completos
// sin necesidad de un message broker separado
}
```
Paso 4: Manejad el Ciclo de Vida Correctamente
Los callbacks de status no son opcionales. Son la diferencia entre una app que se recupera graceful y una que se queda en estado inconsistente:
```javascript
const channel = supabase.channel('critical-data')
channel
.on('postgres_changes', { event: '*', table: 'orders' }, handleChange)
.subscribe((status) => {
switch (status) {
case 'SUBSCRIBED':
setConnectionStatus('online')
break
case 'CHANNEL_ERROR':
// No mostrar error inmediatamente al usuario
// Intentar reconexión en background
scheduleReconnect()
break
case 'TIMED_OUT':
// Probable problema de red
setConnectionStatus('reconnecting')
break
}
})
// Función de reconexión con backoff exponencial
function scheduleReconnect() {
let attempt = 0
const maxAttempts = 5
const interval = setInterval(() => {
attempt++
channel.subscribe()
if (attempt >= maxAttempts) {
clearInterval(interval)
setConnectionStatus('offline')
// Ofrecer fallback: polling cada 30 segundos
}
}, Math.min(1000 * Math.pow(2, attempt), 30000))
}
```
Paso 5: Presence Para Estado Compartido
No implementéis lógica de heartbeat manual. Presence maneja la detección de conexión/desconexión automáticamente:
```javascript
// Channel de Presence para una sala de colaboración
const roomChannel = supabase.channel('room-42', {
config: { presence: { key: currentUser.id } }
})
roomChannel
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
// Nuevo usuario entró
showNotification(`${newPresences[0].user_name} se ha unido`)
})
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
// Usuario salió (o timeout)
updateUserList()
})
.on('presence', { event: 'sync' }, () => {
// Estado inicial sincronizado
renderOnlineUsers(roomChannel.presenceState())
})
.subscribe(async (status) => {
if (status === 'SUBSCRIBED') {
await roomChannel.track({
user_id: currentUser.id,
user_name: currentUser.name,
avatar: currentUser.avatar,
cursor_position: null
})
}
})
```
Eliminando el Message Broker
La comparación Supabase vs Firebase 2026 siempre menciona que Firebase tiene un sistema de realtime integrado. Lo que no mencionan es que Firebase Realtime Database es propietario, no extensible, y no usa Postgres.
Supabase Realtime elimina Redis, Pusher, Ably, y Socket.IO para la mayoría de casos de uso. La razón:
Un Postgres trigger puede publicar directamente en un Broadcast channel. No necesitáis un servicio externo para desacoplar productores de consumidores.
```sql
-- Trigger en Postgres que publica en Realtime Broadcast
CREATE OR REPLACE FUNCTION notify_task_completed()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 'completed' AND OLD.status != 'completed' THEN
-- Supabase usa pg_notify internamente para Broadcast
PERFORM pg_notify(
'task_events',
json_build_object(
'event', 'completed',
'task_id', NEW.id,
'completed_by', NEW.assigned_to,
'completed_at', NEW.updated_at
)::text
);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trigger_task_completed
AFTER UPDATE ON tasks
FOR EACH ROW
EXECUTE FUNCTION notify_task_completed();
```
Este trigger convierte cualquier cambio en PostgreSQL en un evento broadcast que cualquier cliente conectado puede consumir. Sin servicios adicionales. Sin configuraciones complejas.
Manejad las Limitaciones de Conexiones
El tier gratuito de Supabase permite 200 conexiones concurrentes por proyecto. Esto no significa 200 usuarios simultáneos en vuestra app.
La mayoría de usuarios no necesitan WebSockets persistentes. Un usuario que está leyendo una lista de tareas no necesita conexión realtime. Solo los usuarios activos en una vista colaborativa la necesitan.
Estrategia de escalado:
1. Conectar Realtime solo para usuarios activos. Si el usuario lleva 30 segundos sin interactuar, desconectáis el canal.
2. Usar Presence para tabs pasivas. Un usuario puede tener el navegador abierto en múltiples tabs. Solo una tab necesita estar en el canal principal.
3. Fallback a polling para vistas stale. Si la conexión se pierde o el usuario está offline, un fetch periódico cada 30 segundos mantiene los datos razonablemente actualizados.
4. Escalar a tier superior cuando sea necesario. El self-hosted Realtime server no tiene límites artificiales.
Resumen de Conceptos Clave
Supabase Realtime no es solo un sistema de notificaciones de base de datos. Es un backbone de event-streaming que elimina capas enteras de vuestra arquitectura.
Lo que debéis recordar:
→ WAL-based streaming significa cero polling, latencia instantánea desde el commit transaction
→ Postgres Changes es para sincronización de datos, Broadcast es para mensajería sin writes, Presence es para estado colaborativo
→ Broadcast permite orquestación event-driven sin message brokers externos
→ Presence maneja join/leave/timeout automáticamente con CRDT
→ 200 conexiones en tier gratuito son suficientes si conectáis selectivamente
→ El Realtime server es open-source y self-hostable si necesitáis más capacidad
El error más común: Usar postgres_changes para todo, escribir a la base de datos solo para activar UI updates, e implementar heartbeat manual cuando Presence lo hace automáticamente.
La próxima vez que penséis añadir Redis a vuestra arquitectura para decoupling de eventos, considerad primero si Broadcast channels pueden resolver el caso de uso. Para la mayoría de aplicaciones, pueden.
La diferencia entre Supabase vs Firebase 2026 no está solo en la base de datos. Está en que Supabase usa Postgres — el mismo Postgres que ya estáis usando — para todo. Firebase os da un sistema propietario. Postgres os da décadas de optimización, comunidad, y flexibilidad.
Lee el artículo completo en brianmenagomez.com
Más sobre mis servicios en brianmenagomez.com
Herramientas: Conversor IAE CNAE · Gestorias cerca de ti · Calculadora IRPF

