Send live messages and presence with the JavaScript SDK
The everyday flows of `aura.realtime`, with the behaviour of each call as observed against a running stack.
1. Channels and their status
A channel is a named room. Clients that open the same name receive each other's messages. The client connects on its own when you subscribe.
Send only after SUBSCRIBED. Before that, or after a refusal, send() returns a status such as not_connected or requires_subscription instead of sending. Channels are scoped to your project: two projects can use the same name without seeing each other.
2. Broadcast messages
In a real run, a second signed-in client received the message in under two seconds.
The message you receive has the event name added to payload: { "event": "ping", "n": 1, … }. A payload that has its own event key is refused (channel.send returns 'invalid_payload', httpBroadcast returns realtime_payload_event_reserved): rename the key, for example kind.
- You do not receive your own messages by default. Open the channel with
{ config: { broadcast: { self: true } } }to receive them. - A message carries
id,topic,timestamp,eventandpayload.
3. Presence
Presence shows who is connected to a channel, with a state you choose.
presenceState() is keyed by user id. Each entry lists the user's connections:
untrack() removes you from the list, and leaving the channel or closing the page does it for you.
4. Database changes
postgres_changes works on projects with their own database. Enable each table first: PUT /v1/db/schema/tables/{table}/realtime (or aura db table realtime <table> on) with the body {"enabled": true} and a service_role key. Listening starts when the first client subscribes and stops a few minutes after the last one leaves. Delivery is at least once: after a server restart a change can arrive twice, so make your handler idempotent, for example by keying on the row's primary key. A busy cluster can refuse a new listener with a capacity error; retry shortly. Any channel name works: each filter's table selects the table to listen to, and one channel can listen to several tables. On a channel that is not named db:<schema>.<table>, every filter needs a table. The first subscriber can receive up to 5 seconds of changes made just before it subscribed. Changes are kept one hour for recovery, then deleted. When the cluster is full, the subscription fails with capacité d'écoute postgres_changes atteinte pour la base de ce projet, réessayez. If listening stays down for more than a minute, the channel moves to CHANNEL_ERROR, then back to SUBSCRIBED once listening is restored; you do not need to subscribe again. If an outage outlasts the one-hour retention, the channel also receives CHANNEL_ERROR with changements postgres_changes perdus pendant l'interruption, resynchronisez: reload the data from the database. A change keeps the same id each time it is delivered, so you can also skip an id you have already handled. The row of a delete no longer exists, so row level security cannot be evaluated on it. On a table with row level security, a DELETE is therefore delivered only to subscribers whose role has a SELECT policy on the table (or bypasses RLS, like service_role): a visitor (anon) without a read policy does not receive it. On a table without row level security every subscriber receives it. payload.old of a DELETE holds only the primary key columns (nothing if the table has no primary key), and a filter on any other column never matches a delete. For an UPDATE, payload.old also holds only the primary key columns of the previous row (their previous values if the key changed), never its other values: row level security is checked on the new row only. The filter of an UPDATE applies to the new row. Keep sensitive information out of primary keys.
5. Clean up
Watch the connection with aura.realtime.onStateChange(callback) and onError(callback). After a drop, the client reconnects by itself unless you call setAutoReconnect(false).