Skip to main content
Sabo uses Supabase for authentication and database access. This page shows where the clients and types live, how dashboard pages map to tables, and patterns to read/write data safely.

Where things live

  • Server client: sabo/src/lib/supabase/server.ts (SSR/server actions; cookies wired)
  • Browser client: sabo/src/lib/supabase/client.ts (client components; limited usage)
  • Types: sabo/src/lib/types/database.ts (UserProfile, UserSubscription, PaymentHistory)

Database Schema

Sabo’s database includes four main tables with Row Level Security (RLS) enabled:

1. user_profiles

Stores user profile information, preferences, and notification settings. Schema:
Key Fields:
  • user_id - Foreign key to auth.users, unique (one profile per user)
  • username - Unique username, nullable
  • push_notifications - Values: "all", "mentions", "none"
  • updated_at - Automatically updated via trigger
RLS Policies:
  • Users can SELECT/INSERT/UPDATE/DELETE their own profile only
  • Policy checks: auth.uid() = user_id
TypeScript Interface:

2. user_subscriptions

Stores Stripe subscription data for billing management. Schema:
Key Fields:
  • user_id - One subscription per user
  • stripe_customer_id - Stripe Customer ID (e.g., cus_...)
  • stripe_subscription_id - Stripe Subscription ID (e.g., sub_...), unique
  • stripe_price_id - Stripe Price ID (e.g., price_...)
  • status - Subscription status: "active", "trialing", "past_due", "canceled", etc.
  • billing_cycle - "monthly" or "yearly"
  • cancel_at_period_end - If true, subscription cancels at period end
  • canceled_at - When cancellation was requested
  • cancel_at - Future date when subscription will actually end
RLS Policies:
  • Users can SELECT their own subscription only
  • No INSERT/UPDATE/DELETE policies (webhooks use service client to bypass RLS)
TypeScript Interface:

3. payment_history

Stores payment records for invoices and transactions. Schema:
Key Fields:
  • user_id - Foreign key to auth.users
  • stripe_subscription_id - Associated subscription (nullable for one-time payments)
  • stripe_payment_intent_id - Stripe Payment Intent ID (e.g., pi_...)
  • amount - Amount in cents (e.g., 1200 = $12.00)
  • currency - ISO currency code (e.g., "usd", "eur")
  • status - Payment status: "succeeded", "failed", "pending", etc.
  • invoice_url - Stripe-hosted invoice URL
RLS Policies:
  • Users can SELECT their own payment history only
  • No INSERT/UPDATE/DELETE policies (webhooks use service client)
TypeScript Interface:

4. stripe_products

Stores Stripe product catalog (optional, for dynamic pricing displays). Schema:
RLS Policies:
  • Anyone (authenticated) can SELECT active products
  • No INSERT/UPDATE/DELETE policies (managed via webhooks or admin)

Tables and Settings Mapping

The dashboard settings pages map to these tables:
Settings PageTableKey Fields
General Settingsuser_profileslanguage, timezone
Account Settingsuser_profilesfull_name, username, bio, website, phone, birth_date
Billing Settingsuser_subscriptionsplan_name, status, current_period_end
Billing Settingspayment_historyamount, status, invoice_url, created_at
Notification Settingsuser_profilesemail_notifications, push_notifications, communication_emails, social_emails, security_emails
See the type definitions:
Settings implementation: General/Notifications update UserProfile; Account uses supabase.auth.updateUser(); Billing reads UserSubscription/PaymentHistory and links to Stripe Portal.
  • Prefer the server client (createClient) in Server Components and server actions for reads/writes.
  • Revalidate affected paths after writes (e.g., revalidatePath('/dashboard/settings/general')).
  • Keep types centralized in database.ts, and import them where needed for safety.
  • Handle errors explicitly; return predictable shapes to the UI.

Future work (optional)

  • Migrations and type generation from Supabase schema
  • RLS policies and role-based access
  • Seed scripts and local development workflow

See also