Supabase Setup Guide
This documentation has not yet been fully tested with an actual Supabase project. Some steps may be incomplete or require adjustments. We welcome community contributions to validate and improve this guide.
Learn how to configure Boards to use Supabase as your database, storage, and authentication provider instead of the default local PostgreSQL setup.
Why Use Supabase?
Supabase provides a complete backend-as-a-service that includes:
- PostgreSQL Database - Managed PostgreSQL with connection pooling
- Storage - S3-compatible object storage for generated artifacts
- Authentication - Built-in auth with multiple providers
- Realtime - WebSocket subscriptions for live updates
- Row Level Security - Perfect for multi-tenant applications
This guide shows you how to migrate from the local Docker PostgreSQL setup to Supabase.
Prerequisites
- Supabase account (free tier available)
- Existing Boards development environment
- Basic understanding of PostgreSQL
1. Create Supabase Project
- Sign up at supabase.com
- Create a new project:
- Choose a project name (e.g., "boards-dev")
- Set a database password (save this!)
- Select a region close to you
- Wait for setup (usually 1-2 minutes)
2. Get Connection Details
From your Supabase project dashboard:
- Go to Settings → Database
- Find the Connection string section
- Copy the connection details:
# Direct connection (for development)
postgresql://postgres.[PROJECT_REF]:[PASSWORD]@db.[PROJECT_REF].supabase.co:5432/postgres
# Connection pooling (for production)
postgresql://postgres.[PROJECT_REF]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres?pgbouncer=true
- Go to Settings → API and copy:
- Project URL
- Anonymous public key (
anonkey) - Service role key (
service_rolekey) - Keep this secret!
3. Update Environment Configuration
Create or update your environment file:
# packages/backend/.env.supabase
# Database Configuration
BOARDS_DATABASE_URL=postgresql://postgres.abcdefghij:your_password@db.abcdefghij.supabase.co:5432/postgres
# Supabase API Configuration
SUPABASE_URL=https://abcdefghij.supabase.co
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# For production, use connection pooling:
# BOARDS_DATABASE_URL=postgresql://postgres.abcdefghij:your_password@aws-0-us-east-1.pooler.supabase.com:6543/postgres?pgbouncer=true
Security Note: Never commit the service role key to version control. Use environment variables or secure secret management.
4. Apply Database Schema
Apply your schema to Supabase using Alembic:
cd packages/backend
uv run alembic upgrade head
This will create all tables and required extensions (e.g., uuid-ossp).
5. Configure Storage
Set up Supabase Storage for generated artifacts:
Create Storage Buckets
In the Supabase dashboard:
- Go to Storage
- Create buckets:
generations- For AI-generated contentthumbnails- For preview imageslora-models- For custom models (if applicable)
Set Bucket Policies
-- Allow authenticated users to upload to their tenant's folder
CREATE POLICY "Users can upload to their tenant folder" ON storage.objects
FOR INSERT TO authenticated
WITH CHECK (
bucket_id = 'generations'
AND (storage.foldername(name))[1] = auth.jwt() ->> 'tenant_id'
);
-- Allow users to read their tenant's files
CREATE POLICY "Users can view their tenant files" ON storage.objects
FOR SELECT TO authenticated
USING (
bucket_id = 'generations'
AND (storage.foldername(name))[1] = auth.jwt() ->> 'tenant_id'
);
Update Backend Configuration
# packages/backend/src/boards/config.py
import os
from supabase import create_client
# Supabase client for storage operations
SUPABASE_URL = os.environ.get('SUPABASE_URL')
SUPABASE_KEY = os.environ.get('SUPABASE_SERVICE_KEY') # Use service key for backend
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
# Storage configuration
STORAGE_BUCKET = 'generations'
THUMBNAIL_BUCKET = 'thumbnails'
6. Row Level Security (RLS)
Enable RLS for multi-tenant data isolation using Alembic revisions (see Database Migrations guide for patterns). Example:
from alembic import op
def upgrade() -> None:
op.execute("ALTER TABLE users ENABLE ROW LEVEL SECURITY;")
# ... create policies with IF NOT EXISTS guards ...
def downgrade() -> None:
# ... drop policies if exist, then disable RLS ...
op.execute("ALTER TABLE users DISABLE ROW LEVEL SECURITY;")
7. Authentication Integration
Configure Supabase Auth to work with your existing user system:
Update User Registration
# When creating users, sync with Supabase Auth
from supabase import create_client
async def create_user(email: str, password: str, tenant_id: str):
# Create user in Supabase Auth
auth_response = supabase.auth.sign_up({
"email": email,
"password": password,
"options": {
"data": {
"tenant_id": tenant_id # Custom claim for RLS
}
}
})
# Create user record in your database
user_record = await create_user_record(
auth_subject=auth_response.user.id,
auth_provider="supabase",
email=email,
tenant_id=tenant_id
)
return user_record
JWT Custom Claims
Ensure the JWT includes tenant_id for RLS policies:
-- Function to add tenant_id to JWT
CREATE OR REPLACE FUNCTION public.custom_access_token_hook(event jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
BEGIN
-- Add tenant_id from user metadata to JWT
IF event->>'user_id' IS NOT NULL THEN
DECLARE tenant_id TEXT;
SELECT u.tenant_id INTO tenant_id
FROM public.users u
WHERE u.auth_subject = (event->>'user_id')
AND u.auth_provider = 'supabase';
IF tenant_id IS NOT NULL THEN
event := jsonb_set(event, '{tenant_id}', to_jsonb(tenant_id), true);
END IF;
END IF;
RETURN event;
END;
$$;
-- Grant permissions
GRANT EXECUTE ON FUNCTION public.custom_access_token_hook TO service_role;
8. Local Development Options
Option 1: Direct Remote Connection
Use your Supabase project directly for development:
# Use remote database URL
export BOARDS_DATABASE_URL="postgresql://postgres.xxx:password@db.xxx.supabase.co:5432/postgres"
# Start development
make dev
Pros: Real cloud environment, no local setup needed
Cons: Requires internet connection, shared with team
Option 2: Supabase CLI Local Development
Run Supabase locally while staying in sync with remote:
# Start local Supabase
supabase start
# This provides local URLs:
# Database: postgresql://postgres:postgres@localhost:54322/postgres
# API URL: http://localhost:54321
# Sync schema from remote
supabase db pull
# Make local changes and push
supabase db push
Pros: Offline development, isolated environment
Cons: Additional setup, need to sync changes
9. Production Configuration
Connection Pooling
For production, use connection pooling to handle high load:
# Use pooled connection string
BOARDS_DATABASE_URL=postgresql://postgres.xxx:password@aws-0-region.pooler.supabase.com:6543/postgres?pgbouncer=true
Performance Optimization
-- Add indexes for common queries (if not already present)
CREATE INDEX CONCURRENTLY idx_generations_tenant_status ON generations(tenant_id, status);
CREATE INDEX CONCURRENTLY idx_boards_tenant_owner ON boards(tenant_id, owner_id);
-- Optimize for time-series queries
CREATE INDEX CONCURRENTLY idx_generations_created_at ON generations(created_at DESC);
Monitoring and Alerts
Set up monitoring in Supabase dashboard:
- Database tab → Monitor connection count, query performance
- API tab → Monitor request volume and errors
- Storage tab → Monitor storage usage and bandwidth
10. Testing Your Setup
Verify everything works:
# Test database connection
psql $BOARDS_DATABASE_URL -c "SELECT version();"
# Test your application
cd packages/backend
python -c "from boards.dbmodels import Users, Boards; print('✅ import ok')"
# Start the development server
make dev
Next Steps
With Supabase configured, you can now:
- Deploy to production using the same Supabase project
- Set up Supabase Storage for file uploads and serving
- Configure Supabase Auth for user authentication
- Use Supabase Realtime for live updates
- Set up Edge Functions for webhooks and background jobs
For detailed migration workflows, see the Database Migrations guide.