Broadcast
Send and receive messages using Realtime Broadcast
Let's explore how to implement Realtime Broadcast to send messages between clients using either WebSockets, REST API or triggers from your database.
Usage
You can use the Supabase client libraries to send and receive Broadcast messages.
Initialize the client
Go to your Supabase project's API Settings and grab the URL
and anon
public API key.
_10import { createClient } from '@supabase/supabase-js'_10_10const SUPABASE_URL = 'https://<project>.supabase.co'_10const SUPABASE_KEY = '<your-anon-key>'_10_10const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
Listening to broadcast messages
You can provide a callback for the broadcast
channel to receive message. This example will receive any broadcast
messages in room-1
:
_16// Join a room/topic. Can be anything except for 'realtime'._16const channelA = supabase.channel('room-1')_16_16// Simple function to log any messages we receive_16function messageReceived(payload) {_16 console.log(payload)_16}_16_16// Subscribe to the Channel_16channelA_16 .on(_16 'broadcast',_16 { event: 'test' },_16 (payload) => messageReceived(payload)_16 )_16 .subscribe()
Sending broadcast messages
We can send Broadcast messages using channelB.send()
. Let's set up another client to send messages.
_16// Join a room/topic. Can be anything except for 'realtime'._16const channelB = supabase.channel('room-1')_16_16channelB.subscribe((status) => {_16 // Wait for successful connection_16 if (status !== 'SUBSCRIBED') {_16 return null_16 }_16_16 // Send a message once the client is subscribed_16 channelB.send({_16 type: 'broadcast',_16 event: 'test',_16 payload: { message: 'hello, world' },_16 })_16})
Before sending messages we need to ensure the client is connected, which we have done within the subscribe()
callback.
Broadcast options
You can pass configuration options while initializing the Supabase Client.
Self-send messages
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's self
parameter to true
.
_20const myChannel = supabase.channel('room-2', {_20 config: {_20 broadcast: { self: true },_20 },_20})_20_20myChannel.on(_20 'broadcast',_20 { event: 'test-my-messages' },_20 (payload) => console.log(payload)_20)_20_20myChannel.subscribe((status) => {_20 if (status !== 'SUBSCRIBED') { return }_20 channelC.send({_20 type: 'broadcast',_20 event: 'test-my-messages',_20 payload: { message: 'talking to myself' },_20 })_20})
Acknowledge messages
You can confirm that Realtime received your message by setting Broadcast's ack
config to true
.
_17const myChannel = supabase.channel('room-3', {_17 config: {_17 broadcast: { ack: true },_17 },_17})_17_17myChannel.subscribe(async (status) => {_17 if (status !== 'SUBSCRIBED') { return }_17_17 const serverResponse = await myChannel.send({_17 type: 'broadcast',_17 event: 'acknowledge',_17 payload: {},_17 })_17_17 console.log('serverResponse', serverResponse)_17})
Use this to guarantee that the server has received the message before resolving channelD.send
's promise. If the ack
config is not set to true
when creating the channel, the promise returned by channelD.send
will resolve immediately.
Send messages using REST calls
You can also send a Broadcast message by making an HTTP request to Realtime servers. This is useful when you want to send messages from your server or client without having to first establish a WebSocket connection.
This is currently available only in the Supabase JavaScript client version 2.37.0 and later.
_15const channel = supabase.channel('test-channel')_15_15// No need to subscribe to channel_15_15channel_15 .send({_15 type: 'broadcast',_15 event: 'test',_15 payload: { message: 'Hi' },_15 })_15 .then((resp) => console.log(resp))_15_15// Remember to clean up the channel_15_15supabase.removeChannel(channel)
Trigger broadcast messages from your database
This feature is currently in Private Alpha. The API and implementation may change. To request access, submit a Support Ticket.
How it works
Broadcast Changes allows you to trigger messages from your database. To achieve it Realtime is directly reading your WAL (Write Append Log) file using a publication against the realtime.messages
table so whenever a new insert happens a message is sent to connected users.
It uses partitioned tables per day which allows the deletion your previous images in a performant way by dropping the physical tables of this partitioned table. Tables older than 3 days old are deleted.
Broadcasting from the database works like a client-side broadcast, using WebSockets to send JSON packages. Realtime Authorization is required and enabled by default to protect your data.
The database broadcast feature provides two functions to help you send messages:
realtime.send
will insert a message into realtime.messages without a specific format.realtime.broadcast_changes
will insert a message with the required fields to emit database changes to clients. This helps you set up triggers on your tables to emit changes.
Broadcasting a message from your database
The realtime.send
function provides the most flexibility by allowing you to broadcast messages from your database without a specific format. This allows you to use database broadcast for messages that aren't necessarily tied to the shape of a Postgres row change.
_10SELECT realtime.send (_10 to_jsonb ('{}'::text), -- JSONB Payload_10 'event', -- Event name_10 'topic', -- Topic_10 FALSE -- Public / Private flag_10);
Broadcast record changes
Setup realtime authorization
Realtime Authorization is required and enabled by default. To allow your users to listen to messages from topics, create a RLS (Row Level Security) policy:
_10CREATE POLICY "authenticated can receive broadcasts"_10ON "realtime"."messages"_10FOR SELECT_10TO authenticated_10USING ( true );
See the Realtime Authorization docs to learn how to set up more specific policies.
Set up trigger function
First, set up a trigger function that uses realtime.broadcast_changes
to insert an event whenever it is triggered. The event is set up to include data on the schema, table, operation, and field changes that triggered it.
For this example use case, we want to have a topic with the name topic:<record id>
to which we're going to broadcast events.
_14CREATE OR REPLACE FUNCTION public.your_table_changes() RETURNS trigger AS $$_14BEGIN_14 PERFORM realtime.broadcast_changes(_14 'topic:' || NEW.id::text, -- topic_14 TG_OP, -- event_14 TG_OP, -- operation_14 TG_TABLE_NAME, -- table_14 TG_TABLE_SCHEMA, -- schema_14 NEW, -- new record_14 OLD -- old record_14 );_14 RETURN NULL;_14END;_14$$ LANGUAGE plpgsql;
Of note are the Postgres native trigger special variables used:
TG_OP
- the operation that triggered the functionTG_TABLE_NAME
- the table that caused the triggerTG_TABLE_SCHEMA
- the schema of the table that caused the trigger invocationNEW
- the record after the changeOLD
- the record before the change
You can read more about them in this guide.
Set up trigger
Next, set up a trigger so the function runs whenever your target table has a change.
_10CREATE TRIGGER broadcast_changes_for_your_table_trigger_10AFTER INSERT OR UPDATE OR DELETE ON public.your_table_10FOR EACH ROW_10EXECUTE FUNCTION your_table_changes ();
As you can see, it will be broadcasting all operations so our users will receive events when records are inserted, updated or deleted from public.your_table
.
Listen on client side
Finally, client side will requires to be set up to listen to the topic topic:<record id>
to receive the events.
_10const gameId = 'id'_10await supabase.realtime.setAuth() // Needed for Realtime Authorization_10const changes = supabase_10 .channel(`topic:${gameId}`)_10 .on('broadcast', { event: 'INSERT' }, (payload) => console.log(payload))_10 .on('broadcast', { event: 'UPDATE' }, (payload) => console.log(payload))_10 .on('broadcast', { event: 'DELETE' }, (payload) => console.log(payload))_10 .subscribe()