Skip to main content
Markdown

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):

realtime.room( 'order:{id}', { history: 20 }, async ( user, { id } ) => ( await findOrder( id ) )?.userId === user );

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​

val order = rt.join("order:1042")

order.on("updated") { data, meta -> // meta: from, id, at, replayed
status = data.jsonObject["status"]?.jsonPrimitive?.content ?: status
}
order.on("joined") { _, _ -> live = true }
order.on("error") { error, _ -> Log.w("shop", error.toString()) } // { code: forbidden, no_rule, full, expired … }

order.state // RoomState.Joining, Joined, Failed, Left
order.ready() // suspends until joined, throws its error
rt.leave("order:1042") // or order.leave()
  • join gives the same room for the same name, and rt.room(name) the one joined, or null.
  • Events reach a handler opened: you never handle sealed values. Data is the kotlinx.serialization JsonElement.
  • meta.from is the user who sent it from a page or an app, as your server vouched for them, or null for your server. meta.id and meta.at are the kept event id and time.
  • The events of the room itself: joined, join and leave (the members of a presence room), count ({ n }), error ({ code }) and kicked. Nobody can send them.
  • onAny hears every event of the room, (event, data, meta). off(listener) removes a handler.

As flows, decoded​

@Serializable
data class OrderUpdate(val status: String)

viewModelScope.launch {
order.eventsOf<OrderUpdate>("updated").collect { (update, meta) -> status = update.status }
}

events(name) gives the JsonElements. eventsOf<T>(name) decodes each into your @Serializable type, leaving out fields it does not name, and skips one that does not decode. The handler goes when the flow collection ends.

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:
val page = chat.history(limit = 50)                                       // the newest 50
val older = chat.history(before = page.events.firstOrNull()?.meta?.id, limit = 50)
for (event in page.events) show(event.event, event.data, event.meta) // page.more: there is older

history throws RealtimeException("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.