Base de dados: os teus dados em todo o lado
Objetivos deste capítulo
- Compreender o que é uma base de dados e como se organiza
- Migrar a tua app do localStorage para uma verdadeira base online
- Garantir que cada utilizador só vê os seus próprios dados
A peça em falta
Os alunos do Tom ligam-se agora com o email — mas a Lina, fiel ao posto, assinala que os hábitos da biblioteca continuam a não estar no telemóvel dela. Normal: a autenticação diz quem está ali, mas os dados, esses, ainda vivem no localStorage de cada aparelho. Falta um lugar central onde arrumar os hábitos de cada conta: uma base de dados online. É o trabalho deste capítulo, e é provavelmente o mais transformador do curso — depois dele, a tua app deixa de ser um gadget de navegador para se tornar um verdadeiro serviço.
A mudança de lógica cabe numa frase: em vez de cada navegador guardar a sua própria cópia dos dados, todos os aparelhos leem e escrevem no mesmo sítio, um servidor de base de dados. A tua app torna-se um balcão: mostra o que a base diz, e regista lá cada ação. A Lina marca «Leitura» no telemóvel; a marcação parte para a base; no dia seguinte na biblioteca, a app pergunta à base «mostra-me os hábitos da Lina» e está tudo lá.
Uma base de dados, em concreto
Se já abriste uma folha de cálculo, conheces 80 % do conceito. Uma base de dados é um conjunto de tabelas; cada tabela parece uma folha de cálculo com colunas (os tipos de informação) e linhas (os registos). Cada linha possui um identificador único (id), e — é a parte mágica — uma linha pode apontar para outra: a coluna user_id de um hábito diz «este hábito pertence a este utilizador». É o que se chama uma relação, e é ela que liga limpamente os dados às contas do capítulo 6.
O modelo do Tom é minimalista: uma tabela habits (uma linha por hábito, com o nome e o user_id do dono) e uma tabela checks (uma linha por dia marcado, ligada ao seu hábito). Duas tabelas, quatro colunas úteis cada — resiste à tentação de prever dez «para mais tarde». Não precisas de escrever tu mesmo o que se segue, mas deves saber lê-lo: é a linguagem SQL que a IA gerará para criar as tabelas.
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ê-o como quase-português: «cria uma tabela habits; cada linha tem um id único, pertence a um utilizador, tem um nome, e guarda a data de criação». A linha references auth.users é a relação: impossível criar um hábito órfão, sem dono. Pede sempre à IA que te explique o esquema que propõe — é nestas leituras comentadas que mais aprendes.
flowchart LR T["Telemóvel da Lina"] -->|"Marca um hábito"| B["Base de dados Supabase"] O["Computador da biblioteca"] -->|"Mesma conta"| B B -->|"Devolve os mesmos dados"| T B -->|"Devolve os mesmos dados"| O
A segurança primeiro: cada um vê os seus dados
Pergunta crucial antes de escrever o mínimo dado: o que impede a app da Lina de ler os hábitos do Karim? Resposta: nada, por omissão. Cabe-te a ti exigi-lo, através do que o Supabase chama Row Level Security (RLS) — a «segurança ao nível das linhas». O princípio é límpido: cada tabela recebe uma regra do tipo «um utilizador só pode ler e modificar as linhas cujo user_id é o seu». A própria base recusa tudo o resto, faça o que fizer o código da app.
Migrar sem perder nada
Os teus utilizadores existentes têm dados no localStorage deles: deitá-los fora seria fazê-los pagar a tua subida de nível. O plano de migração é clássico e elegante: no primeiro login de um utilizador, a app verifica se restam dados locais, e propõe-lhe importá-los para a conta dele. Depois da importação bem-sucedida, o localStorage é limpo, e a base torna-se a única fonte de verdade. Eis o prompt completo do Tom — repara que é ele próprio que divide o trabalho em etapas:
Faz evoluir a minha app de acompanhamento de hábitos do localStorage para o Supabase. Contexto: a autenticação por link mágico já funciona (capítulo anterior). Etapas que quero, por esta ordem: 1. cria as tabelas habits e checks, ligadas ao utilizador ligado via user_id 2. ativa a Row Level Security com uma regra por tabela: cada utilizador só lê e modifica as suas próprias linhas 3. faz a app ler e escrever na base em vez do localStorage: adição, eliminação, marcação, cálculo do streak 4. no primeiro login de um utilizador que ainda tenha dados locais, propõe-lhe importá-los para a conta dele, e depois limpa o localStorage Faz UMA etapa de cada vez e espera que eu confirme o meu teste antes de passar à seguinte. A cada etapa, explica-me em duas frases o que fizeste.
Este prompt condensa tudo o que aprendeste: a visão completa anunciada à partida (capítulo 3), a divisão em etapas testáveis (capítulo 4), a segurança exigida explicitamente, e o pedido de explicação que transforma cada etapa numa lição. Quando a IA acabar a etapa 1, vai ver as tuas tabelas no painel de controlo do Supabase (separador «Table Editor»): ver os teus dados «a sério», na interface do serviço, torna todo o conceito concreto.
Testar a persistência a sério
A migração toca no coração da tua app: o teste deve estar à altura. Não te contentes com «parece que funciona» — desenrola um cenário completo, com várias contas e vários aparelhos. Eis a checklist do Tom:
- Persistência: marca um hábito, recarrega a página, fecha o navegador, volta — a marcação continua lá.
- Multi-aparelhos: marca no telemóvel, recarrega no computador — a marcação aparece.
- Isolamento: cria duas contas de teste, adiciona hábitos diferentes em cada uma, e verifica que nenhuma vê os dados da outra.
- Migração: num navegador com dados locais antigos, liga-te e verifica que a importação é proposta, e depois que tudo chegou à conta.
- Logout: desligado, a app não deve mostrar nenhum dado.
Para o teste de isolamento — o mais importante — faz-te ajudar ativamente. A IA é excelente a gerar cenários de teste mais espertos do que os teus:
Criei duas contas de teste: aluno1@teste.pt e aluno2@teste.pt. Dá-me um cenário de teste passo a passo para verificar que: - cada conta só vê os seus próprios hábitos e marcações - uma marcação feita num aparelho aparece noutro aparelho da mesma conta após recarregamento - um utilizador desligado não vê nenhum dado Explica-me depois como verificar diretamente no painel de controlo do Supabase que cada linha de habits tem o user_id certo, e como testar que a Row Level Security bloqueia mesmo um acesso cruzado.
Latência e estados de carregamento
Uma mudança subtil acompanha a migração: os teus dados já não são instantâneos. Com o localStorage, estava tudo no local; com uma base online, cada leitura e cada escrita faz uma ida e volta de rede de algumas dezenas ou centenas de milissegundos. Na maior parte das vezes, é impercetível — mas numa ligação lenta, uma lista que demora um segundo a aparecer sem nenhum indicador dá a impressão de uma app partida. Pede à IA que adicione estados de carregamento («um pequeno indicador enquanto os hábitos carregam») e uma mensagem clara se a rede falhar («impossível contactar o servidor, tenta de novo»).
É também o momento de aprender um reflexo de profissional: testar a tua app simulando uma rede má. Nas ferramentas de desenvolvimento do navegador (F12), o separador «Network» permite limitar a ligação («Slow 3G»). Trinta segundos de teste nestas condições mostram-te exatamente o que viverão os teus utilizadores com pior ligação — e o que é preciso melhorar.
O que o Tom acabou de ganhar
Mede o caminho: a app do Tom tem agora contas, dados centralizados, protegidos linha a linha, e sincronizados entre todos os aparelhos. A Lina marca na biblioteca, verifica o streak no autocarro, e tudo é coerente. Mas há um ganho mais discreto: o Tom dispõe agora de um verdadeiro backend. Todas as funcionalidades «impossíveis» de antes — enviar emails, aceitar um pagamento, ligar IA — tornam-se acessíveis, porque têm finalmente um sítio onde se apoiar. É exatamente o programa do próximo capítulo.
Contexto
O Tom dedica o fim de semana à migração: é o trabalho mais estruturante desde o início do projeto, e quer fazê-lo de forma limpa — tabelas, segurança, migração dos dados existentes, testes multi-contas. Segue o mesmo caminho na tua app, etapa a etapa, sem nunca avançar antes de validar a etapa em curso. No fim, os teus dados seguir-te-ão para todo o lado.
Instruções
- Descreve à IA o teu modelo de dados em português («uma tabela para…, uma tabela para…») e pede-lhe o esquema SQL comentado.
- Envia o prompt de migração exigindo explicitamente a Row Level Security, uma etapa de cada vez.
- Depois da etapa das tabelas, abre o painel de controlo do Supabase e verifica a estrutura delas no Table Editor.
- Desenrola a checklist completa: persistência, multi-aparelhos, isolamento entre duas contas de teste, migração dos dados locais.
- Pede a adição de estados de carregamento e de uma mensagem de erro de rede, e depois testa em modo «Slow 3G» via F12.
- Commit final: «migração da base de dados completa e testada» — é um marco importante do teu projeto.
Em resumo
- Uma base de dados online é o lugar central onde todos os aparelhos leem e escrevem os mesmos dados.
- Tabelas, colunas, linhas: uma base parece uma folha de cálculo, com identificadores únicos e relações.
- A coluna user_id liga cada dado ao seu dono — é a ponte com a autenticação do capítulo 6.
- A Row Level Security é inegociável: sem ela, cada utilizador pode tecnicamente ler os dados dos outros.
- Migra sem perdas: propõe a importação dos dados locais no primeiro login, e depois limpa o localStorage.
- Testa a sério: persistência, multi-aparelhos, isolamento entre contas, e comportamento em rede lenta.
- Com um verdadeiro backend, as funcionalidades avançadas (emails, pagamento, IA) tornam-se finalmente acessíveis.
Quiz — verifica a tua compreensão
1. Que problema resolve a base de dados, que a autenticação sozinha não resolvia?
2. Para que serve a coluna user_id na tabela habits?
3. O que acontece se te esqueceres de ativar a Row Level Security?
4. Como migrar os dados localStorage dos utilizadores existentes sem os perder?
5. Depois da migração, a lista aparece vazia quando a base contém linhas. Qual é a causa mais provável?