Sigues Llamando a Supabase "el Clon de Firebase". Ese Error de Categoría es el Que te Va a Costar los Datos de tus Usuarios
Piensas que Supabase es un clon de Firebase con esteroides. La narrativa lleva años instalada: "alternativa open-source a Firebase", "ideal para MVPs", "empieza en minutos sin pensar en el backend".
*El problema no es la etiqueta. El problema es que la etiqueta te hace ignorar la verdadera naturaleza del producto. *
Supabase no es una base de datos NoSQL con reglas declarativas como Firestore. Es una instancia completa de PostgreSQL real — la base de datos relacional más probada del planeta — vestida de serverless. Y la consecuencia práctica es brutal: el modelo de seguridad depende de ti, no de un JSON que escribes en un dashboard.
Firebase ponía sus security rules como una capa entre el cliente y los datos. Supabase baja la seguridad hasta SQL policies que viven en el propio esquema. Más auditable, más portable, sí.
Pero hay un matiz que la mayoría descubre demasiado tarde: *una tabla creada con RLS desactivado es legible y escribible por cualquier desconocido que tenga la anon key pública. *
Y la anon key es pública por diseño. Va en tu bundle de frontend.
Espera. Vamos al código antes de teorizar.
El Gotcha Que Nadie te Cuenta: Tu Tabla Sin Políticas es un Buffet Abierto
Crear una tabla y asumir que está protegida es el error más común en 2026.
```sql
-- Esto se ve inocuo. No lo es.
create table profiles (
id uuid references auth.users primary key,
email text,
full_name text
);
-- El endpoint REST generado por PostgREST ya existe.
-- GET /rest/v1/profiles → 200 OK con TODAS las filas
```
Ahí mismo, sin tocar una línea más, cualquier persona con la anon key puede hacer `GET /rest/v1/profiles` y recibir el email de todos tus usuarios.
La anon key viaja en tu frontend. Está en las Network tabs de cualquiera que abra DevTools.
Ahora activa el RLS y repite la misma llamada:
```sql
alter table profiles enable row level security;
-- La misma petición ahora devuelve cero filas.
-- GET /rest/v1/profiles → [] (vacío)
```
*Esa diferencia — de exponerte a estar a salvo — es exactamente un `alter table`. *
Y el problema es que el estado por defecto de PostgreSQL es abierto. El RLS no se activa solo. Firebase al menos forzaba que escribieras sus reglas para que hubiera cualquier restricción. Supabase te da el poder total de PostgreSQL y asume que vas a ser disciplinado.
El 90% de los proyectos que empiezan un MVP con Supabase no lo son.
La Query Que te Salva el Culo
Puedes enumerar todas tus tablas sin RLS en una sola consulta:
```sql
select schemaname, tablename
from pg_tables
where schemaname = 'public'
and tablename not in (
select tablename from pg_policies
);
```
Corre esa query en tu consola de Supabase ahora mismo.
Si devuelve algo, tienes datos al descubierto.
Por Qué el "Empieza Rápido, Migra Después" Está Invertido
Toda la doctrina de los MVPs con Firebase se resumía en una frase: "No pierdas tiempo en esquemas, migra cuando crezcas."
El problema de esa frase es que construías sobre NoSQL y el coste del cambio no era diferible — se pagaba en el peor momento posible, cuando ya tenías datos vivos, features encadenadas y clientes.
*Con Supabase la trayectoria está invertida: empiezas en PostgreSQL real y nunca necesitas migrar. *
Porque el esquema ES la API. PostgREST genera el endpoint REST directamente del esquema de la base de datos. Cada tabla, cada vista, cada columna se convierte en un endpoint. Cambias el esquema y el API cambia solo.
Esto colapsa una capa entera de CRUD.
En Firebase escribes handlers, validaciones, transformaciones. En Supabase escribes la migración y el backend se genera solo:
```sql
-- La migración ES el contrato de la API.
create table tasks (
id uuid primary key default gen_random_uuid(),
user_id uuid references auth.users not null,
title text not null,
done boolean default false,
created_at timestamptz default now()
);
alter table tasks enable row level security;
create policy "users read own tasks"
on tasks for select
using (auth.uid() = user_id);
create policy "users insert own tasks"
on tasks for insert
with check (auth.uid() = user_id);
```
Con esas 25 líneas tienes: endpoints REST filtrados por usuario, insert limitado al dueño, y tipado generado casi automático con los clients de Supabase.
En Firestore tendrías que escribir las security rules JSON, configurar colecciones, y después construir la capa de servicios.
❌ Firebase: Rules JSON en consola + reglas de datos manuales + handlers por recurso.
✅ Supabase: RLS en SQL junto al esquema + PostgREST + un tier de CRUD que desaparece.
Pero ojo — esa inversión tiene un lado oscuro.
El Esquema es Tan Bueno Como Tu Disciplina
Si el esquema es la API, un esquema descuidado es una API descuidada.
En Firebase, un refactor de una ruta se aislaba en un handler. En Supabase, un cambio de esquema toca la base de datos directamente, y las policies incorrectas se propagan a cada petición.
Eso no es un argumento contra Supabase. Es un argumento contra la pereza arquitectónica.
Realtime: Donde la Base PostgreSQL Enseña las Costuras
Firebase tiene un Realtime Database pensado para fan-out de chats. Supabase hace realtime sobre la replicación lógica de PostgreSQL — el mismo mecanismo que usa la DB para mantener réplicas sincronizadas.
La ventaja es enorme: *el stream de eventos que alimenta tu UI es el mismo canal de escritura canónico de la base de datos. *
```js
const channel = supabase
.channel('task-updates')
.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'tasks' },
(payload) => console.log(payload.new)
)
.subscribe();
```
Pero hay que ser deliberados. Publicar toda la tabla en realtime es broadcasting innecesario. Supabase hace que el realtime sea opt-in por tabla via `supabase_realtime` publication — tú decides qué tabla emite eventos.
El trade-off es real: chat con fan-out muchos-a-muchos requiere diseño de canales cuidado. Firestore resuelve eso más fácilmente a escala pequeña, pero no te da SQL encima de los datos.
Depende de qué estés construyendo.
El Método RLS-First: Cinco Pasos para No Exponerte
Llamo a esto El Método RLS-First. No es opcional. Es lo único que separa un MVP de una brecha de datos.
Paso 1: Local, Nunca Nube Directa
Arranca con el CLI de Supabase o Docker. Diseña el esquema con migraciones desde el día uno.
```bash
supabase init
supabase start
supabase db push
```
La base de datos local es la fuente de verdad, no los clics en el dashboard. Cuando despliegas, las migraciones se aplican con `supabase db push` y todo queda versionado.
Paso 2: RLS como Preocupación de Primer Nivel
Activa RLS en cada tabla como parte de la migración inicial. Escribe las policies junto a la definición de la tabla. No en un "paso de seguridad" posterior.
Añade un check en CI que falle el build si hay alguna tabla con RLS desactivado. Es la version del linter pero para seguridad. Una línea en tu pipeline y el error deja de ser humano.
Paso 3: Diseña para la Anon Key Pública
Asume que el cliente puede leer todo lo que una policy permite. La service key jamás toca el frontend.
Para operaciones privilegiadas — incrementar un contador, operaciones administrativas — usa SECURITY DEFINER functions:
```sql
create or replace function public.increment_counter(row_id uuid)
returns void
security definer
set search_path = public
as $$
update counters set value = value + 1 where id = row_id;
$$ language sql;
```
La función ejecuta con los privilegios del propietario, no del cliente. Eso te da una válvula de escape controlada sin abrir policies peligrosas.
Paso 4: Realtime Deliberado, No Automático
Suscríbete solo a las tablas que necesitan updates en vivo. Filtra los canales. No transmitas tablas enteras.
Rendimiento y privacidad van de la mano aquí: menos broadcast, menos superficie de ataque.
Paso 5: Auth, Storage y Realtime Integrados
Conecta `auth.uid()` en las policies de RLS. Los buckets de storage deben referenciar el mismo user id. Verifica el flujo completo — registro, subida, fetch — contra el stack local antes de desplegar.
```sql
create policy "users read own avatars"
on storage.objects for select
using (bucket_id = 'avatars' and auth.uid() = owner);
```
¿Y el Lock-In? Hablemos de lo que es Realmente Pegajoso
Contesta esta objeción directamente porque la escucho cada semana.
El 80% de lo que usas en Supabase es PostgreSQL estándar. Puedes volcar la base (`pg_dump`) y emigrar a cualquier proveedor Postgres — Neon, RDS, tu propio servidor — en un día. Las policies RLS son SQL puro. Las migraciones son SQL puro. pgvector es una extensión estándar.
Las capas pegajosas de verdad son Auth y Edge Functions. Esas tienen APIs propietarias.
La válvula de escape: self-hosting con Docker o el CLI. Si un día odias la plataforma, te la montas tú. No es bonito, pero existe. Firebase no te ofrece siquiera esa opción.
Y sobre los outages y los límites de conexiones — hay que separar el FUD de 2022 de la realidad operativa. La gestión se ha estabilizado, pero PostgreSQL serverless tiene costuras reales: connection pooling, cold starts. El que te diga que son perfectos te está vendiendo algo.
Mi consejo: usa Supabase para el MVP, diseña las migrations como si fueran portátiles, y decide a escala si self-host o te quedas. *La disciplina de diseño te da la opción. La pereza te la quita. *
Veredicto 2026
Firebase no es basura. Sigue siendo la respuesta correcta para casos muy concretos: aplicaciones con patrones de datos extremadamente simples, equipos que no quieren tocar SQL bajo ninguna circunstancia.
Pero el 90% de los proyectos que elegían Firebase por comodidad acababan llorando en Stack Overflow cuando necesitaban un JOIN o una consulta analítica.
Supabase te da el poder de una base de datos real desde el día uno. Y con ese poder viene una responsabilidad que Firebase te quitaba de encima: *si no activas el RLS, tus datos están abiertos a internet. *
El impuesto oculto no es el lock-in. Es la línea de SQL que no escribiste.
Corre la query que te doy arriba. Si tu tabla no aparece, estás a salvo. Si aparece, tienes trabajo que hacer hoy mismo — no la semana que viene.
La buena noticia es que el arreglo es un comando:
```sql
alter table tu_tabla enable row level security;
```
Luego escribe tus policies. Y añade el check de CI.
Porque en 2026, el que ignora RLS no pierde el trabajo por falta de talento.
Lo pierde por haber expuesto la tabla de sus clientes a todo internet durante 18 meses sin saberlo.
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

