The friendly wrapper saves weeks. The Postgres underneath means the design decisions are permanent. This page is how we make those decisions — schemas we won't regret, RLS policies that actually hold, and the small list of things we deliberately don't do in Supabase.
Bigger, uglier, but shareable, unguessable, and safe to generate on the client. Serial keys leak business volume.
Non-negotiable. `updated_at` is a trigger, not an app-layer concern.
Otherwise it's a foot-gun. Every query needs `WHERE deleted_at IS NULL` — one miss leaks a deleted user.
Ask any fintech engineer why. Or don't and find out expensively later.
Postgres enums are painful to alter. Lookup tables cost one join and give you a UI-editable list.
Never let the default happen. CASCADE, SET NULL, RESTRICT — pick, document, done.
"RLS handles security so I can call `select *` from the client." → Now every table you forget to enable RLS on is a public data leak. And the service-role key you sprinkled into a Next.js API route bypasses everything.
"RLS is a defence-in-depth layer. My API still authenticates and authorises. RLS is the seatbelt when the API has a bug." → Every table has RLS on by default. Policies are readable in one sentence: "a user can read a row where user_id = auth.uid()".
-- The four policies every user-facing table needs
alter table invoices enable row level security;
create policy "own_read" on invoices for select using (user_id = auth.uid());
create policy "own_insert" on invoices for insert with check (user_id = auth.uid());
create policy "own_update" on invoices for update using (user_id = auth.uid());
create policy "own_delete" on invoices for delete using (user_id = auth.uid());
-- Roles never stored on profiles. Separate table + security-definer function.
-- See our user-roles pattern for the full setup.Edge Functions time out. For anything > 30s (video processing, LLM batch, exports), we run a dedicated worker on Fly / Railway with pg-boss reading a job queue in Postgres.
Long-running aggregates hurt live query performance. We stream to a read replica or a warehouse (BigQuery / DuckDB) and query there.
Contact forms, waitlists — spam gets ugly. We front them with Cloudflare Turnstile + rate limits before they touch Supabase.
Supabase saves you 3–6 weeks of auth, RLS wiring, storage, and realtime plumbing. For 90% of SaaS starts, that time is worth more than the abstraction it costs. Roll your own when compliance forbids managed services or when you need Postgres extensions Supabase doesn't allow.
Tell us your goals — we'll recommend the right technology honestly, even if it isn't this one.