Wann du es brauchst
Das Produkt braucht eigene Daten, die einem Nutzer gehören, zum Beispiel Notizen, Projekte oder gespeicherte Einträge.
Regeln
- Migrationen sind append-only: Lege eine neue Datei in
supabase/migrations/an und ändere nie eine bereits angewendete. - RLS ist die Autorisierungsgrenze. Jede neue Tabelle in
publicaktiviert Row Level Security in derselben Migration, die sie anlegt. - Der Eigentümer kommt aus der Sitzung (
auth.uid()), nie aus dem Request-Body. - Servercode nutzt den Client pro Request aus
src/lib/supabase/server.ts, der als angemeldeter Nutzer arbeitet.SUPABASE_SERVICE_ROLE_KEYumgeht RLS: Verwende ihn nicht für Nutzerdaten und importiere ihn nie in Client-Code.
Schritte
Lege
supabase/migrations/<timestamp>_<name>.sqlmit einem Zeitstempel nach der letzten Migration an. Für eine Tabellenotes: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;Schreibe nur die Policies, die das Produkt braucht: Eine Tabelle, die Nutzer lesen, aber nie ändern, hat nur eine
select-Policy. Schreibe(select auth.uid())wie inprofiles, damit Postgres den Ausdruck einmal pro Abfrage statt einmal pro Zeile auswertet.Wenn Nutzer bestimmte Spalten nicht ändern dürfen, folge
profiles:revoke updateauf die Tabelle, danngrant update (<spalten>)nur auf die änderbaren.Lies und schreibe aus einer Server Component oder einem Route Handler mit
createClient()aussrc/lib/supabase/server.ts. Rufe wie insrc/app/api/me/route.tszuerstauth.getUser()auf und antworte ohne Nutzer mit 401. Filtere nicht nach einerowner_idaus dem Request: RLS begrenzt die Zeilen bereits.Wende die Migration an (
supabase db pushoder der SQL-Editor des Projekts) und prüfe die Isolation mit zwei Konten: Nutzer A legt eine Zeile an, Nutzer B darf sie weder sehen noch ändern noch löschen, und eine anonyme Anfrage erhält keine Zeilen.Lege die Validierung der Eingaben in ein reines Modul unter
src/lib/und teste es intest/mit Vitest; die Tests nutzen kein Netzwerk.Beschreibe die Tabelle und ihre Regeln in
DATABASE.md.
Fertig, wenn
- die Prüfbefehle erfolgreich sind;
- die neue Tabelle RLS aktiviert hat und eine Policy pro erlaubter Operation;
- die Prüfung mit zwei Konten aus Schritt 5 erfolgreich war;
DATABASE.mddie Tabelle beschreibt.
