diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
index 8a0a19a4476..d882244fc55 100644
--- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
+++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts
@@ -1559,6 +1559,10 @@ export const realtime: NavMenuConstant = {
name: 'Listening to Postgres Changes with Flutter',
url: '/guides/realtime/realtime-listening-flutter',
},
+ {
+ name: 'Migrate to Broadcast Changes',
+ url: '/guides/realtime/migrate-from-postgres-changes',
+ },
],
},
{
diff --git a/apps/docs/content/guides/realtime/broadcast.mdx b/apps/docs/content/guides/realtime/broadcast.mdx
index e741947d227..6fa1482f7df 100644
--- a/apps/docs/content/guides/realtime/broadcast.mdx
+++ b/apps/docs/content/guides/realtime/broadcast.mdx
@@ -4,7 +4,7 @@ subtitle: 'Send and receive messages using Realtime Broadcast'
description: 'Send and receive messages using Realtime Broadcast'
---
-Let's explore how to implement Realtime Broadcast to send messages between clients.
+Let's explore how to implement Realtime Broadcast to send messages between clients using either WebSockets, REST API or triggers from your database.
## Usage
@@ -88,7 +88,7 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj
### Listening to broadcast messages
-You can provide a callback for the `broadcast` channel to receive message. In this example we will receive any `broadcast` messages in `room-1`:
+You can provide a callback for the `broadcast` channel to receive message. This example will receive any `broadcast` messages in `room-1`:
+
+## 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]/docs/guides/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.
+
+```sql
+SELECT realtime.send (
+ to_jsonb ('{}'::text), -- JSONB Payload
+ 'event', -- Event name
+ 'topic', -- Topic
+ FALSE -- Public / Private flag
+);
+```
+
+### 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:
+
+```sql
+CREATE POLICY "authenticated can receive broadcasts"
+ON "realtime"."messages"
+FOR SELECT
+TO authenticated
+USING ( true );
+
+```
+
+See the [Realtime Authorization](/docs/guides/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:` to which we're going to broadcast events.
+
+```sql
+CREATE OR REPLACE FUNCTION public.your_table_changes() RETURNS trigger AS $$
+BEGIN
+ PERFORM realtime.broadcast_changes(
+ 'topic:' || NEW.id::text, -- topic
+ TG_OP, -- event
+ TG_OP, -- operation
+ TG_TABLE_NAME, -- table
+ TG_TABLE_SCHEMA, -- schema
+ NEW, -- new record
+ OLD -- old record
+ );
+ RETURN NULL;
+END;
+$$ LANGUAGE plpgsql;
+```
+
+Of note are the Postgres native trigger special variables used:
+
+- `TG_OP` - the operation that triggered the function
+- `TG_TABLE_NAME` - the table that caused the trigger
+- `TG_TABLE_SCHEMA` - the schema of the table that caused the trigger invocation
+- `NEW` - the record after the change
+- `OLD` - the record before the change
+
+You can read more about them in this [guide](https://www.postgresql.org/docs/current/plpgsql-trigger.html#PLPGSQL-DML-TRIGGER).
+
+#### Set up trigger
+
+Next, set up a trigger so the function runs whenever your target table has a change.
+
+```sql
+CREATE TRIGGER broadcast_changes_for_your_table_trigger
+AFTER INSERT OR UPDATE OR DELETE ON public.your_table
+FOR EACH ROW
+EXECUTE 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:` to receive the events.
+
+```jsx
+const gameId = 'id'
+await supabase.realtime.setAuth() // Needed for Realtime Authorization
+const changes = supabase
+ .channel(`topic:${gameId}`)
+ .on('broadcast', { event: 'INSERT' }, (payload) => console.log(payload))
+ .on('broadcast', { event: 'UPDATE' }, (payload) => console.log(payload))
+ .on('broadcast', { event: 'DELETE' }, (payload) => console.log(payload))
+ .subscribe()
+```
diff --git a/apps/docs/content/guides/realtime/migrate-from-postgres-changes.mdx b/apps/docs/content/guides/realtime/migrate-from-postgres-changes.mdx
new file mode 100644
index 00000000000..dddc8e1dc15
--- /dev/null
+++ b/apps/docs/content/guides/realtime/migrate-from-postgres-changes.mdx
@@ -0,0 +1,118 @@
+---
+title: 'Migrate to Broadcast Changes'
+subtitle: 'How to migrate from Postgres Changes to Broadcast Changes'
+description: 'How to migrate from Postgres Changes to Broadcast Changes'
+sidebar_label: 'Migrate to Broadcast Changes'
+---
+
+Postgres Changes has some [limitations](/docs/guides/realtime/postgres-changes#limitations) as your application scales. To continue broadcasting database changes to users as you scale, you can use Broadcast Changes.
+
+## Example application using Postgres Changes
+
+Here we have a simple chess application that has a game id and we want to track whenever we have new moves happening for a given game id.
+
+We store this information in a `public.moves` table and every time a new move is added to a given `game_id` we want to receive the changes in our connected Realtime client
+
+
+
+In our client we will have our implementation to receive insert events with the usual code:
+
+```javascript
+const gameId = '4a8bbe89-f601-4414-bd47-8d0f7ab2a31a'
+const changes = supabase
+ .channel('chess-moves')
+ .on(
+ 'postgres_changes',
+ {
+ event: 'INSERT',
+ schema: 'public',
+ table: 'moves',
+ filter: `game_id=eq.${gameId}`,
+ },
+ (payload) => console.log(payload)
+ )
+ .subscribe()
+ ...
+```
+
+## Migrate to broadcast changes
+
+To use Broadcast Changes, first familiarize yourself with the [Broadcast Changes implementation](/docs/guides/realtime/broadcast#trigger-broadcast-messages-from-your-database).
+
+### Set up authorization
+
+Broadcast Changes is private by default, using [Realtime Authorization](/docs/guides/realtime/authorization) to control access. First, set up RLS policies to control user access to relevant messages:
+
+```sql
+CREATE POLICY "authenticated can listen to game moves"
+ON "realtime"."messages"
+FOR SELECT
+TO authenticated
+USING (
+ EXISTS (
+ SELECT 1
+ FROM game_users
+ WHERE (SELECT auth.uid()) = user_id
+ AND (select realtime.topic()) = 'games:' || game_id::text
+ AND realtime.messages.extension = 'broadcast'
+ )
+);
+```
+
+### Set up trigger function
+
+We need to define our trigger function to adapt to our use case and use the provided function `realtime.broadcast_changes`
+
+```sql
+CREATE OR REPLACE FUNCTION public.broadcast_moves() RETURNS trigger AS $$
+BEGIN
+ PERFORM realtime.broadcast_changes(
+ 'games:' || NEW.game_id::text, -- topic
+ TG_OP, -- event
+ TG_OP, -- operation
+ TG_TABLE_NAME, -- table
+ TG_TABLE_SCHEMA, -- schema
+ NEW, -- new record
+ OLD -- old record
+ );
+ RETURN NULL;
+END;
+$$ LANGUAGE plpgsql;
+```
+
+### Setup trigger with created function
+
+Now we need to setup our trigger to capture the events we want
+
+```sql
+CREATE TRIGGER chess_move_changes
+AFTER INSERT ON public.moves
+FOR EACH ROW
+EXECUTE FUNCTION public.broadcast_moves();
+```
+
+### **Listen to changes in client**
+
+Finally you can setup your client to listen for your events
+
+```js
+const gameId = '4a8bbe89-f601-4414-bd47-8d0f7ab2a31a'
+await supabase.realtime.setAuth() // Needed for Realtime Authorization
+const changes = supabase
+ .channel(`games:${gameId}`)
+ .on(
+ 'broadcast',
+ {
+ event: 'INSERT',
+ },
+ (payload) => console.log(payload)
+ )
+ .subscribe()
+```
diff --git a/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-dark.png b/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-dark.png
new file mode 100644
index 00000000000..acbdeb6aa99
Binary files /dev/null and b/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-dark.png differ
diff --git a/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-light.png b/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-light.png
new file mode 100644
index 00000000000..dfbe19be14e
Binary files /dev/null and b/apps/docs/public/img/guides/realtime/realtime-broadcast-changes-migration-schema-example-light.png differ