Broadcast
Send low-latency messages using the client libs, REST, or your Database.
You can use Realtime Broadcast to send low-latency messages between users. Messages can be sent using the client libraries, REST APIs, or directly from your database.
How Broadcast works#
The way Broadcast works changes based on the channel you are using:
- REST API: Receives an HTTP request and then sends a message via WebSocket to connected clients
- Client libraries: Sends a message via WebSocket to the server, and then the server sends a message via WebSocket to connected clients
- Database: Adds a new entry to
realtime.messageswhere a logical replication is set to listen for changes, and then sends a message via WebSocket to connected clients
The public flag (the last argument in realtime.send(payload, event, topic, is_private)) only affects who can subscribe to the topic not who can read messages from the database.
- Public (
false) → Anyone can subscribe to that topic without authentication - Private (
true) → Only authenticated clients can subscribe to that topic
Regardless if it's public or private, the Realtime service connects to your database as the authenticated Supabase Admin role.
For Authorization, we insert a message and try to read it, and rollback the transaction to verify that the Row Level Security (RLS) policies set by the user are being respected by the user joining the channel, but this message isn't sent to the user. You can read more about it in Authorization.
Subscribe to messages#
You can use the Supabase client libraries to receive Broadcast messages.
Initialize the client#
Get the Project URL and key from the project's Connect dialog.
Changes to API keys
Supabase is changing the way keys work to improve project security and developer experience. You can read the full announcement, but in the transition period, you can use both the current anon and service_role keys and the new publishable key with the form sb_publishable_xxx which will replace the older keys.
In most cases, you can get the correct key from the Project's Connect dialog, but if you want a specific key, you can find all keys in the API Keys section of a Project's Settings page:
- For legacy keys, copy the
anonkey for client-side operations and theservice_rolekey for server-side operations from the Legacy API Keys tab. - For new keys, open the API Keys tab, if you don't have a publishable key already, click Create new API Keys, and copy the value from the Publishable key section.
1import { createClient } from '@supabase/supabase-js'23const SUPABASE_URL = 'https://<project>.supabase.co'4const SUPABASE_KEY = '<sb_publishable_... or anon key>'56const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)Receive Broadcast messages#
You can receive Broadcast messages by providing a callback to the channel.
1// @noImplicitAny: false2import { createClient } from '@supabase/supabase-js'3const supabase = createClient('https://<project>.supabase.co', '<sb_publishable_... or anon key>')45// ---cut---6// Join a room/topic. Can be anything except for 'realtime'.7const myChannel = supabase.channel('test-channel')89// Simple function to log any messages we receive10function messageReceived(payload) {11 console.log(payload)12}1314// Subscribe to the Channel15myChannel16 .on(17 'broadcast',18 { event: 'shout' }, // Listen for "shout". Can be "*" to listen to all events19 (payload) => messageReceived(payload)20 )21 .subscribe()Send messages#
Broadcast using the client libraries#
You can use the Supabase client libraries to send Broadcast messages.
1import { createClient } from '@supabase/supabase-js'2const supabase = createClient('your_project_url', 'your_supabase_api_key')34// ---cut---5const myChannel = supabase.channel('test-channel')67/**8 * Sending a message before subscribing will use HTTP9 */10myChannel11 .send({12 type: 'broadcast',13 event: 'shout',14 payload: { message: 'Hi' },15 })16 .then((resp) => console.log(resp))171819/**20 * Sending a message after subscribing will use WebSockets21 */22myChannel.subscribe((status) => {23 if (status !== 'SUBSCRIBED') {24 return null25 }2627 myChannel.send({28 type: 'broadcast',29 event: 'shout',30 payload: { message: 'Hi' },31 })32})Broadcast from the Database#
This feature is in Public Beta. Submit a support ticket if you have any issues.
All the messages sent using Broadcast from the Database are stored in realtime.messages table and will be deleted after 3 days.
You can send messages directly from your database using the realtime.send() function:
1select2 realtime.send(3 jsonb_build_object('hello', 'world'), -- JSONB Payload4 'event', -- Event name5 'topic', -- Topic6 false -- Public / Private flag7 );The realtime.send() function in the database includes a flag that determines whether the broadcast is private or public, and client channels also have the same configuration. For broadcasts to work correctly, these settings must match. A public broadcast only reaches public channels and a private broadcast only reaches private channels.
By default, all database broadcasts are private, meaning clients must authenticate to receive them. If the database sends a public message but the client subscribes to a private channel, the message is not delivered because private channels only accept signed, authenticated messages.
You can use the realtime.broadcast_changes() helper function to broadcast messages when a record is created, updated, or deleted. For more details, read Subscribing to Database Changes.
Broadcast using the REST API#
You can send a Broadcast message by making an HTTP request to Realtime servers.
1curl -v \2-H 'apikey: <SUPABASE_TOKEN>' \3-H 'Content-Type: application/json' \4--data-raw '{5 "messages": [6 {7 "topic": "test",8 "event": "event",9 "payload": { "test": "test" }10 }11 ]12}' \13'https://<PROJECT_REF>.supabase.co/realtime/v1/api/broadcast'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.
1const myChannel = supabase.channel('room-2', {2 config: {3 broadcast: { self: true },4 },5})67myChannel.on(8 'broadcast',9 { event: 'test-my-messages' },10 (payload) => console.log(payload)11)1213myChannel.subscribe((status) => {14 if (status !== 'SUBSCRIBED') { return }15 myChannel.send({16 type: 'broadcast',17 event: 'test-my-messages',18 payload: { message: 'talking to myself' },19 })20})Acknowledge messages#
You can confirm that the Realtime servers have received your message by setting Broadcast's ack setting to true.
1import { createClient } from '@supabase/supabase-js'2const supabase = createClient('your_project_url', 'your_supabase_api_key')34// ---cut---5const myChannel = supabase.channel('room-3', {6 config: {7 broadcast: { ack: true },8 },9})1011myChannel.subscribe(async (status) => {12 if (status !== 'SUBSCRIBED') { return }1314 const serverResponse = await myChannel.send({15 type: 'broadcast',16 event: 'acknowledge',17 payload: {},18 })1920 console.log('serverResponse', serverResponse)21})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.
1const channel = supabase.channel('test-channel')23// No need to subscribe to channel45channel6 .send({7 type: 'broadcast',8 event: 'test',9 payload: { message: 'Hi' },10 })11 .then((resp) => console.log(resp))1213// Remember to clean up the channel1415supabase.removeChannel(channel)Trigger broadcast messages from your database#
How it works#
Broadcast Changes allows you to trigger messages from your database. To achieve it, Realtime directly reads your Write-Ahead Log (WAL) file using a publication against the realtime.messages table. Whenever a new insert occurs, a message is sent to connected users.
It uses partitioned tables per day, which allows performant deletion of your previous messages by dropping the physical tables of this partitioned table. Tables older than 3 days are deleted.
Broadcasting from the database works like a client-side broadcast, using WebSockets to send JSON payloads. Realtime Authorization is required and enabled by default to protect your data.
Broadcast Changes provides two functions to help you send messages:
realtime.send()inserts a message intorealtime.messageswithout a specific format.realtime.broadcast_changes()inserts 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.
1SELECT realtime.send (2 '{}'::jsonb, -- JSONB Payload3 'event', -- Event name4 'topic', -- Topic5 FALSE -- Public / Private flag6);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 an RLS policy:
1CREATE POLICY "authenticated can receive broadcasts"2ON "realtime"."messages"3FOR SELECT4TO authenticated5USING ( true );Read Realtime Authorization to learn how to set up more specific policies.
Set up trigger function#
First, set up a trigger function that uses the realtime.broadcast_changes() function 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, you're going broadcast events to a topic named topic:<record_id>.
1CREATE OR REPLACE FUNCTION public.your_table_changes()2RETURNS trigger3SECURITY DEFINER SET search_path = ''4AS $$5BEGIN6 PERFORM realtime.broadcast_changes(7 'topic:' || NEW.id::text, -- topic8 TG_OP, -- event9 TG_OP, -- operation10 TG_TABLE_NAME, -- table11 TG_TABLE_SCHEMA, -- schema12 NEW, -- new record13 OLD -- old record14 );15 RETURN NULL;16END;17$$ LANGUAGE plpgsql;The Postgres native trigger special variables used are:
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.
1CREATE TRIGGER broadcast_changes_for_your_table_trigger2AFTER INSERT OR UPDATE OR DELETE ON public.your_table3FOR EACH ROW4EXECUTE 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.
1const gameId = 'id'2await supabase.realtime.setAuth() // Needed for Realtime Authorization3const changes = supabase4 .channel(`topic:${gameId}`)5 .on('broadcast', { event: 'INSERT' }, (payload) => console.log(payload))6 .on('broadcast', { event: 'UPDATE' }, (payload) => console.log(payload))7 .on('broadcast', { event: 'DELETE' }, (payload) => console.log(payload))8 .subscribe()Broadcast replay#
This feature is currently in Public Alpha. If you have any issues submit a support ticket.
How it works#
Broadcast Replay enables private channels to access messages that were sent earlier. Only messages published via Broadcast From the Database are available for replay.
You can configure replay with the following options:
since(Required): The epoch timestamp in milliseconds (for example,1697472000000), specifying the earliest point from which messages should be retrieved.limit(Optional): The number of messages to return. This must be a positive integer, with a maximum value of 25.
This is currently available only in the Supabase JavaScript client version 2.74.0 and later.
1const config = {2 private: true,3 broadcast: {4 replay: {5 since: 1697472000000, // Unix timestamp in milliseconds6 limit: 107 }8 }9}10const channel = supabase.channel('main:room', { config })1112// Broadcast callback receives meta field13channel.on('broadcast', { event: 'position' }, (payload) => {14 if (payload?.meta?.replayed) {15 console.log('Replayed message: ', payload)16 } else {17 console.log('This is a new message', payload)18 }19 // ...20})21.subscribe()When to use Broadcast replay#
A few common use cases for Broadcast Replay include:
- Displaying the most recent messages from a chat room
- Loading the last events that happened during a sports event
- Ensuring users always see the latest events after a page reload or network interruption
- Highlighting the most recent sections that changed in a web page