Presence is a live membership set per channel. Each entry is a client id with an optional data payload, kept current by the service even when connections drop.
Watch presence
const off = channel.presence.on((event) => {
console.log(event.action, event.clientId, event.data);
});
Registering the first listener opens a presence watcher on the channel. The listener first receives one enter per current member, so you can build the initial roster from events alone, then live transitions as they happen. Removing the last listener closes the watcher, so idle channels cost nothing.
presence.subscribe is an alias of presence.on, and both accept an action filter:
channel.presence.on('leave', (event) => {
removeUser(event.clientId);
});
Enter, update, and leave
await channel.presence.enter({ name: 'Acorn', status: 'building' });
await channel.presence.update({ name: 'Acorn', status: 'idle' });
await channel.presence.leave();
enter announces this connection with an optional payload, update replaces the payload, and leave removes it. Closing the connection also removes the member after the service notices the drop.
Presence traffic bills as messages. Each enter, update, or leave counts one message in, and every watching client that receives the event counts one message out. That multiplies on channels where everyone watches everyone: 200 clients entering while 200 watch is about 40,000 messages. Keep presence channels reasonably sized, and see Usage and billing for the full accounting.
The presence event
| Field | Type | Meaning |
|---|---|---|
action |
'enter' | 'update' | 'leave' |
What changed |
clientId |
string |
The member’s client id |
connectionId |
string |
The member’s connection, since one client id can hold several |
data |
unknown? |
The payload from enter or update |
timestamp |
number |
When the transition happened, ms since epoch |
Reconnects
If this connection entered presence and then drops, the SDK re-enters automatically once it reconnects, with the last payload it sent. Other members see a leave only when the service gives up on the connection, so brief blips usually pass unnoticed.
Presence over REST
Fetch a point-in-time snapshot of the members without holding a WebSocket open:
import { Rest } from '@foony/realtime';
const rest = new Rest({ key: process.env.FOONY_API_KEY! });
const page = await rest.channels.get('chat:lobby').presence.get();
for (const member of page.items) {
console.log(member.clientId, member.data);
}
Capabilities
Entering presence requires the presence operation on the channel. Watching presence events only requires subscribe. See Auth and capabilities.