mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 07:09:07 +03:00
* docs(python): update realtime docs with async methods and notes * chore(docs): update with admonitions
694 lines
18 KiB
Plaintext
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.
|