Base de datos: tus datos en todas partes
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.
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
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.
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:
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:
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.
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.
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.
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
- Describe a la IA tu modelo de datos en tu idioma («una tabla para…, una tabla para…») y pídele el esquema SQL comentado.
- Envía el prompt de migración exigiendo explícitamente la Row Level Security, una etapa a la vez.
- Tras la etapa de las tablas, abre el panel de Supabase y verifica su estructura en el Table Editor.
- Despliega la checklist completa: persistencia, multidispositivo, aislamiento entre dos cuentas de prueba, migración de los datos locales.
- Pide añadir estados de carga y un mensaje de error de red, y prueba en modo «Slow 3G» vía F12.
- Commit final: «migración base de datos completa y probada» — es un hito mayor de tu proyecto.
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?
2. ¿Para qué sirve la columna user_id en la tabla habits?
3. ¿Qué pasa si olvidas activar la Row Level Security?
4. ¿Cómo migrar los datos de localStorage de los usuarios existentes sin perderlos?
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?