Skip to main content

Source: ocean/docs/adr/0033-realtime-subscriptions.md | ✏️ Edit on GitHub

0033. Supabase Realtime Subscriptions for Live Updates

Date: 2025-08-14

Status

Accepted

Context

Our billing and organization pages required users to manually refresh to see updates such as:

  • Subscription status changes
  • Payment method additions
  • Invoice generation
  • Provisioning completion

This led to:

  • Poor user experience during critical operations
  • Support tickets about "missing" updates
  • Users repeatedly refreshing pages
  • Confusion during collaborative billing management

Decision

We implemented Supabase Realtime subscriptions to push live updates to the UI without polling or manual refreshes.

Implementation Approach

  1. Generic Hook: useRealtimeSubscription for any table/filter combination
  2. Specific Hooks: Pre-configured subscriptions for common use cases
  3. Automatic Query Invalidation: Updates trigger React Query cache refreshes
  4. Type-Safe: Full TypeScript support for payload types

Example Usage

// Generic subscription
useRealtimeSubscription({
table: 'organizations',
event: 'UPDATE',
filter: `id=eq.${orgId}`,
onUpdate: (payload) => {
console.log('Org updated:', payload.new)
},
invalidateQueries: [['organization', orgId]],
})

// Specialized hook
useSubscriptionStatusSubscription(orgId)

Subscriptions Implemented

  1. Organization Updates: Plan changes, billing info
  2. Subscription Status: Active, canceled, past due
  3. Invoice Creation: New invoices appear instantly
  4. Payment Methods: Added/removed cards
  5. Provisioning Events: Resource creation status

Consequences

Positive

  1. Real-time UX: Changes appear instantly across all tabs/users
  2. Reduced Server Load: No polling required
  3. Collaborative: Multiple users see same updates
  4. Event-Driven: Natural fit for billing events
  5. Efficient: Only subscribes to relevant data

Negative

  1. WebSocket Connections: Additional connection management
  2. Debugging Complexity: Harder to trace data flow
  3. State Synchronization: Must handle connection drops

Performance Impact

  • Polling requests: Eliminated (was ~1req/5sec/user)
  • Time to see updates: 5-30 seconds → <100ms
  • User satisfaction: Significant improvement in billing workflows

Alternatives Considered

  1. Polling

    • Rejected: Inefficient, poor UX
  2. Server-Sent Events

    • Rejected: Less flexible than WebSockets
  3. Manual Refresh Only

    • Rejected: Poor user experience

Implementation Details

  • Subscriptions are request-scoped (cleanup on unmount)
  • Automatic reconnection on connection loss
  • Query invalidation triggers UI updates
  • Filtered subscriptions reduce unnecessary updates

Best Practices

  1. Subscribe only to data visible on screen
  2. Use filters to minimize updates
  3. Combine with optimistic updates for best UX
  4. Handle connection state in critical flows
  5. Log but don't crash on subscription errors

References