Aurabase Logo
aurabasedocs
docs›Guides›Realtime and presence (SDK)

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.

7 min read·Level intermediate·Revised on Oct 12, 2026
This English text was generated automatically from the French original and has not been reviewed yet.
#
Channels

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.

channel.tstypescript
const room = aura.realtime.channel('room-1')

room.subscribe((status) => {
  // 'SUBSCRIBED' | 'CHANNEL_ERROR' | 'TIMED_OUT' | 'CLOSED'
  if (status === 'SUBSCRIBED') console.log('ready')
})

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.

#
Broadcast

2. Broadcast messages

broadcast.tstypescript
// Receive
room.on('broadcast', { event: 'ping' }, (message) => {
  console.log(message.payload) // { event: 'ping', n: 1, text: 'hello' }
})

// Send, once SUBSCRIBED
const status = await room.send({ type: 'broadcast', event: 'ping', payload: { n: 1, text: 'hello' } })
// → 'ok'

In a real run, a second signed-in client received the message in under two seconds.

The payload carries an `event` key

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, event and payload.
#
Presence

3. Presence

Presence shows who is connected to a channel, with a state you choose.

presence.tstypescript
const lobby = aura.realtime.channel('lobby', { config: { presence: { key: user.id } } })

lobby.on('presence', { event: 'sync' }, () => {
  console.log(lobby.presenceState())
})
lobby.subscribe(async (status) => {
  if (status === 'SUBSCRIBED') await lobby.track({ name: 'Bob' })
})

presenceState() is keyed by user id. Each entry lists the user's connections:

presenceState()json
{
  "d6ba3994-…": [
    { "client_id": "936cb015-…", "user_id": "d6ba3994-…", "metadata": { "name": "Bob" },
      "joined_at": "2026-10-12T19:32:19.093Z", "last_seen": "2026-10-12T19:32:19.096Z" }
  ]
}

untrack() removes you from the list, and leaving the channel or closing the page does it for you.

#
Changes

4. Database changes

Projects with their own database

postgres_changes works on projects with their own database. Enable each table first: PUT /v1/db/{project_id}/schema/tables/{table}/realtime 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. Name the channel after the table, db:public.<table>: changes are published on that channel. 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; subscribe again once it is back. An outage longer than the one-hour retention loses the changes made during it.

changes.tstypescript
const changes = aura.realtime
  .channel('db:public.todos')
  .on('postgres_changes', { event: '*', schema: 'public', table: 'todos' }, (payload) => {
    console.log(payload.eventType, payload.new)
  })
  .subscribe((status) => {
    if (status === 'CHANNEL_ERROR') console.warn('database changes are not available for this project')
  })
#
Cleanup

5. Clean up

cleanup.tstypescript
room.unsubscribe()                 // leave one channel
aura.realtime.removeChannel(room)  // leave and forget it
aura.realtime.removeAllChannels()  // leave every channel
aura.realtime.disconnect()         // close the connection

Watch the connection with aura.realtime.onStateChange(callback) and onError(callback). After a drop, the client reconnects by itself unless you call setAutoReconnect(false).

Last updated · Oct 12, 2026