Supabase vs Firebase 2026: Supabase es PostgreSQL con Gabardina, y el Miedo al Lock-In es lo que Menos Merece

Supabase vs Firebase 2026: Supabase es PostgreSQL con Gabardina, y el Miedo al Lock-In es lo que Menos Merece

Programming· 17 min read

Supabase es PostgreSQL con Gabardina: tu REST API es PostgREST, tu auth es una tabla, tu realtime es LISTEN/NOTIFY y tu seguridad entera es Row Level Security

Cada "feature mágica" de Supabase es un primitivo de Postgres disfrazado de API.

Tu REST API tipo CRUD no es middleware personalizado — es PostgREST exponiendo tu esquema directamente. Tus usuarios autenticados viven en una tabla auth.users que puedes cruzar con JOIN contra tu negocio. Tu realtime se apoya en LISTEN/NOTIFY y replicación lógica. Tu modelo de autorización completo son políticas de Row Level Security evaluadas contra tu JWT.

Esto no es una paradoja curiosa ni un detalle de implementación para fanáticos de Postgres. Es la diferencia fundamental entre alquilar una plataforma y poseer un activo. Y una vez que lo ves, deja de ser posible volver a pensar en Supabase como "Firebase pero con SQL" sin sentir que estás haciendo trampa mental.

Eso significa que el miedo más grande que tiene la gente con Supabase — el lock-in — es exactamente lo que menos merece.

No estás encerrado en una plataforma propietaria. Estás pagando por una orquestación de componentes open-source pegados alrededor de un Postgres que puedes mover cuando quieras. La relación de dependencia no es con un vendor cerrado sino con un estándar de base de datos que lleva décadas establecido y que no depende de la salud financiera de ninguna startup de San Francisco.

---

El Reframing Que Cambia Cómo Arquitectas

Todo el mundo asume lo mismo: Supabase es "Firebase pero con SQL". Otro BaaS propietario al que te vas a quedar atado.

La realidad es la inversa. Con Firebase, tus datos viven detrás de una API propietaria y una migración significa reescribir queries. Cada llamada firestore.collection('x').get() es una invocación a un contrato que solo Google conoce y que Google puede cambiar cuando le plazca. Tu esquema no es tuyo: es una proyección de las reglas de Firestore. La migración no es un pg_dump sino una reescritura completa de la capa de datos de tu aplicación.

Con Supabase, el contrato entero es "es Postgres". El REST API se genera del esquema. La tabla de auth es SQL estándar. Un pg_dump se lleva todo contigo. Incluso las features que parecen más acopladas — el realtime, las políticas de seguridad — son extensiones de un motor de base de datos cuyo comportamiento está documentado en manuales que no dependen de un roadmap de startup.

El framework mental correcto: no estás comprando una base de datos. Estás comprando el ensamblaje — el pegamento entre Postgres, PostgREST, un servicio de auth en Go, un servidor de realtime en Elixir y storage compatible con S3.

Esos componentes existen todos por separado. Puedes montarlos tú mismo. Lo que Supabase vende es la integración: el DX, la cadencia de releases coordinada, el dashboard, las Edge Functions y el hecho de que todo funcione junta con un solo comando en local. Esa es la distinción importante — entre lo que es la plataforma y lo que hace la plataforma. Lo que hace es orquestar. Lo que es, sigue siendo Postgres.

Hay una analogía útil con el mercado de inteligencia artificial. Cuando un producto como Claude o GPT te da una API, estás comprando acceso a un modelo cuyo comportamiento solo puedes controlar mediante prompts y parámetros. Migrar entre proveedores significa reentrenar tus hábitos de prompting. Con Postgres, en cambio, el lenguaje de query no es una abstracción propietaria; es SQL, un estándar que precede a cualquier startup y que seguirá existiendo si Supabase desaparece mañana. La lección es la misma: cuando el contrato es un estándar abierto, la portabilidad es una propiedad del sistema, no una promesa del vendor.

---

Evidencia: Cada "Magia" es un Primitivo de Postgres

1. El REST API no existe — es tu esquema

Crea una tabla con su política de RLS y ya tienes un endpoint funcional sin escribir ni una línea de servidor:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Ahora curl https://<tu-proyecto>.supabase.co/rest/v1/posts con la cabecera de anon apikey devuelve datos. El endpoint no es una feature. Es tu tabla expuesta por PostgREST.

Detente un segundo aquí. Ese curl funciona porque PostgREST traduce cada petición HTTP a una query SQL sobre tu esquema. El filtro, la paginación, el select de columnas específicas: todo es SQL transpilado a HTTP. Si migras a un Postgres hosteado en cualquier otro sitio, puedes levantar PostgREST tú mismo en un contenedor y el mismo curl funciona con un cambio de URL. No hay código de aplicación que reescribir porque no había código de aplicación en primer lugar.

