Next.js en Supabase, in productie
Next.js op de App Router met Supabase erachter: Postgres dat standaard alles weigert, toegang over een grens via security-definer-functies, een SQL-testsuite die aantoont dat de policies kloppen, en uitrollen op Vercel.
Deze combinatie is makkelijk om mee te beginnen en makkelijk om precies op één punt fout te doen: de database is bereikbaar vanuit de browser. Supabase geeft de client een URL en een publieke sleutel, en vanaf dat moment staat alleen row-level security tussen een nieuwsgierige bezoeker en uw tabellen. Het meeste werk aan een Supabase-project is daarom databasewerk, en dat werk laat zich niet uitstellen tot na de schermen: elke query die er dan al is, is op de oude aannames geschreven.
Standaard weigeren
Geen tabel is leesbaar tot een policy dat toestaat. Zo begint het project, en dat betekent dat een tabel die er een half jaar later bij komt onzichtbaar is in plaats van openbaar. De fout wordt dan een ontbrekende functie, die binnen een dag gemeld wordt, in plaats van een datalek, dat niemand meldt.
Toegang die terecht een grens moet oversteken wordt niet geregeld door een policy ruimer te maken. Die loopt via een security-definer-functie met een smalle signatuur, zodat de uitzondering één object met een naam is dat te beoordelen valt, in plaats van een verruimde regel die stilletjes overal elders ook geldt.
Aantonen in plaats van doorlezen
Een securitymodel dat alleen gelezen is, is niet geverifieerd. De policies worden gedekt door een SQL-testsuite die tegen een wegwerp-Postgres draait en per rol vastlegt wat wel en niet zichtbaar is. Een per ongeluk verruimde policy loopt daar dus tegenaan en valt vandaag om als test, in plaats van over een half jaar in een logregel op te duiken.
De applicatiekant
Next.js op de App Router: server components voor alles wat data leest, zodat de query op de server blijft en de sessie niet verder reist dan nodig, en client components alleen waar er echte interactie is. Authenticatie loopt via Supabase en de sessie wordt op de server gelezen in plaats van vanuit de browser geloofd.
De types komen uit de database in plaats van met de hand geschreven te worden, zodat een hernoemde kolom in de editor een typefout oplevert en niet tijdens het draaien een lege waarde. De gegenereerde types staan in de repository en worden opnieuw gegenereerd zodra het schema verandert.
Draaien
- Uitrollen op Vercel vanaf main, met een preview-URL per branch.
- Alleen migraties. Een toegepaste migratie wordt nooit aangepast; een correctie is een nieuwe migratie.
- Stripe voor betalingen en Resend voor transactionele e-mail, waar een project dat nodig heeft.
- Cloudflare voor DNS en e-mailrouting.
Een finance-contentplatform dat zo gebouwd is, draait in productie en staat hieronder gelinkt.
Veelgestelde vragen
- Is het veilig om Supabase aan de browser bloot te stellen?
- Ja als row-level security zijn werk doet, en anders niet. De anon-sleutel is bewust openbaar; de policies op elke tabel bepalen wat een verzoek te zien krijgt. Daarom staat het project op deny-by-default en zit er een testsuite om de policies heen.
- Waar is een security-definer-functie voor?
- Voor het geval dat een query terecht meer moet zien dan de aanroeper. De functie draait met de rechten van haar eigenaar en heeft een smalle signatuur, zodat de uitzondering één object met een naam is dat te beoordelen valt, in plaats van een policy die voor iedereen ruimer wordt.
- Kunt u een bestaand project naar Next.js en Supabase migreren?
- Meestal wel, en de volgorde is bepalend: eerst schema en policies met tests eromheen, dan de applicatie, dan de deploypipeline. Eerst de schermen verplaatsen en de toegangsregels later doen is precies waar deze migraties op uitlopen.
- Welke versie van Next.js en welk rendermodel?
- Next.js 16 op de App Router, met server components als uitgangspunt en client components alleen waar er echte interactie is. Lezen gebeurt op de server, zodat sleutels daar blijven.