Vibe Coding — tu primera app sin saber programar — 7. Base de datos: tus datos en todas partes

19 min read min de lecture
Capítulo 07

Base de datos: tus datos en todas partes

Capítulo 7 de 10 · 70%

Objetivos de este capítulo

  • Comprender qué es una base de datos y cómo se organiza
  • Migrar tu app del localStorage a una verdadera base en línea
  • Garantizar que cada usuario solo ve sus propios datos

La pieza que falta

Los alumnos de Tom ya se conectan con su email — pero Lina, fiel a su puesto, señala que sus hábitos de la biblioteca siguen sin estar en su teléfono. Normal: la autenticación dice quién está ahí, pero los datos, ellos, siguen viviendo en el localStorage de cada dispositivo. Falta un lugar central donde guardar los hábitos de cada cuenta: una base de datos en línea. Esa es la obra de este capítulo, y probablemente la más transformadora del curso — después de ella, tu app deja de ser un juguete de navegador para convertirse en un verdadero servicio.

El cambio de lógica cabe en una frase: en lugar de que cada navegador guarde su propia copia de los datos, todos los dispositivos leen y escriben en el mismo sitio, un servidor de base de datos. Tu app se convierte en una ventanilla: muestra lo que dice la base, y registra ahí cada acción. Lina marca «Lectura» en su teléfono; la marca se va a la base; al día siguiente en la biblioteca, la app pide a la base «muéstrame los hábitos de Lina» y todo está ahí.

Una base de datos, en concreto

Si ya has abierto una hoja de cálculo, conoces el 80 % del concepto. Una base de datos es un conjunto de tablas; cada tabla se parece a una hoja de cálculo con columnas (los tipos de información) y filas (los registros). Cada fila posee un identificador único (id), y — esta es la parte mágica — una fila puede apuntar a otra: la columna user_id de un hábito dice «este hábito pertenece a este usuario». Es lo que se llama una relación, y es ella la que vincula limpiamente los datos a las cuentas del capítulo 6.

El modelo de Tom es minimalista: una tabla habits (una fila por hábito, con su nombre y el user_id de su propietario) y una tabla checks (una fila por día marcado, vinculada a su hábito). Dos tablas, cuatro columnas útiles cada una — resiste a la tentación de prever diez «para más adelante». No necesitas escribir lo que sigue tú mismo, pero debes saber leerlo: es el lenguaje SQL que la IA generará para crear las tablas.

sql
create table habits (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users not null,
  name text not null,
  created_at timestamptz default now()
);

create table checks (
  id uuid primary key default gen_random_uuid(),
  habit_id uuid references habits not null,
  day date not null
);

Léelo como un casi-español: «crea una tabla habits; cada fila tiene un id único, pertenece a un usuario, lleva un nombre, y guarda su fecha de creación». La línea references auth.users es la relación: imposible crear un hábito huérfano, sin propietario. Pide siempre a la IA que te explique el esquema que propone — es en esas lecturas comentadas donde más aprendes.

flowchart LR
  T["Teléfono de Lina"] -->|"Marca un hábito"| B["Base de datos Supabase"]
  O["Ordenador de la biblioteca"] -->|"Misma cuenta"| B
  B -->|"Devuelve los mismos datos"| T
  B -->|"Devuelve los mismos datos"| O
Un solo lugar de verdad: todos los dispositivos de una misma cuenta leen y escriben en la misma base.

La seguridad primero: cada uno ve sus datos

Pregunta crucial antes de escribir el más mínimo dato: ¿qué impide a la app de Lina leer los hábitos de Karim? Respuesta: nada, por defecto. Te toca a ti exigirlo, mediante lo que Supabase llama la Row Level Security (RLS) — la «seguridad a nivel de fila». El principio es límpido: cada tabla recibe una regla del tipo «un usuario solo puede leer y modificar las filas cuyo user_id es el suyo». La propia base rechaza todo lo demás, haga lo que haga el código de la app.

Sin RLS activada, cualquier usuario conectado puede técnicamente leer los datos de todos los demás — bastan unas manipulaciones en la consola del navegador. Exige siempre, palabra por palabra, en tu prompt: «activa la Row Level Security con una regla por tabla: cada usuario solo accede a sus propias filas». Es innegociable.

Migrar sin perder nada

