Quando usarlo
Il prodotto ha bisogno di dati propri che appartengono a un utente, per esempio note, progetti o elementi salvati.
Regole da rispettare
- Le migration sono append-only: crea un nuovo file in
supabase/migrations/, non modificarne mai una già applicata. - La RLS è il confine di autorizzazione. Ogni nuova tabella in
publicabilita la row level security nella stessa migration che la crea. - Il proprietario arriva dalla sessione (
auth.uid()), mai dal corpo della richiesta. - Il codice server usa il client per richiesta di
src/lib/supabase/server.ts, che agisce come l'utente autenticato.SUPABASE_SERVICE_ROLE_KEYscavalca la RLS: non usarla per i dati degli utenti e non importarla mai nel codice client.
Passi
Crea
supabase/migrations/<timestamp>_<nome>.sqlcon un timestamp successivo all'ultima migration. Per una tabellanotes:create table public.notes ( id uuid primary key default gen_random_uuid(), owner_id uuid not null default auth.uid() references auth.users(id) on delete cascade, body text not null check (char_length(body) between 1 and 2000), created_at timestamptz not null default now() ); create index notes_owner_id_idx on public.notes (owner_id); alter table public.notes enable row level security; create policy "notes_select_own" on public.notes for select to authenticated using ((select auth.uid()) = owner_id); create policy "notes_insert_own" on public.notes for insert to authenticated with check ((select auth.uid()) = owner_id); create policy "notes_update_own" on public.notes for update to authenticated using ((select auth.uid()) = owner_id) with check ((select auth.uid()) = owner_id); create policy "notes_delete_own" on public.notes for delete to authenticated using ((select auth.uid()) = owner_id); revoke all on public.notes from anon;Scrivi solo le policy che servono al prodotto: una tabella che gli utenti leggono ma non modificano ha solo una policy
select. Scrivi(select auth.uid())come inprofiles, così Postgres lo valuta una volta per query e non una volta per riga.Se gli utenti non devono modificare alcune colonne, segui
profiles:revoke updatesulla tabella, poigrant update (<colonne>)solo su quelle modificabili.Leggi e scrivi da un Server Component o da un Route Handler con
createClient()disrc/lib/supabase/server.ts. Come insrc/app/api/me/route.ts, chiama primaauth.getUser()e rispondi 401 se l'utente manca. Non filtrare su unowner_idpreso dalla richiesta: la RLS limita già le righe.Applica la migration (
supabase db pushoppure l'editor SQL del progetto) e verifica l'isolamento con due account: l'utente A crea una riga, l'utente B non deve vederla, modificarla o cancellarla, e una richiesta anonima non riceve righe.Metti la validazione dell'input in un modulo puro sotto
src/lib/e testala intest/con Vitest; i test non usano la rete.Descrivi la tabella e le sue regole in
DATABASE.md.
Completato quando
- i comandi di verifica passano;
- la nuova tabella ha la RLS abilitata e una policy per ogni operazione consentita;
- la verifica con due account del passo 5 è riuscita;
DATABASE.mddescrive la tabella.
