Comprehensive guide for implementing Supabase Realtime features with best practices, scalable patterns, and migration strategies...
Expert implementation guide for Supabase Realtime features focusing on scalable patterns and best practices.
Always use broadcast over postgres_changes - postgres_changes is single-threaded and doesn't scale. Use broadcast with database triggers for all database change notifications.
Use dedicated topics - Never broadcast to global topics. Use granular patterns like room:123:messages, user:456:notifications.
Private channels by default - Set private: true for all database-triggered channels. Enable private-only mode in production.
scope:entity:id (e.g., room:123:messages)entity_action in snake_case (e.g., message_created)const channel = supabase.channel('room:123:messages', {
config: {
broadcast: { self: true, ack: true },
private: true // Required for RLS
}
})
// Set auth before subscribing
await supabase.realtime.setAuth()
channel
.on('broadcast', { event: 'message_created' }, handler)
.subscribe((status, err) => {
if (status === 'SUBSCRIBED') console.log('Connected')
})
// Cleanup
supabase.removeChannel(channel)
-- Use realtime.broadcast_changes for database events
CREATE OR REPLACE FUNCTION notify_table_changes()
RETURNS TRIGGER AS $$
BEGIN
PERFORM realtime.broadcast_changes(
TG_TABLE_NAME || ':' || COALESCE(NEW.id, OLD.id)::text,
TG_OP,
TG_OP,
TG_TABLE_NAME,
TG_TABLE_SCHEMA,
NEW,
OLD
);
RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER messages_broadcast_trigger
AFTER INSERT OR UPDATE OR DELETE ON messages
FOR EACH ROW EXECUTE FUNCTION notify_table_changes();
-- Required for private channels
CREATE POLICY "room_members_can_read" ON realtime.messages
FOR SELECT TO authenticated
USING (
topic LIKE 'room:%' AND
EXISTS (
SELECT 1 FROM room_members
WHERE user_id = auth.uid()
AND room_id = SPLIT_PART(topic, ':', 2)::uuid
)
);
-- Required index for performance
CREATE INDEX idx_room_members_user_room ON room_members(user_id, room_id);
broadcast not postgres_changesprivate: true for database triggers// Notify user when CV optimization is complete
const channel = supabase.channel(`user:${userId}:notifications`, {
config: { private: true }
})
channel
.on('broadcast', { event: 'optimization_complete' }, (payload) => {
// Show notification to user
toast.success('Your CV has been optimized!')
})
.subscribe()
// Real-time progress during CV optimization
const channel = supabase.channel(`job:${jobId}:progress`, {
config: { private: true }
})
channel
.on('broadcast', { event: 'progress_update' }, (payload) => {
setProgress(payload.percentage)
})
.subscribe()