Generate Supabase project with auth, RLS policies, edge functions, type generation. Use when user asks to "scaffold a Supabase project".
Generate production-ready Supabase project with PostgreSQL schema, Row Level Security policies, auth configuration, type-safe client, Edge Functions, and local development setup.
"Row Level Security is not optional — it is the authorization layer. Every table without RLS is a data breach waiting to happen. Enable RLS first, open access second."
organization_id, or fully isolated schemas).supabase init → creates supabase/ directory.supabase start → local Postgres + Auth + Storage + Edge Functions + Studio at localhost:54323.supabase/migrations/0001_initial.sql — initial schema with all tables.supabase/seed.sql with test data for local development.supabase db reset runs migrations + seed from scratch.supabase/.env.local → never commit. Add to .gitignore.ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;FOR ALL USING (true) policies — this is equivalent to disabling RLS.anon, authenticated, service_role):
SELECT: what rows can this role read?INSERT: what can they create? (include WITH CHECK clause)UPDATE: what can they modify? (include both USING and WITH CHECK)DELETE: what can they delete?auth.uid() for user-scoped access: USING (user_id = auth.uid()).auth.jwt() ->> 'organization_id' or a helper function.supabase db test.supabase gen types typescript --local > src/types/supabase.ts.package.json scripts: "db:types": "supabase gen types typescript --local > src/types/supabase.ts".createClient<Database>(url, key) using generated Database type.service_role key in the browser client — use the anon key only.src/lib/supabase.ts (browser client) and src/lib/supabase-server.ts (server client with auth cookie handling).signIn, signOut, onAuthStateChange, getSession wrapped in consistent API.supabase functions new <name>.supabase db push for schema migrations, supabase functions deploy for Edge Functions.| Decision | Default | Alternative | When to Switch |
|---|---|---|---|
| Multi-tenancy | organization_id on each table | Separate Supabase projects | Full data isolation requirement |
| Auth provider | Email + Google OAuth | Clerk, Auth.js | Enterprise SSO (SAML) needed |
| File storage | Supabase Storage | S3 + CloudFront | > 100GB files; CDN edge needed |
| Realtime | Supabase Realtime (Broadcast) | Pusher, Ably | Very high concurrency (>10k connections) |
| RLS policy complexity | Simple auth.uid() check | Custom PostgreSQL functions | Complex org/role hierarchy |
| Edge Functions | Supabase Deno | AWS Lambda, Cloudflare Workers | Need npm packages not in Deno |
| TypeScript client | @supabase/supabase-js v2 | Direct PostgreSQL via Drizzle | Complex queries needing full SQL |
| Local development | supabase start (Docker) | Remote dev project | Low RAM machine (<8GB RAM) |
| Schema migrations | Supabase CLI migrations | Prisma Migrate | Team already on Prisma |
| Cron jobs | pg_cron (Supabase) | External cron + API calls | Frequency < daily |
anon role can only read public data — no writes without authentication.service_role key referenced in any frontend code.supabase db reset completes without errors (migrations + seed are idempotent).supabase/functions/.env.service_role key in browser — The service_role key bypasses RLS completely. Exposing it means any user can read/delete any row in the database. Use only in server-side contexts.ALTER TABLE, hand-written types stay wrong silently. Always run supabase gen types.eq('user_id', userId) as security — Client-side filtering is not security. If RLS isn't set, a malicious user can remove the .eq() filter. RLS must enforce access, not just the client query.bytea columns. Postgres is not an object store.