Supabase: Postgres, Auth e a regra do RLS
A espinha dorsal de dados do ClubPetro. Se o Claude é o cérebro, o Supabase é a memória — e tem uma regra de segurança sem exceção.
O que é
PostgreSQL gerenciado com bateria inclusa: banco, Auth, API automática (PostgREST), Realtime, Storage e funções. A gente usa principalmente banco, auth e storage.
Onde usamos
- Banco — 50+ tabelas, acessadas via Drizzle ORM (TypeScript tipado ponta a ponta).
- Auth — login do time com Google OAuth.
- Storage/Realtime — arquivos e telas ao vivo, quando faz sentido.
Conceitos-chave
- Connection pooling: há o transaction pooler (porta 6543, ideal pra serverless/edge, exige
prepare:false) e o session pooler (5432). Em serverless, conexão direta esgota o banco — use o pooler. - RLS (Row Level Security): políticas SQL que filtram linhas por usuário.
auth.uid()identifica quem chama; aservice role keyignora RLS (só no servidor). - Drizzle: schema em TS, queries tipadas, e o compilador pega erro antes do runtime.
Boas práticas
- RLS em toda tabela, no mesmo migration que a cria. Policy explícita pra tabela exposta; deny-all (RLS sem policy) pra tabela interna.
- service role key só no servidor, nunca no client.
- Migrations versionadas e revisadas como código.
- Backups e point-in-time recovery — saiba qual é o seu antes de precisar.
- Use
selectcom colunas explícitas (não*) em caminho quente.
Pegadinhas (leia com atenção)
- ⚠️ Sem RLS = vazamento. A API expõe qualquer tabela sem Row Level Security pra quem tem a chave pública. Inegociável.
- ⚠️ Porta 5432 dá SASL_SIGNATURE_MISMATCH no nosso setup — use 6543 +
prepare:false. - ⚠️ Migrations são SQL à mão (
packages/db/drizzle/NNNN_*.sql); o_journal.jsonestá desatualizado, entãodb:migratenão é o mecanismo. - ⚠️ Drift DB ↔ Drizzle: o schema TS nem sempre reflete o banco. Cheque o banco real antes de criar dado. Jamais
db:pushem produção. - ⚠️ Realtime e Storage respeitam (ou deveriam) RLS/políticas — bucket "público só pra testar" é o RLS esquecido da camada de arquivo.
Quando usar (e quando não)
Default pra qualquer dado relacional da plataforma. Não o use como fila de alta vazão nem pra dado que é naturalmente de documento/realtime mobile (aí veja Firebase, #11).
Supabase guarda a verdade; o RLS é o cadeado. Tabela nova nasce com o cadeado fechado — ou não nasce.
Próximo da série: #04 — Trigger.dev.