Vercel y Supabase parecen ser la pareja ideal para tu siguiente aplicación. Con Edge Functions que se despliegan en milisegundos, Postgres administrado y autenticación preconfigurada, las ventajas son evidentes. La propuesta es tentadora: un entorno full-stack en minutos, sin complicaciones de DevOps. Sin embargo, tres semanas más tarde de comenzar tu proyecto de dashboard colaborativo, te das cuenta de algo preocupante: esta integración tiene más capas de complejidad de las que los tutoriales de YouTube mencionan.
Photo: Stephen Dawson on Unsplash
No se trata de bugs evidentes o documentación incompleta. El problema radica en algo más profundo: Vercel se enfoca en despliegues instantáneos y distribución global, mientras que Supabase prioriza la consistencia de datos y subscripciones persistentes. Al intentar combinar ambos, te enfrentas a dos paradigmas arquitectónicos que no se llevan tan bien. Lo curioso es que nadie te lo advirtió porque todos están encantados con el "Hello World" que se despliega en segundos.
El problema oculto detrás de las Edge Functions y Postgres
Vercel ofrece Edge Functions en más de 70 regiones globales, y Supabase promete conexiones directas a Postgres. ¿Suena perfecto? En la teoría quizás, pero la práctica revela un sistema distribuido con latencias inesperadas.
Cada consulta desde tu Edge Function en Sao Paulo a tu instancia Supabase en us-east-1 cubre miles de kilómetros. Imagina añadir autenticación JWT y validación de permisos. De repente tu "función inmediata" tarda 400-600ms en responder. Esto se hace crítico en aplicaciones colaborativas donde cada tecla presionada puede activar validaciones complejas.
La arquitectura de Vercel asume funciones sin estado y efímeras, mientras que Supabase confía en conexiones persistentes para escuchar cambios en tiempo real. Estos dos enfoques son sólidamente incompatibles. ¿Cómo mantener una conexión WebSocket estable desde una Edge Function que podría ejecutarse en cualquier región?
El infierno de las conexiones concurrentes
En el plan Pro de Supabase, obtienes 60 conexiones directas a Postgres por $25/mes. Suena razonable, hasta que tu aplicación de chat es lanzada y 200 usuarios intentan conectarse al mismo tiempo. Cada subscripción en tiempo real consume una conexión, y Postgres comienza a rechazar nuevas.
La solución oficial es usar el Pooler de Supabase, que agrega conexiones mediante PgBouncer en modo transacción. Pero, ojo, el pooling en este modo no soporta todas las características de Postgres que podrías usar. LISTEN/NOTIFY no funciona y los prepared statements fallan. Si activaste RLS, cada consulta debe establecer el contexto de sesión, aumentando el overhead. ¿No es frustrante?
Terminas con una arquitectura híbrida: conexiones directas para Realtime, pooler para consultas regulares y un sistema de respaldo para casos donde el pooler falla. Esto no está en ningún "Getting Started".
Realtime Subscriptions: el talón de Aquiles de la arquitectura serverless
Photo: Luke Chesser on Unsplash
Supabase Realtime utiliza WebSockets sobre Phoenix Channels para transmitir los cambios de la base de datos. Modificas una fila y todos los clientes suscritos reciben la actualización. Aparentemente elegante, pero cuando tu frontend está en Vercel y tu backend en Supabase, construyes un sistema de mensajería distribuido sin haberlo planeado.
El principal problema radica en la gestión del estado del cliente. Vercel fomenta el uso de componentes React que se renderizan a demanda, mientras que Supabase Realtime espera que tu cliente mantenga una única conexión WebSocket estable. Estos paradigmas chocan brutalmente cuando comienzas a escalar.
Imagina un dashboard de análisis en tiempo real con múltiples widgets, cada uno suscrito a diferentes tablas. Con la API estándar de Supabase, eso resulta en 15 canales WebSocket separados. Cada uno con su propio heartbeat y lógica de reconexión. En un navegador puede funcionar, pero en una app web móvil con conexión inestable, es un desastre de memory leaks y reconnection storms.
La trampa del RLS en tiempo real
Row Level Security parece perfecto para permisos granulares. Defines políticas SQL y Postgres gestiona el resto, suena ideal. Pero al combinar RLS con Realtime subscriptions, las cosas se complican.
Cada suscripción evalúa tus políticas RLS para determinar qué filas puede ver un usuario. Esto significa potencialmente ejecutar consultas complejas en cada operación insert/update. Con 500 usuarios concurrentes en la misma tabla, cada operación desencadena 500 evaluaciones de políticas.
El costo computacional escala linealmente con las conexiones activas y como Postgres ejecuta RLS checks dentro del mismo proceso de procesamiento de escrituras, esto puede ocasionar una significantiva contention. Tu write throughput se degrada justo cuando más lo necesitas: bajo carga alta.
La solución requiere repensar tu modelo de datos. Separar tablas de eventos de tablas de estado. Usar vistas materializadas para precomputar permisos. Crear tu propia capa de broadcast que filtre en la capa de aplicación en vez de Postgres. Dicho esto, básicamente estás desmantelando gran parte de las ventajas que te prometieron.
El costo oculto de las migraciones y el control de versiones
Supabase gestiona migraciones mediante archivos SQL versionados, y Vercel despliega tu frontend automáticamente en cada push. El problema surge al coordinar cambios de esquema con cambios de código en un sistema distribuido.
Imaginas desplegar una migración que renombra una columna. Supabase la aplica al instante. Pero tu Edge Function aún referencia el nombre antiguo porque el deploy de Vercel toma 45 segundos más. Durante ese intervalo, tu app está rota. No hay rollback automático ni deployment coordinado.
La solución teórica es blue-green deployments con compatibilidad de esquema. Mantener dos versiones del esquema simultáneamente: una que soporta el código antiguo y nuevo. Alias de columnas, vistas compatibles, triggers de migración gradual. Terminas haciendo el trabajo de un database reliability engineer sin haberlo planeado.
El problema de los datos en preview deployments
Vercel crea un preview deployment por cada pull request. Es genial para pruebas visuales. Pero si tu app depende de Supabase, cada preview necesita acceso a datos reales o un ambiente de staging completo.
Opción 1: todos los previews apuntan a la misma instancia de desarrollo de Supabase. Ahora múltiples ramas comparten la misma base de datos. Migraciones de una branch pueden romper funcionalidades de otra. Caos total.
Opción 2: cada preview tiene su propia instancia de Supabase. Necesitas automatizar creación de proyectos, restauración de backups, configuración de secrets, sincronización de Edge Functions. Estás construyendo tu propio Heroku Review Apps desde cero.
Opción 3: empleas datos sintéticos mediante mocking. Pero entonces tus previews no prueban la interacción real con Supabase, invalidando gran parte del valor de tener previews.
No hay una solución perfecta. Cada enfoque tiene trade-offs significativos que no se mencionan en los tutoriales de "Deploy your SaaS in 10 minutes". ¿Vale la pena la complejidad?
La factura que no esperabas: egress y function invocations
Vercel cobra por ancho de banda y tiempo de ejecución de funciones más allá de sus generosos free tiers. Supabase cobra por transferencia de datos y computación de base de datos. Al construir una app en tiempo real, ambos costos explotan de formas inesperadas.
Cada mensaje Realtime es un evento de salida desde Supabase. Un chat con 100 mensajes por minuto entre 50 usuarios suma 5,000 eventos de salida por minuto. A $0.09 por GB tras los primeros 10GB, esto se acumula rápido. No hablamos solo de texto: cualquier referencia a blobs, metadata, cualquier heartbeat.
En Vercel, cada configuración de subscripción, consulta y verificación de autenticación es una invocación de función. Si tu app realiza 10 consultas diferentes por carga de página y tienes 10,000 usuarios activos diarios, son 100,000 invocaciones diarias solo en cargas iniciales. Añade polling para funciones que no pudiste resolver con Realtime puro y fácilmente alcanzas el millón de invocaciones mensuales.
El costo invisible de mantener conexiones activas
Supabase Realtime mantiene conexiones WebSocket abiertas, consumiendo recursos en su infraestructura Phoenix, por lo cual tienen límites estrictos de conexiones concurrentes. Además, usuarios que dejan tu pestaña abierta pero no usan activamente la app siguen consumiendo conexiones.
Implementar detección de presencia y limpieza de conexiones se vuelve crucial. Necesitas detectar cuando un cliente está inactivo, cerrar la conexión, implementar un exponential backoff en reconnects. Todo esto es lógica de redes de bajo nivel que no esperabas implementar al elegir "servicios gestionados".
Si estos mecanismos fallan, podrías despertar con una factura de overage porque miles de conexiones zombies consumieron tus recursos durante un fin de semana.
Cuando Supabase Edge Functions complica más de lo que resuelve
Supabase anunció sus propias Edge Functions basadas en Deno. Teóricamente, esto resolvería el problema de latencia: tu lógica serverless correría cerca de tu base de datos. Pero ahora tienes código ejecutándose en dos plataformas diferentes con runtimes distintos.
Vercel Edge Functions usan un subset de Web APIs sobre V8 isolates y Supabase Edge Functions usan Deno runtime. Paquetes de Node que funcionan en una no necesariamente funcionan en la otra. Tus dependencias deben ser cross-compatible o duplicas lógica.
El deployment también se complica. Manejas dos sistemas de CI/CD: Vercel para tu frontend y algunas funciones, Supabase CLI para tus funciones del lado de datos. Coordinar lanzamientos coherentes entre ambos entornos añade fricción.
Y la observabilidad se fragmenta: logs de Vercel en un dashboard, logs de Supabase en otro. Tracing distribuido entre ambos es prácticamente inexistente en sus ofertas estándar. Cuando algo falla en producción, estás jugando al detective con múltiples interfaces.
El camino correcto: arquitectura híbrida consciente
No estoy diciendo que Vercel + Supabase sea una mala combinación. En mi experiencia, requiere más reflexión arquitectónica de lo que muchos anticipan. La curva de aprendizaje no está en APIs individuales, sino en entender implicaciones de sistemas distribuidos.
Si decides seguir esta ruta, hazlo con los ojos bien abiertos. Diseña para consistencia eventual desde el inicio. Implementa circuit breakers para cuando Supabase o Vercel tengan problemas. Monitorea latencias end-to-end, no solo tiempos de respuesta individuales. Presupuesta no solo el costo inicial, sino también el costo a escala.
Considera seriamente si realmente necesitas el despliegue Edge para tu caso. Si tu mercado es principalmente LATAM y tu base de datos está en Virginia, pasar a Edge Functions en 70 regiones puede sumar latencia, no reducirla. A veces un monolito Next.js en una región cercana a tu base de datos es objetivamente superior.
La paradoja de las herramientas modernas es que hacen lo simple trivial y lo complejo invisible hasta que es demasiado tarde. Vercel y Supabase son excelentes herramientas. Pero su integración para aplicaciones en tiempo real sofisticadas no es el camino sin fricciones que los case studies de cinco minutos sugieren. ¿Vale la pena la complejidad añadida o estás optimizando prematuramente para una escala que quizás nunca llegue? Para cerrar, la elección depende de tus necesidades y previsión, pero vale la pena pensarlo detenidamente.