Supabase is PostgreSQL as a service with auth, storage, and realtime. But connecting from Next.js requires 3 distinct patterns based on execution context. Mixing up clients is a classic source of bugs and security leaks.
1. The three Supabase clients
In a Next.js SaaS, you need three clients:
- Browser client (
createBrowserClient) — for client components. Uses session cookies. - Server client (
createServerClient) — for server components, server actions, and route handlers. Reads/writes cookies via Next.js. - Service client — bypasses RLS, used for admin operations and public caches. Never in a user-exposed route without explicit checks.
2. Configure environment variables
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=...
SUPABASE_SECRET_KEY=... # SERVER ONLYThe secret key must NEVER appear client-side. Without the NEXT_PUBLIC_ prefix, Next.js keeps it server-only.
3. Enable RLS on every table
By default, create your tables with RLS enabled:
CREATE TABLE projects (...);
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;Then add membership-based policies. Without RLS, any authenticated user can read all rows.
4. Generate TypeScript types
Use the Supabase CLI to generate types from your schema:
supabase gen types typescript --project-id=YOUR_ID > types/database.tsImport these types in your clients to get autocomplete everywhere.
5. Server-side validation with getUser()
To identify a user server-side, ALWAYS use supabase.auth.getUser(), never getSession(). Sessions can be forged by a malicious client; getUser() validates the token with Supabase.
Skip 3 days of Supabase + RLS + types setup. Boilerplate-Stack includes the three clients, RLS helpers, generated types, and all optimized queries (N+1 prevention, targeted .select()).
Conclusion
Supabase is powerful but demands strict discipline on the Next.js side. Once configured properly, it's a game changer. Boilerplate-Stack hands you that configuration ready to use.