This recipe produces a complete PostgreSQL database schema tailored to your business model — SaaS, marketplace, or ecommerce — with migration files, Row Level Security policies, indexes, and a schema evolution strategy. [src1]
npm install -g supabasecreated_at and updated_at to every table. [src4]Which schema pattern?
├── SaaS (B2B multi-tenant)
│ └── SCHEMA A: Shared schema with tenant_id + RLS
├── SaaS (B2C single-tenant)
│ └── SCHEMA B: User-scoped tables with user_id + RLS
├── Marketplace (two-sided)
│ └── SCHEMA C: Vendors + buyers + listings + transactions
├── Ecommerce (single vendor)
│ └── SCHEMA D: Products + carts + orders + payments
└── Internal tool
└── SCHEMA E: Users + core entity + audit log
| Schema | Tables | Complexity | Multi-tenancy | Best For |
|---|---|---|---|---|
| A: B2B SaaS | 8-12 | Medium | tenant_id on all tables | Team-based products |
| B: B2C SaaS | 5-8 | Low | user_id scoping | Consumer apps |
| C: Marketplace | 10-15 | High | vendor_id + buyer_id | Two-sided platforms |
| D: Ecommerce | 8-12 | Medium | None | Online stores |
| E: Internal tool | 4-6 | Low | Optional | Admin panels |
Duration: 5 minutes · Tool: Supabase CLI
Run supabase init and supabase start to create local development environment with migrations directory.
Verify: supabase status shows running instance. · If failed: Ensure Docker is running.
Duration: 15-25 minutes · Tool: SQL Editor
Create tables matching your business model pattern. B2B SaaS uses organizations + profiles + items with tenant_id scoping. Marketplace adds vendors + listings + orders + reviews. Ecommerce uses products + carts + orders + order_items. [src2] [src5]
Verify: All tables created without errors. · If failed: Check SQL syntax and auth.users reference.
Duration: 10 minutes · Tool: SQL Editor
Enable RLS on all user-facing tables. Create policies for SELECT, INSERT, UPDATE, DELETE scoped to user/organization membership. [src1]
Verify: Test with two users — user A cannot see user B's data. · If failed: Check RLS is enabled and policies reference auth.uid() correctly.
Duration: 10 minutes · Tool: SQL Editor
Add auto-profile creation on signup, updated_at triggers, and business logic functions (inventory management, etc.).
Verify: Create test user, confirm profile auto-created. · If failed: Check trigger uses SECURITY DEFINER to bypass RLS.
Duration: 5 minutes · Tool: Supabase CLI
Run supabase db reset to apply all migrations. Add seed data in supabase/seed.sql. Push to remote with supabase db push. [src6]
Verify: Reset completes without errors; all tables exist in Studio. · If failed: Run migrations individually to isolate failing statement.
Duration: 5 minutes · Tool: Git + Supabase CLI
Establish the change-diff-test-commit-push workflow. Use supabase db diff to generate migrations from local changes. Commit migration files to Git. Deploy with supabase db push. [src1]
Verify: supabase db reset applies all migrations cleanly. · If failed: Use supabase migration repair to fix state.
{
"output_type": "database_schema",
"format": "SQL migration files",
"columns": [
{"name": "migration_files", "type": "string", "description": "Ordered SQL files in supabase/migrations/", "required": true},
{"name": "table_count", "type": "number", "description": "Number of tables created", "required": true},
{"name": "rls_policies", "type": "number", "description": "Number of RLS policies", "required": true},
{"name": "indexes", "type": "number", "description": "Number of indexes", "required": true},
{"name": "business_model", "type": "string", "description": "Schema pattern used", "required": true}
],
"expected_row_count": "3-6 migration files",
"sort_order": "chronological",
"deduplication_key": "migration_files"
}
| Quality Metric | Minimum Acceptable | Good | Excellent |
|---|---|---|---|
| RLS coverage | All user-facing tables | + service role bypass documented | + policy unit tests |
| Foreign key integrity | All relationships have FK | + ON DELETE actions defined | + deferred constraints |
| Index coverage | PKs + FKs indexed | + frequently filtered columns | + composite indexes |
| Migration reversibility | Can reset from scratch | + down migrations | + tested rollback |
| Naming consistency | snake_case throughout | + prefix conventions | + documented naming guide |
If below minimum: Review each table for missing RLS. Add indexes on WHERE-clause columns. Test with supabase db reset.
| Error | Likely Cause | Recovery Action |
|---|---|---|
relation "auth.users" does not exist | Running outside Supabase | Use Supabase local dev or create users table manually |
permission denied for table | RLS enabled, no matching policy | Add policy for current user role |
duplicate key value violates unique constraint | Seed data conflicts | Use ON CONFLICT DO NOTHING |
supabase db push conflict | Remote has unsynced changes | Run supabase db pull first |
| Slow queries after launch | Missing indexes | Run EXPLAIN ANALYZE, add indexes |
| Component | Free Tier | Pro ($25/mo) | Scale |
|---|---|---|---|
| Supabase database | 500 MB, 2 projects | 8 GB, unlimited | Custom |
| Local dev (CLI + Docker) | Free | Free | Free |
| Migration tooling | Free | Free | Free |
| Total | $0 | $25/mo | $75+/mo |
Deploying without RLS means the public anon key can access any table data — the most common Supabase security vulnerability. [src1]
Write policies before application code.
Sequential IDs expose data volume and enable enumeration attacks. [src2]
gen_random_uuid() produces globally unique, non-sequential identifiers.
Floating-point arithmetic causes rounding errors in financial calculations. [src7]
Use INTEGER for price_cents. Format for display only at the presentation layer.
Use this recipe when a developer needs to design the database schema for an MVP, choosing the right pattern for their business model. Requires a tech stack decision and project scaffold. Handles schema design, RLS, migrations, and evolution — not application code.