Skip to main content

Relationship Diagram

The following diagram shows the relationships between all database tables:

Foreign Key Relationships

profiles

Foreign KeyReferencesOn DeleteDescription
user_idauth.users(id)CASCADELinks profile to auth user
Notes:
  • user_id has a UNIQUE constraint (one profile per user)
  • Profile is auto-created via trigger when user signs up

user_roles

Foreign KeyReferencesOn DeleteDescription
user_idauth.users(id)CASCADELinks role to auth user
Notes:
  • (user_id, role) has a UNIQUE constraint (prevent duplicate role assignments)
  • A user can have multiple roles (e.g., both client and event_manager)

organizations

Foreign KeyReferencesOn DeleteDescription
owner_idauth.users(id)CASCADEOrganization owner
Notes:
  • No incoming foreign keys (top of the organization hierarchy)
  • Deleting the owner deletes the organization and all child records (events, attendees, etc.)

organization_members

Foreign KeyReferencesOn DeleteDescription
organization_idorganizations(id)CASCADEParent organization
user_idauth.users(id)CASCADEMember user account
Notes:
  • (organization_id, user_id) has a UNIQUE constraint (user can only be added once per org)
  • Deleting the organization removes all member associations
  • Deleting the user removes their membership

events

Foreign KeyReferencesOn DeleteDescription
organization_idorganizations(id)CASCADEParent organization
Notes:
  • (organization_id, slug) has a UNIQUE constraint (event slugs unique within org)
  • Deleting the organization deletes all events and their attendees
  • Event deletion should call return_attendee_tokens_on_event_delete() to refund tokens

event_manager_assignments

Foreign KeyReferencesOn DeleteDescription
event_idevents(id)CASCADEAssigned event
user_idauth.users(id)CASCADEAssigned event manager
Notes:
  • (event_id, user_id) has a UNIQUE constraint (manager assigned once per event)
  • Deleting the event removes all manager assignments
  • Deleting the user removes their event assignments

attendees

Foreign KeyReferencesOn DeleteDescription
event_idevents(id)CASCADEParent event
Notes:
  • (event_id, unique_id) has a UNIQUE constraint
  • (event_id, email) has a UNIQUE constraint
  • Deleting the event deletes all attendees
  • Deleting attendees triggers counter updates in the events table

token_transactions

Foreign KeyReferencesOn DeleteDescription
organization_idorganizations(id)CASCADEOrganization that purchased tokens
Notes:
  • Transactions are immutable (no UPDATE policies)
  • Deleting the organization deletes transaction history

audit_logs

Foreign KeyReferencesOn DeleteDescription
organization_idorganizations(id)SET NULLOrganization context
Notes:
  • organization_id is nullable (platform-level actions may not have an org)
  • ON DELETE SET NULL preserves audit history even if org is deleted
  • user_id is not a foreign key (allows logging of deleted users)

token_pricing

No foreign keys. This is a configuration table managed by super admins.

platform_settings

No foreign keys. This is a global configuration table.

Cascading Delete Behavior

Understanding cascading deletes is critical for data integrity:

Deleting a User (auth.users)

Impact:
  • If the user owns an organization, all events, attendees, transactions, and members are deleted
  • This is by design to maintain referential integrity
  • Consider transferring ownership before deleting a client account

Deleting an Organization

Impact:
  • All events, attendees, members, and transactions are permanently deleted
  • Audit logs remain but lose organization reference
  • No token refunds occur automatically

Deleting an Event

Impact:
  • All attendees and manager assignments are deleted
  • Application should call return_attendee_tokens_on_event_delete() to refund attendee tokens
  • Event token is not automatically refunded

Unique Constraints

Unique constraints prevent duplicate data:

Single-Column Unique Constraints

TableColumn(s)Purpose
profilesuser_idOne profile per user
organizationsslugGlobally unique organization slugs
platform_settingskeyOne row per setting

Composite Unique Constraints