Tus usuarios existentes tienen datos en su localStorage: tirarlos sería hacerles pagar tu salto de calidad. El plan de migración es clásico y elegante: en la primera conexión de un usuario, la app mira si quedan datos locales, y le propone importarlos a su cuenta. Tras una importación exitosa, el localStorage se limpia, y la base se convierte en la única fuente de verdad. Aquí tienes el prompt completo de Tom — fíjate en que él mismo trocea la obra en etapas:

PROMPT
Haz evolucionar mi app de seguimiento de hábitos del localStorage a Supabase.
Contexto: la autenticación por enlace mágico ya funciona (capítulo anterior).
Etapas que quiero, en este orden:
1. crea las tablas habits y checks, vinculadas al usuario conectado vía user_id
2. activa la Row Level Security con una regla por tabla: cada usuario solo lee y modifica sus propias filas
3. haz que la app lea y escriba en la base en lugar del localStorage: añadir, eliminar, marcar, cálculo de la racha
4. en la primera conexión de un usuario que todavía tenga datos locales, proponle importarlos a su cuenta, y luego limpia el localStorage
Haz UNA etapa a la vez y espera a que confirme mi prueba antes de pasar a la siguiente.
En cada etapa, explícame en dos frases lo que has hecho.

Este prompt condensa todo lo que has aprendido: la visión completa anunciada de entrada (capítulo 3), el troceado en etapas comprobables (capítulo 4), la seguridad exigida explícitamente, y la petición de explicación que transforma cada etapa en lección. Cuando la IA termine la etapa 1, ve a ver tus tablas en el panel de Supabase (pestaña «Table Editor»): ver tus datos «de verdad», en la interfaz del servicio, hace todo el concepto concreto.

Probar la persistencia de verdad

La migración toca el corazón de tu app: la prueba debe estar a la altura. No te conformes con «parece que funciona» — despliega un escenario completo, con varias cuentas y varios dispositivos. Aquí tienes la checklist de Tom:

  • Persistencia: marca un hábito, recarga la página, cierra el navegador, vuelve — la marca sigue ahí.
  • Multidispositivo: marca en tu teléfono, recarga en el ordenador — la marca aparece.
  • Aislamiento: crea dos cuentas de prueba, añade hábitos diferentes en cada una, y verifica que ninguna ve los datos de la otra.
  • Migración: en un navegador que tenga datos locales antiguos, conéctate y verifica que se propone la importación, y luego que todo ha llegado a la cuenta.
  • Desconexión: desconectado, la app no debe mostrar ningún dato.

Para la prueba de aislamiento — la más importante — hazte ayudar activamente. La IA es excelente generando escenarios de prueba más astutos que los tuyos:

PROMPT
He creado dos cuentas de prueba: alumno1@test.es y alumno2@test.es.
Dame un escenario de prueba paso a paso para verificar que:
- cada cuenta solo ve sus propios hábitos y marcas
- una marca hecha en un dispositivo aparece en otro dispositivo de la misma cuenta tras recargar
- un usuario desconectado no ve ningún dato
Explícame después cómo verificar directamente en el panel de Supabase que cada fila de habits lleva el user_id correcto, y cómo probar que la Row Level Security bloquea de verdad un acceso cruzado.
Acostúmbrate a abrir el panel de Supabase en paralelo a tus pruebas: ves aparecer las filas en directo cuando marcas en la app. Es la mejor manera de comprender lo que pasa «detrás» — y de detectar inmediatamente una fila con un user_id ausente o falso.

Latencia y estados de carga

Un cambio sutil acompaña a la migración: tus datos ya no son instantáneos. Con localStorage, todo estaba en el sitio; con una base en línea, cada lectura y cada escritura hace un viaje de ida y vuelta de red de unas decenas o centenas de milisegundos. La mayor parte del tiempo, es imperceptible — pero en una conexión lenta, una lista que tarda un segundo en mostrarse sin ningún indicador da la impresión de una app rota. Pide a la IA que añada estados de carga («un pequeño indicador mientras los hábitos se cargan») y un mensaje claro si la red falla («imposible contactar con el servidor, vuelve a intentarlo»).

Es también el momento de aprender un reflejo de pro: probar tu app simulando una mala red. En las herramientas de desarrollo del navegador (F12), la pestaña «Network» permite limitar la conexión («Slow 3G»). Treinta segundos de prueba en esas condiciones te muestran exactamente lo que vivirán tus usuarios peor conectados — y lo que hay que mejorar.

