I've shipped multiple production platforms on Next.js + Supabase — a hiring platform, a Skool-style community, marketing sites. For a small team that needs auth, a real database, and storage without managing infrastructure, it's hard to beat. But it has sharp edges.
Row Level Security is your backend
The biggest mental shift: with Supabase, the client talks to the database. Your security model is RLS policies, not API middleware. Write them first, not last:
create policy "Members read their community's posts" on posts for select using ( community_id in ( select community_id from memberships where user_id = auth.uid() ) );
Test policies with different users early. A missing policy fails closed (annoying); a wrong one fails open (a breach).
Server Components changed the calculus
With the App Router, most data fetching moves into server components using the service client — RLS still applies via the user's session. The client-side Supabase SDK is now mostly for realtime subscriptions and optimistic mutations. Less JavaScript shipped, fewer loading spinners.
The patterns that kept us sane
- Database functions for multi-step writes. Creating a job posting + notifying followers + updating counts belongs in one
rpc()call, not three client round-trips that can half-succeed. - Generated types.
supabase gen types typescripton every migration. Untyped database calls are how production bugs are born. - Storage policies mirror table policies. A private community's images need the same access rules as its posts — easy to forget.
Where it strains
Heavy background jobs, complex search, and multi-tenant analytics will eventually want dedicated tools (queues, search engines, warehouses). That's fine. The stack's job is to get you to the point where those problems are real — with paying users — instead of imagined.
Boring, fast, and cheap to operate. For client work and early products, that's the whole brief.