Skip to main content

Data Access Guide

Blue North is standardizing on Supabase/Postgres as the system of record. The database should hold accounts, campaigns, campaign memberships, voters, districts, turfs, contact history, schedules, admin audit logs, and related production data.

Rule Of Thumb

UI should not call Supabase all over the app. Prefer focused data-access modules owned by a feature.

Good:

features/campaigns/data/campaigns.repo.ts
features/voters/data/voters.repo.ts
features/turf/data/turfs.repo.ts

Avoid:

components/some-panel.tsx calls supabase.from("campaign_memberships") directly
components/another-card.tsx repeats the same query

Supabase Client Helpers

Client creation lives in:

platform/lib/supabase/client.ts
platform/lib/supabase/server.ts
platform/lib/supabase/middleware.ts
platform/proxy.ts

Use the browser client only in client components that truly need client-side auth or realtime behavior. Use the server client for server components, route handlers, and server actions.

Multi-Tenant Access

Production data is campaign-scoped. Most tables should include campaign_id, and queries should always be written with the active campaign in mind.

Database-level Row Level Security is the final safety net:

profiles
campaigns
campaign_memberships
voters
voter_lists
turfs
contact_attempts
audit_events

The app should still pass explicit campaign context for clarity and performance.

Repository Shape

Repository modules should expose named functions with business meaning:

export async function listCampaignsForCurrentUser()
export async function createCampaign(input)
export async function getVotersPage(input)
export async function saveTurf(input)

They should hide table names, Supabase query details, and mapping between database rows and UI models.

Migrations

All database schema changes belong in:

platform/supabase/migrations

Migration expectations:

  • include indexes for common query paths
  • enable RLS for campaign-owned data
  • avoid destructive changes without a documented rollout plan
  • prefer expand/backfill/contract for large tables

Current Transition State

The app still has legacy static-data and localStorage paths. That is expected during the transition.

When moving a surface to Supabase:

  1. Define the database table or migration.
  2. Add a feature-owned repository.
  3. Update UI to call the repository/server route.
  4. Keep sandbox fallback behavior explicit.
  5. Verify build and preview deploy.

Recommended first production migrations after auth:

  • campaign creation and membership onboarding
  • saved voter lists
  • saved turfs and turf memberships
  • contact attempts