Files
supabase/apps/docs/content/guides/realtime/broadcast.mdx
Andrew Smith a2342c4207 docs(python): update realtime docs with async methods and notes (#36015)
* docs(python): update realtime docs with async methods and notes

* chore(docs): update with admonitions
2025-05-29 15:45:54 +00:00

694 lines
18 KiB
Plaintext

---
title: 'Broadcast'
subtitle: 'Send low-latency messages using the client libs, REST, or your Database.'
description: '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.
## Subscribe to messages
You can use the Supabase client libraries to receive Broadcast messages.
### Initialize the client
Go to your Supabase project's [API Settings](https://supabase.com/dashboard/project/_/settings/api) and grab the `URL` and `anon` public API key.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
import { createClient } from '@supabase/supabase-js'
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_KEY = '<your-anon-key>'
const supabase = createClient(SUPABASE_URL, SUPABASE_KEY)
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
Supabase.initialize(
url: 'https://<project>.supabase.co',
anonKey: '<your-anon-key>',
);
runApp(MyApp());
}
final supabase = Supabase.instance.client;
```
</TabPanel>
<TabPanel id="swift" label="Swift">
```swift
import Supabase
let SUPABASE_URL = "https://<project>.supabase.co"
let SUPABASE_KEY = "<your-anon-key>"
let supabase = SupabaseClient(supabaseURL: URL(string: SUPABASE_URL)!, supabaseKey: SUPABASE_KEY)
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val supabaseUrl = "https://<project>.supabase.co"
val supabaseKey = "<your-anon-key>"
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
install(Realtime)
}
```
</TabPanel>
<TabPanel id="python" label="Python">
```python
import asyncio
from supabase import acreate_client
URL = "https://<project>.supabase.co"
KEY = "<your-anon-key>"
async def create_supabase():
supabase = await acreate_client(URL, KEY)
return supabase
```
</TabPanel>
</Tabs>
### Receiving Broadcast messages
You can provide a callback for the `broadcast` channel to receive message. This example will receive any `broadcast` messages that are sent to `test-channel`:
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
{/* prettier-ignore */}
```js
// @noImplicitAny: false
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://<project>.supabase.co', '<your-anon-key>')
// ---cut---
// Join a room/topic. Can be anything except for 'realtime'.
const myChannel = supabase.channel('test-channel')
// Simple function to log any messages we receive
function messageReceived(payload) {
console.log(payload)
}
// Subscribe to the Channel
myChannel
.on(
'broadcast',
{ event: 'shout' }, // Listen for "shout". Can be "*" to listen to all events
(payload) => messageReceived(payload)
)
.subscribe()
```
</TabPanel>
<TabPanel id="dart" label="Dart">
{/* prettier-ignore */}
```dart
final myChannel = supabase.channel('test-channel');
// Simple function to log any messages we receive
void messageReceived(payload) {
print(payload);
}
// Subscribe to the Channel
myChannel
.onBroadcast(
event: 'shout', // Listen for "shout". Can be "*" to listen to all events
callback: (payload) => messageReceived(payload)
)
.subscribe();
```
</TabPanel>
<TabPanel id="swift" label="Swift">
```swift
let myChannel = await supabase.channel("test-channel")
// Listen for broadcast messages
let broadcastStream = await myChannel.broadcast(event: "shout") // Listen for "shout". Can be "*" to listen to all events
await myChannel.subscribe()
for await event in broadcastStream {
print(event)
}
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
{/* prettier-ignore */}
```kotlin
val myChannel = supabase.channel("test-channel")
/ Listen for broadcast messages
val broadcastFlow: Flow<JsonObject> = myChannel
.broadcastFlow<JsonObject>("shout") // Listen for "shout". Can be "*" to listen to all events
.onEach { println(it) }
.launchIn(yourCoroutineScope) // you can also use .collect { } here
myChannel.subscribe()
```
</TabPanel>
<TabPanel id="python" label="Python">
<Admonition type="note">
In the following Realtime examples, certain methods are awaited. These should be enclosed within an `async` function.
</Admonition>
{/* prettier-ignore */}
```python
# Join a room/topic. Can be anything except for 'realtime'.
my_channel = supabase.channel('test-channel')
# Simple function to log any messages we receive
def message_received(payload):
print(f"Broadcast received: {payload}")
# Subscribe to the Channel
await my_channel
.on_broadcast('shout', message_received) # Listen for "shout". Can be "*" to listen to all events
.subscribe()
```
</TabPanel>
</Tabs>
## Send messages
### Broadcast using the client libraries
You can use the Supabase client libraries to send Broadcast messages.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
{/* prettier-ignore */}
```js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('your_project_url', 'your_supabase_api_key')
// ---cut---
const myChannel = supabase.channel('test-channel')
/**
* Sending a message before subscribing will use HTTP
*/
myChannel
.send({
type: 'broadcast',
event: 'shout',
payload: { message: 'Hi' },
})
.then((resp) => console.log(resp))
/**
* Sending a message after subscribing will use Websockets
*/
myChannel.subscribe((status) => {
if (status !== 'SUBSCRIBED') {
return null
}
myChannel.send({
type: 'broadcast',
event: 'shout',
payload: { message: 'Hi' },
})
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
{/* prettier-ignore */}
```dart
final myChannel = supabase.channel('test-channel');
// Sending a message before subscribing will use HTTP
final res = await myChannel.sendBroadcastMessage(
event: "shout",
payload: { 'message': 'Hi' },
);
print(res);
// Sending a message after subscribing will use Websockets
myChannel.subscribe((status, error) {
if (status != RealtimeSubscribeStatus.subscribed) {
return;
}
myChannel.sendBroadcastMessage(
event: 'shout',
payload: { 'message': 'hello, world' },
);
});
```
</TabPanel>
<TabPanel id="swift" label="Swift">
{/* prettier-ignore */}
```swift
let myChannel = await supabase.channel("test-channel") {
$0.broadcast.acknowledgeBroadcasts = true
}
// Sending a message before subscribing will use HTTP
await myChannel.broadcast(event: "shout", message: ["message": "HI"])
// Sending a message after subscribing will use Websockets
await myChannel.subscribe()
try await myChannel.broadcast(
event: "shout",
message: YourMessage(message: "hello, world!")
)
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.channel("test-channel") {
broadcast {
acknowledgeBroadcasts = true
}
}
// Sending a message before subscribing will use HTTP
myChannel.broadcast(event = "shout", buildJsonObject {
put("message", "Hi")
})
// Sending a message after subscribing will use Websockets
myChannel.subscribe(blockUntilSubscribed = true)
channelB.broadcast(
event = "shout",
payload = YourMessage(message = "hello, world!")
)
```
</TabPanel>
<TabPanel id="python" label="Python">
<Admonition type="note">
When an asynchronous method needs to be used within a synchronous context, such as the callback for `.subscribe()`, utilize `asyncio.create_task()` to schedule the coroutine. This is why the [initialize the client](#initialize-the-client) example includes an import of `asyncio`.
</Admonition>
{/* prettier-ignore */}
```python
my_channel = supabase.channel('test-channel')
# Sending a message after subscribing will use Websockets
def on_subscribe(status, err):
if status != RealtimeSubscribeStates.SUBSCRIBED:
return
asyncio.create_task(my_channel.send_broadcast(
'shout',
{ "message": 'hello, world' },
))
await my_channel.subscribe(on_subscribe)
```
</TabPanel>
</Tabs>
{/* supa-mdx-lint-disable-next-line Rule001HeadingCase */}
### Broadcast from the Database
<Admonition type="caution">
This feature is in Public Beta. [Submit a support ticket](https://supabase.help) if you have any issues.
</Admonition>
You can send messages directly from your database using the `realtime.send()` function:
{/* prettier-ignore */}
```sql
select
realtime.send(
jsonb_build_object('hello', 'world'), -- JSONB Payload
'event', -- Event name
'topic', -- Topic
false -- Public / Private flag
);
```
It's a common use case to broadcast messages when a record is created, updated, or deleted. We provide a helper function specific to this use case, `realtime.broadcast_changes()`. For more details, check out the [Subscribing to Database Changes](/docs/guides/realtime/subscribing-to-database-changes) guide.
### Broadcast using the REST API
You can send a Broadcast message by making an HTTP request to Realtime servers.
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="curl"
queryGroup="http"
>
<TabPanel id="curl" label="cURL">
{/* prettier-ignore */}
```bash
curl -v \
-H 'apikey: <SUPABASE_TOKEN>' \
-H 'Content-Type: application/json' \
--data-raw '{
"messages": [
{
"topic": "test",
"event": "event",
"payload": { "test": "test" }
}
]
}' \
'https://<PROJECT_REF>.supabase.co/realtime/v1/api/broadcast'
```
</TabPanel>
<TabPanel id="POST" label="POST">
{/* prettier-ignore */}
```bash
POST /realtime/v1/api/broadcast HTTP/1.1
Host: {PROJECT_REF}.supabase.co
Content-Type: application/json
apikey: {SUPABASE_TOKEN}
{
"messages": [
{
"topic": "test",
"event": "event",
"payload": {
"test": "test"
}
}
]
}
```
</TabPanel>
</Tabs>
## Broadcast options
You can pass configuration options while initializing the Supabase Client.
### Self-send messages
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
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`.
{/* prettier-ignore */}
```js
const myChannel = supabase.channel('room-2', {
config: {
broadcast: { self: true },
},
})
myChannel.on(
'broadcast',
{ event: 'test-my-messages' },
(payload) => console.log(payload)
)
myChannel.subscribe((status) => {
if (status !== 'SUBSCRIBED') { return }
channelC.send({
type: 'broadcast',
event: 'test-my-messages',
payload: { message: 'talking to myself' },
})
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
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`.
```dart
final myChannel = supabase.channel(
'room-2',
opts: const RealtimeChannelConfig(
self: true,
),
);
myChannel.onBroadcast(
event: 'test-my-messages',
callback: (payload) => print(payload),
);
myChannel.subscribe((status, error) {
if (status != RealtimeSubscribeStatus.subscribed) return;
// channelC.send({
myChannel.sendBroadcastMessage(
event: 'test-my-messages',
payload: {'message': 'talking to myself'},
);
});
```
</TabPanel>
<TabPanel id="swift" label="Swift">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`.
```swift
let myChannel = await supabase.channel("room-2") {
$0.broadcast.receiveOwnBroadcasts = true
}
let broadcastStream = await myChannel.broadcast(event: "test-my-messages")
await myChannel.subscribe()
try await myChannel.broadcast(
event: "test-my-messages",
payload: YourMessage(
message: "talking to myself"
)
)
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`.
```kotlin
val myChannel = supabase.channel("room-2") {
broadcast {
receiveOwnBroadcasts = true
}
}
val broadcastFlow: Flow<JsonObject> = myChannel.broadcastFlow<JsonObject>("test-my-messages")
.onEach {
println(it)
}
.launchIn(yourCoroutineScope)
myChannel.subscribe(blockUntilSubscribed = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.broadcast(
event = "test-my-messages",
payload = YourMessage(
message = "talking to myself"
)
)
```
</TabPanel>
<TabPanel id="python" label="Python">
<Admonition type="note">
When an asynchronous method needs to be used within a synchronous context, such as the callback for `.subscribe()`, utilize `asyncio.create_task()` to schedule the coroutine. This is why the [initialize the client](#initialize-the-client) example includes an import of `asyncio`.
</Admonition>
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`.
```python
# Join a room/topic. Can be anything except for 'realtime'.
my_channel = supabase.channel('room-2', {"config": {"broadcast": {"self": True}}})
my_channel.on_broadcast(
'test-my-messages',
lambda payload: print(payload)
)
def on_subscribe(status, err):
if status != RealtimeSubscribeStates.SUBSCRIBED:
return
# Send a message once the client is subscribed
asyncio.create_task(channel_b.send_broadcast(
'test-my-messages',
{ "message": 'talking to myself' },
))
my_channel.subscribe(on_subscribe)
```
</TabPanel>
</Tabs>
### Acknowledge messages
<Tabs
scrollable
size="small"
type="underlined"
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
You can confirm that the Realtime servers have received your message by setting Broadcast's `ack` config to `true`.
{/* prettier-ignore */}
```js
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('your_project_url', 'your_supabase_api_key')
// ---cut---
const myChannel = supabase.channel('room-3', {
config: {
broadcast: { ack: true },
},
})
myChannel.subscribe(async (status) => {
if (status !== 'SUBSCRIBED') { return }
const serverResponse = await myChannel.send({
type: 'broadcast',
event: 'acknowledge',
payload: {},
})
console.log('serverResponse', serverResponse)
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
final myChannel = supabase.channel('room-3',opts: const RealtimeChannelConfig(
ack: true,
),
);
myChannel.subscribe( (status, error) async {
if (status != RealtimeSubscribeStatus.subscribed) return;
final serverResponse = await myChannel.sendBroadcastMessage(
event: 'acknowledge',
payload: {},
);
print('serverResponse: $serverResponse');
});
```
</TabPanel>
<TabPanel id="swift" label="Swift">
You can confirm that Realtime received your message by setting Broadcast's `acknowledgeBroadcasts` config to `true`.
```swift
let myChannel = await supabase.channel("room-3") {
$0.broadcast.acknowledgeBroadcasts = true
}
await myChannel.subscribe()
await myChannel.broadcast(event: "acknowledge", message: [:])
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `acknowledgeBroadcasts` parameter to `true`.
```kotlin
val myChannel = supabase.channel("room-2") {
broadcast {
acknowledgeBroadcasts = true
}
}
myChannel.subscribe(blockUntilSubscribed = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.broadcast(event = "acknowledge", buildJsonObject { })
```
</TabPanel>
<TabPanel id="python" label="Python">
Unsupported in Python yet.
</TabPanel>
</Tabs>
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.