Este es el punto donde la gente suele hacer la pregunta obvia: "¿y si necesito lógica de negocio más compleja que un CRUD?" La respuesta es que PostgREST soporta funciones de Postgres expuestas como endpoints RPC, y para todo lo demás están las Edge Functions en Deno. Pero la arquitectura mental correcta es invertir el orden: empieza por el esquema y sube. No empieces por el servidor y bajes.

2. Realtime es un case study de reutilizar primitivos

postgres_changes usa replicación lógica — perfecto para eventos a nivel de fila que escalan. Presence y broadcast usan LISTEN/NOTIFY — ligero pero con límites de fan-out.

Saber cuál respalda tu canal te dice cuándo va a romperse. Presence de alta frecuencia entre miles de clientes no es lo que LISTEN/NOTIFY fue diseñado para soportar:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Lo que la mayoría de la gente no aprecia es la separación de responsabilidades que hay detrás. postgres_changes se implementa sobre el mecanismo de replicación lógica que Postgres usa desde hace años para replicación entre servidores. Cuando te suscribes con un filtro de schema/table/event, ese filtro se evalúa en el motor de Postgres, no en un hub central de mensajería. La consecuencia práctica: los payloads que llegan al cliente ya están filtrados, la autorización de RLS se aplica en la propia fila, y no hay una capa intermedia de eventos que duplique tus datos.

Esto importa porque el error clásico de las plataformas BaaS es tratar el realtime como una feature independiente de la base de datos. Aquí el realtime es un subproducto de la base de datos. Si entiendes que LISTEN/NOTIFY es broadcast puro sin garantías de orden ni persistencia, sabes que no debes construir un sistema de colas de mensajes sobre presence. Y si entiendes que la replicación lógica es el motor de postgres_changes, sabes que la fiabilidad del sistema depende de Postgres, no de un servicio de websockets propietario.

3. Tu auth es "solo una tabla" — y eso tiene las dos caras

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

La cara buena: joins entre auth.users y tus tablas de negocio te dan analítica, paneles de admin y queries por usuario gratis. Cada tabla que quieras consultar con la identidad del usuario se cruza con una foreign key hacia auth.users(id) sin necesidad de sincronizar con un servicio de usuarios separado. No hay evento de usuario duplicado en un bucket de S3 ni una llamada a una API de gestión de identidades que no conoces.

La cara mala: los metadatos de usuario viven en la misma base que tus datos de producto. PII, retención y preguntas tipo GDPR se convierten en preguntas de base de datos, no de servicio aparte. Cuando borras usuarios de auth.users, tienes que pensar en cascada: ¿qué pasa con sus posts, sus comentarios, sus sesiones? ¿Anonimizas o borras en cascada con foreign keys? ¿Cómo manejas el derecho al olvido cuando tu esquema de negocio referencia a ese usuario? Estas preguntas no desaparecen con Firebase, pero con Supabase son preguntas de SQL que tú controlas, no campos de un formulario de consola de administración.

La decisión de diseño aquí es profunda: al mantener auth y negocio en la misma base, Supabase intercambia la separación de responsabilidades por la capacidad de hacer joins. Para la mayoría de los proyectos, ese intercambio vale la pena — es el mismo trade-off que hace cualquier base de datos relacional bien diseñada. Lo importante es que la decisión la tomas tú, con conocimiento, no una que el vendor tomó por ti.

4. Edge Functions y Storage siguen siendo pluggable

Las Edge Functions corren en Deno y Storage es compatible con S3. Nada propietario:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Storage es un caso particularmente revelador. El API de storage de Supabase es un prefijo de S3: objetos, buckets, URLs firmadas para uploads y downloads, políticas de acceso por bucket. Si mañana decidieras mover tus archivos a cualquier proveedor S3 — AWS, la nube de tu empresa, MinIO en tu servidor — el único coste es cambiar el endpoint y reconfigurar los buckets. Los archivos son tuyos, el API es estándar.

Y para las Edge Functions, Deno es un runtime de JavaScript/TypeScript open-source que puedes ejecutar en cualquier sitio. La contribución de Supabase aquí es el gestionado: el deploy con un solo comando, el autoscaling, la integración con la base de datos. Pero el código que escribes no depende de ningún SDK propietario. Es el mismo patrón que en el resto de la plataforma: el ensamblaje es de Supabase, el material es estándar.

5. Todo es self-hostable

Un Docker Compose levanta Postgres más la API, auth, realtime, storage y studio localmente. docker compose up en supabase/docker arranca el stack entero.