TableColumn(s)Purpose
user_roles(user_id, role)Prevent duplicate role assignments
organization_members(organization_id, user_id)User can only join an org once
events(organization_id, slug)Event slugs unique within organization
event_manager_assignments(event_id, user_id)Manager assigned once per event
attendees(event_id, unique_id)Unique attendee IDs per event
attendees(event_id, email)Unique attendee emails per event
The attendees table has two unique constraints: one on unique_id and one on email, both scoped to the event. This prevents duplicate attendees by either identifier.

Relationship Patterns

One-to-One

  • User → Profile: Each user has exactly one profile
    • Enforced by UNIQUE constraint on profiles.user_id
    • Created automatically via trigger on signup

One-to-Many

  • Organization → Events: An organization owns multiple events
  • Event → Attendees: An event has multiple attendees
  • Organization → Token Transactions: An organization has multiple purchases
  • User → Organizations (as owner): A user can own multiple organizations

Many-to-Many

  • Users ↔ Organizations (via organization_members)
    • A user can be a member of multiple organizations
    • An organization can have multiple members
  • Users ↔ Events (via event_manager_assignments)
    • An event manager can be assigned to multiple events
    • An event can have multiple event managers
  • Users ↔ Roles (via user_roles)
    • A user can have multiple roles (e.g., client + event_manager)
    • A role can be assigned to multiple users

Authorization Patterns

Foreign keys support RLS policy authorization:

Ownership-Based Access

-- Users can view their own organization
CREATE POLICY "Owners can view own org" 
  ON organizations FOR SELECT 
  USING (auth.uid() = owner_id);

-- Owners can manage their events
CREATE POLICY "Org owners can manage their events"
  ON events FOR ALL
  USING (organization_id IN (
    SELECT id FROM organizations WHERE owner_id = auth.uid()
  ));

Membership-Based Access

-- Organization members can view events
CREATE POLICY "Org members can view events"
  ON events FOR SELECT
  USING (organization_id IN (
    SELECT organization_id FROM organization_members 
    WHERE user_id = auth.uid() AND status = 'active'
  ));

Assignment-Based Access

-- Event managers can view assigned events' attendees
CREATE POLICY "Event managers can view attendees"
  ON attendees FOR SELECT
  USING (event_id IN (
    SELECT event_id FROM event_manager_assignments 
    WHERE user_id = auth.uid()
  ));

Database Functions (RPC)

Several security definer functions abstract complex relationship queries:

Authorization Helpers

FunctionPurpose
has_role(_user_id, _role)Check if user has a specific role
is_org_member(_user_id, _org_id)Check if user is an active org member
get_user_org_id(_user_id)Get the organization ID for a user (owner or member)

Public Access Functions

FunctionPurpose
get_org_public_info(_slug)Safe organization lookup for public pages
get_event_public_info(_event_slug, _org_id)Event details for check-in and portal pages
public_atomic_checkin(...)Validate and perform check-in in one call
get_attendee_portal_data(...)Attendee data for portal access

Business Logic Functions

FunctionPurpose
create_org_and_assign_role(...)Atomic org creation + role assignment on signup
deduct_event_token(_organization_id)Safely deduct one event token
deduct_attendee_tokens(_organization_id, _count)Safely deduct attendee tokens
return_attendee_tokens_on_event_delete(...)Refund attendee tokens when event is deleted
log_audit_event(...)Create audit log entry with authorization checks
All token deduction functions use FOR UPDATE row locking to prevent race conditions under concurrent access.

Circular Dependency Prevention

The schema avoids circular foreign key dependencies:
  • auth.users (managed by Supabase) is at the root
  • profiles and user_roles reference users but nothing references them
  • organizations reference users but only via owner_id
  • organization_members references both orgs and users (bridge table)
  • All tenant data flows from organizations down (events → attendees)
This unidirectional flow ensures clean cascading deletes without deadlocks.

Next Steps

Schema Overview

High-level architecture and design principles

Tables Reference

Detailed column specifications

Build docs developers (and LLMs) love