Rooms
A room is a name the app joins to get its events: order:1042, chat:34:42, shop. Your server declares your rooms once, by pattern, with what each keeps and allows, and answers for each user who asks to join (rooms in depth, with Node, PHP and Python side by side):
- Node
- PHP
- Python
realtime.room( 'order:{id}', { history: 20 }, async ( user, { id } ) => ( await findOrder( id ) )?.userId === user );
$realtime->room( 'order:{id}', fn ( $user, array $p ) => find_order( $p['id'] )?->user_id === $user, [ 'history' => 20 ] );
realtime.room("order:{id}", lambda user, p: (order := find_order(p["id"])) is not None and order.user_id == user, history=20)
The app asks your auth route for every room it joins in the same moment in one request, with the headers your RealtimeOptions give, and keeps each token until 10 minutes before it expires.
Joining
let order = rt.join("order:1042")
order.on("updated") { data, meta in
status = data["status"]?.string ?? status // meta: from, id, at, replayed
}
order.on("joined") { _, _ in live = true }
order.on("error") { error, _ in print(error["code"]?.string ?? "") } // forbidden, no_rule, full, expired …
order.state // .joining, .joined, .failed, .left
try await order.ready() // waits until joined, throws its error
rt.leave("order:1042") // or order.leave()
joingives the same room for the same name, andrt.room(name)the one joined, or nil.- Events reach a handler opened: you never handle sealed values. Data is a
JSONValue(data["status"]?.string,.number,.bool,.array,.object,.datafor binary). meta.fromis the user who sent it from a page or an app, as your server vouched for them, or nil for your server.meta.idandmeta.atare the kept event id and time.- The events of the room itself:
joined,joinandleave(the members of a presence room),count({ n }),error({ code }) andkicked. Nobody can send them. onAnyhears every event of the room,(event, data, meta).off(listener)removes a handler.
As async sequences, decoded
struct OrderUpdate: Decodable { let status: String }
for await (update, meta) in order.events("updated", as: OrderUpdate.self) {
status = update.status
}
events(_:) gives the JSONValues. events(_:as:) decodes each into your type, and skips one that does not decode. The sequence ends when its task is cancelled, and its handler with it.
History, and nothing lost on a reconnect
A room keeps its events for 30 minutes at most, for recovery. Your app keeps its own records, such as the messages of a chat, from the webhook.
- A reconnect gets what it missed: back within the history span, the app gets the events it missed first, in order, with
meta.replayed, then the new ones. That holds after a drop, a move between servers, and the app coming back from the background. - Paging back, oldest first, at most 100 a page:
let page = try await chat.history(limit: 50) // the newest 50
let older = try await chat.history(before: page.events.first?.meta.id, limit: 50)
for event in page.events { show(event.event, event.data, event.meta) } // page.more: there is older
history throws no_history for a room that keeps none.
Rooms anyone may join
A room your server declares public may be joined signed in or not: config() gives the app its grants (grants in RealtimeOptions), so it joins with no request to your server.