El plan gratuito de Supabase cubre con mucho una app como la de Tom: 500 MB de base de datos son años de marcas de hábitos. No te preocupes por los límites antes de tener cientos de usuarios activos — y ese día, será un buen problema que tener.

Lo que Tom acaba de ganar

Mide el camino: la app de Tom tiene ahora cuentas, datos centralizados, asegurados fila a fila, y sincronizados entre todos los dispositivos. Lina marca en la biblioteca, verifica su racha en el autobús, y todo es coherente. Pero hay una ganancia más discreta: Tom dispone ahora de un verdadero backend. Todas las funcionalidades «imposibles» de antes — enviar emails, aceptar un pago, conectar IA — se vuelven accesibles, porque por fin tienen un sitio donde apoyarse. Ese es exactamente el programa del próximo capítulo.

🛠️ Te toca a ti

Contexto

Tom dedica su fin de semana a la migración: es la obra más estructurante desde el inicio del proyecto, y quiere hacerla limpiamente — tablas, seguridad, migración de los datos existentes, pruebas multicuenta. Sigue el mismo camino en tu app, etapa por etapa, sin avanzar nunca antes de haber validado la etapa en curso. Al final, tus datos te seguirán a todas partes.

Instrucciones

  1. Describe a la IA tu modelo de datos en tu idioma («una tabla para…, una tabla para…») y pídele el esquema SQL comentado.
  2. Envía el prompt de migración exigiendo explícitamente la Row Level Security, una etapa a la vez.
  3. Tras la etapa de las tablas, abre el panel de Supabase y verifica su estructura en el Table Editor.
  4. Despliega la checklist completa: persistencia, multidispositivo, aislamiento entre dos cuentas de prueba, migración de los datos locales.
  5. Pide añadir estados de carga y un mensaje de error de red, y prueba en modo «Slow 3G» vía F12.
  6. Commit final: «migración base de datos completa y probada» — es un hito mayor de tu proyecto.
Pista — Si la app muestra una lista vacía tras la migración cuando la base sí contiene filas, es casi siempre la RLS la que bloquea (regla ausente o mal escrita). Da a la IA el mensaje de error de la consola Y tu regla RLS actual: el diagnóstico es inmediato.

En resumen

  • Una base de datos en línea es el lugar central donde todos los dispositivos leen y escriben los mismos datos.
  • Tablas, columnas, filas: una base se parece a una hoja de cálculo, con identificadores únicos y relaciones.
  • La columna user_id vincula cada dato a su propietario — es el puente con la autenticación del capítulo 6.
  • La Row Level Security es innegociable: sin ella, cada usuario puede técnicamente leer los datos de los demás.
  • Migra sin pérdidas: propón la importación de los datos locales en la primera conexión, y limpia después el localStorage.
  • Prueba en serio: persistencia, multidispositivo, aislamiento entre cuentas, y comportamiento en red lenta.
  • Con un verdadero backend, las funcionalidades avanzadas (emails, pago, IA) por fin se vuelven accesibles.

Quiz — comprueba tu comprensión

1. ¿Qué problema resuelve la base de datos, que la autenticación sola no resolvía?

La autenticación dice QUIÉN está ahí; la base de datos almacena QUÉ en el mismo sitio para todos los dispositivos. Las dos juntas dan la sincronización que Lina esperaba.

2. ¿Para qué sirve la columna user_id en la tabla habits?

user_id apunta al usuario propietario: es la relación que permite mostrar a cada uno sus hábitos, y a la RLS bloquear a los demás.

3. ¿Qué pasa si olvidas activar la Row Level Security?

Por defecto, nada aísla las filas entre usuarios. La RLS hace que la propia base aplique la regla «cada uno sus filas» — exígela palabra por palabra en tu prompt.

4. ¿Cómo migrar los datos de localStorage de los usuarios existentes sin perderlos?

La importación en la primera conexión preserva el historial de cada uno: la base se convierte después en la única fuente de verdad. Tus usuarios no pagan tu salto de calidad.

5. Tras la migración, la lista se muestra vacía aunque la base sí contiene filas. ¿Cuál es la causa más probable?

Es el síntoma clásico: la base rechaza la lectura porque la regla RLS no reconoce al usuario. Consola + regla RLS enviadas a la IA = diagnóstico inmediato.

Auteur(s)

R

REHOUMA Haythem

Haythem Rehouma est un ingénieur et architecte IA et cloud, formateur et enseignant technique, avec un profil orienté IA médicale, AWS, MLOps, LLM/RAG et vision par ordinateur.