Esto no es un truco de marketing. Significa que puedes hacer tu desarrollo local completo sin tocar la nube, con tus migraciones, tus policies y tus tests. Y significa — esto es lo importante— que el stack completo es reproducible fuera de la plataforma hosted. Vale la pena enfatizar el contraste con el resto del mercado BaaS, donde el "entorno local" suele ser un emulador con un subconjunto de features. Aquí no hay emulador: es el mismo código, el mismo Postgres, los mismos binarios.

---

El Impuesto Oculto Real: tus Hábitos, No el Vendor

Aquí está la paradoja del lock-in.

Con Firebase o Firestore, el impuesto es estructural: tus datos viven detrás de una API propietaria y la migración significa reescribir queries. Ese es el motor de negocio. El vendor gana cuando te quedas. El ecosistema completo — las reglas de seguridad de Firestore, el SDK, el modelo de documentos — se diseña para que la fricción de salir sea máxima.

Con Supabase, el "impuesto" solo existe si tú lo creas:

Si construyes tablas clickeando en el dashboard y escribes lógica de autorización en el cliente, te has encerrado tú mismo. Da igual que el Postgres subyacente sea portable. Lo que se ha vuelto inportable es tu proceso: tu esquema no está versionado, tu seguridad no está en la base de datos, y el conocimiento que has acumulado sobre tu propio sistema vive en clicks de interfaz que no puedes reproducir en otro Postgres.

Si versionas tu esquema como migraciones SQL y pones tu seguridad en RLS, tu contrato con Supabase es trivial de romper. El pg_dump se lleva los datos, las migraciones se llevan el esquema, las policies se llevan la seguridad, y un PostgREST levantado localmente replica el API.

El riesgo de lock-in no es el vendor. Son tus hábitos de equipo.

Si dependes de clicks en el dashboard y lógica client-side, te has encerrado independientemente de lo portable que sea el Postgres debajo. El Postgres no te va a salvar de un proceso que no versiona el esquema.

Esto conecta con una verdad más general del software: la fricción de migrar no la crea la tecnología, la crea el conocimiento tácito acumulado sobre la tecnología. Si ese conocimiento está en SQL versionado y policies documentadas, es transferible. Si está en la cabeza de la persona que clickeaba el dashboard, no se va a transferir nunca.

---

RLS Como Modelo de Seguridad: el Cambio Mental

Aquí va el cambio de chip que poca gente explica bien.

El frontend habla con la base de datos directamente. Eso significa que la autorización tiene que vivir en la base de datos o no existe. Esto invierte la asunción clásica de "confía en el app server".

En una arquitectura tradicional, el servidor filtra: el cliente pide, el servidor decide. Con Supabase, el cliente puede ejecutar queries directamente contra el REST API. Si la política de RLS no está en la tabla, la fila es visible y editable para cualquiera con una API key de anon. Nada te protege. La seguridad deja de ser un middleware y se convierte en una propiedad del esquema.

Esto tiene implicaciones de diseño que van más allá de "escribe un create policy". Por ejemplo:

  • Cada política debe evaluar el JWT con auth.uid() o auth.jwt(). No puedes confiar en un campo booleano guardado en el cliente.
  • Las políticas se evalúan por fila. Una query con un JOIN que cruza tablas con políticas distintas aplica cada política en su tabla. Entender cómo se componen las políticas entre joins es fundamental para no filtrar datos por accidente.
  • Las funciones de Postgres que usan security definer se saltan las políticas de RLS. Son una herramienta potente — para servicio a servicio o para lógica que necesita traspasar la seguridad por fila — pero también un riesgo si no se controlan.

El coste de depuración es real: una policy demasiado permisiva es silenciosa. Y una query que devuelve vacío cuando esperabas datos puede dejarte horas mirando un SELECT que debería funcionar.

Por eso el desarrollo local con supabase start y testear policies en SQL no es negociable. Y por eso el modelo documentado de Supabase es "RLS en cada tabla con checks de auth.uid()", no guardias en el app layer.

El impuesto de depuración se aprende. Y se paga solo en seguridad.

---

El Protocolo de Salida Ensayada

Este es el framework que te permite soltar Supabase cuando quieras — y el que convierte la portabilidad en una feature, no en esperanza.

Paso 1: Versiona tu esquema como migraciones desde el día uno

Nada de clickear tablas en el dashboard. Usa supabase db push o una carpeta de migraciones versionada. Esto es lo que hace real la afirmación de portabilidad. Cada cambio de esquema, cada policy, cada función de Postgres es un archivo .sql que tu equipo revisa en un pull request igual que revisa el código de la aplicación. Si mañana cambias de plataforma, el esquema no vive en Supabase: vive en tu repositorio.

