Files
supabase-postgres-best-prac…/skills/supabase/references/realtime-broadcast-database.md

2.9 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Broadcast from Database Triggers CRITICAL Scalable pattern for notifying clients of database changes realtime, broadcast, database, triggers, realtime.send, realtime.broadcast_changes

Broadcast from Database Triggers

Use database triggers with realtime.broadcast_changes() instead of postgres_changes for better scalability. This avoids per-subscriber RLS checks.

realtime.broadcast_changes()

Broadcasts database changes in a standard format.

create or replace function room_messages_broadcast()
returns trigger
security definer
set search_path = ''
language plpgsql
as $$
begin
  perform realtime.broadcast_changes(
    'room:' || coalesce(new.room_id, old.room_id)::text,  -- topic
    tg_op,                                                 -- event (INSERT/UPDATE/DELETE)
    tg_op,                                                 -- operation
    tg_table_name,                                         -- table
    tg_table_schema,                                       -- schema
    new,                                                   -- new record
    old                                                    -- old record
  );
  return null;  -- AFTER trigger return value is ignored
end;
$$;

create trigger messages_broadcast_trigger
  after insert or update or delete on messages
  for each row execute function room_messages_broadcast();

Client subscription:

const channel = supabase
  .channel('room:123', { config: { private: true } })
  .on('broadcast', { event: 'INSERT' }, (payload) => console.log('Insert:', payload))
  .on('broadcast', { event: 'UPDATE' }, (payload) => console.log('Update:', payload))
  .on('broadcast', { event: 'DELETE' }, (payload) => console.log('Delete:', payload))
  .subscribe()

realtime.send()

Sends custom payloads without table binding.

select realtime.send(
  jsonb_build_object('message', 'Custom notification'),  -- payload
  'notification_sent',                                   -- event
  'user:456:notifications',                              -- topic
  true                                                   -- private (true = requires auth)
);

Public vs Private Mismatch

Incorrect:

-- Database sends to public channel
select realtime.send('{}', 'event', 'topic', false);  -- private = false
// Client expects private channel - won't receive message
const channel = supabase.channel('topic', { config: { private: true } })

Correct:

-- Database sends to private channel
select realtime.send('{}', 'event', 'topic', true);  -- private = true
// Client matches
const channel = supabase.channel('topic', { config: { private: true } })