Paso 2: RLS en cada tabla antes de escribir lógica de autorización

Pon tus policies (con auth.uid() y auth.jwt()) antes de escribir cualquier guardia en el app. RLS es la única fuente de verdad para el control de acceso. La regla es simple: si una tabla no tiene RLS habilitada con al menos una policy, es una tabla pública o una tabla rota. No deberían existir tablas en esa zona gris.

Paso 3: Diseña tu esquema de auth intencionalmente

Referencia auth.users(id) con foreign keys. Trata la tabla de usuarios como parte de tu modelo de datos. Auth y negocio conviven en una sola base consultable. Preguntas como "¿qué usuarios han escrito más posts este mes?" son un JOIN, no una integración con un servicio externo.

Paso 4: Usa Realtime con filtros en servidor

Suscríbete con postgres_changes y filtra por schema/table/event. Nada de suscribirte a tablas enteras y filtrar en el cliente. Payloads pequeños y autorización dentro de Postgres. El filtro en el cliente es una invitación a enviar datos que el cliente no debería recibir y a escalar un fan-out que no necesitas.

Paso 5: Ensaya el drill de salida antes de necesitarlo

Periódicamente, prueba un restore de pg_dump en una instancia de Postgres puro:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

"soltar Supabase en cualquier momento" es una operación ensayada, no una esperanza. Igual que se ensayan los disaster recovery plans en producción, ensayar la migración fuera de la plataforma te da dos cosas: la confianza de que puedes hacerlo, y el diagnóstico temprano de cualquier cosa que se haya acoplado accidentalmente a la plataforma.

---

La Pregunta "¿Por Qué No Montarlo Yo Mismo?"

Todos los componentes existen por separado. Postgres + PostgREST + un servicio de auth en Go + un servidor de realtime en Elixir + S3.

¿Por qué no saltarte Supabase y montarlos tú?

Por la misma razón por la que nadie monta su propio corredor de mensajes antes de lanzar un MVP: el valor no está en las piezas, está en el ensamblaje. El DX local, el dashboard, el workflow de migraciones, las Edge Functions en Deno y la cadencia de release coordinada entre componentes.

Puedes reconstruir el ensamblaje si tienes el apetito de operaciones. No quieres tenerlo.

Piénsalo en términos de coste de oportunidad. Cada hora que dedicas a parchear el realtime de Elixir o a mantener actualizado tu auth server en Go es una hora que no dedicas a tu producto. El ensamblaje de Supabase ya está resuelto, testado y versionado. Lo que te queda a ti es lo único que de verdad importa: el esquema, las policies y la lógica de negocio. La diferencia entre comprar el ensamblaje y construirlo no es el dinero — es la atención.

Existe, eso sí, un escenario legítimo para montarlo tú mismo: si tu equipo tiene una necesidad operativa que el ensamblaje hosted no cubre — una red privada estricta, compliance de datos que prohíbe proveedores externos, o un volumen de throughput que no justifica el coste del hosted. En esos casos, el hecho de que todo sea open-source y self-hostable es exactamente la salida que necesitas. Ni siquiera en ese escenario te quedas atrapado: puedes empezar hosted y migrar a self-hosted cuando la escala lo justifique, con la certeza de que los componentes son los mismos.

---

Conclusión

Supabase no es un servicio de base de datos. Es un Postgres con una capa fina de orquestación encima — y cada "feature mágica" es un primitivo de Postgres disfrazado de API.

Eso invierte la conversación sobre lock-in. Con Firestore, tu impuesto es estructural. Con Supabase, el único impuesto que pagas es el que te impones con clicks en el dashboard y lógica client-side.

La lección real del debate supabase vs firebase 2026: no estáis eligiendo entre dos BaaS. Estáis eligiendo entre una API propietaria que os cobra por quedarse y un Postgres portable que se va con vosotros en un pg_dump.

El ensamblaje lo alquila. La base de datos es vuestra.

Y eso, a la larga, es la diferencia que importa. Porque el software que sobrevive a sus vendors no es el que tiene el mejor DX ni el ecosistema más brillante — es el que se construye sobre estándares que cualquiera puede ejecutar. Postgres lleva décadas demostrando esa longevidad. Supabase, al elegir envolverlo en lugar de reinventarlo, ha hecho algo más inteligente de lo que parece a primera vista: ha convertido la base de datos más fiable de la industria en un servicio moderno sin renunciar a lo que la hace fiable. Ese es el trade-off que merece la pena hacer.

Artículos relacionados

---

¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

Brian Mena

Brian Mena

Software engineer building profitable digital products: SaaS, directories and AI agents. All from scratch, all in production.

LinkedIn