You’ll build chat rooms: people open a room, see the recent conversation, watch new messages arrive live, see who else is there, and see when someone is typing. Rooms are private to their members. You’ll use: Realtime channels, presence, history, channel rules. Before you start: have sign-in working, as in An app with signed-in users.

How it works

A channel is a name, like room:design. Everyone subscribed receives what’s sent to it. A channel rule matches a pattern of names (room:*) and decides who may join and send, whether presence is tracked, and how many messages are kept for latecomers. Channel history is a backlog for context. With the rule’s Kind set to Durable, kept messages also survive a restart, for the rule’s Retention. It still isn’t a searchable archive. If messages must be kept forever (searchable, exportable), also save them to a table; see Variation: permanent messages.

1. Create the channel rule

Realtime → Channels → New channel rule:

2. Open a room

src/room.ts
Asking for the same channel name twice returns the same channel object, so separate components share one subscription.

3. Send messages

Each message arrives as { id, channel, event, payload, from, sent_at }. from is the sender’s user id (or "server" for your backend), filled in by Pawabase, so it can’t be faked. Show payload.name, but trust from.

4. Who’s online

One person on two devices appears twice in members, with different presence_refs; group by user_id. Entries disappear when someone leaves or their connection drops.

5. Typing indicators

Typing is frequent and disposable, so send it as a message instead of presence, and don’t keep it:
Throttling to one every two seconds stays well under the rule’s publish rate limit.

6. Make rooms private to their members

Keep a room_members table (room_id, user_id) and check membership in the rule. Channel rules read the caller and the channel name, not other tables, so give each member the channel through their token, or put the members in the channel’s name. The simplest secure pattern uses the person’s id in the channel name for direct messages, and an organization for team rooms: A placeholder like {{ auth.org }} counts as a wildcard when deciding which rule governs a channel, and must equal the caller’s own value to let them in: someone at Globex asking for org:acme:room:design is governed by this rule and refused, rather than falling through to a looser default. See Channel rules and Rules on realtime. For rooms whose members are a list you manage yourself, publish messages from a flow that checks membership, and let clients only subscribe.

Variation: permanent messages

To keep every message, write it to a messages table, and let the table broadcast it:
  1. Create messages (room_id, author_id as Owner field, text) with suitable rules.
  2. Turn on Broadcast changes in the table’s settings.
  3. Send with pawabase.from("messages").create({ room_id, text }), and listen with record changes.
Now history is a normal query with paging and search, and the record rules decide who sees what.

Test it

Open two browsers signed in as different people, join the same room, and check: messages appear in both, the online list shows both, typing shows on the other side, and reloading shows the last messages. In Activity → Realtime, watch policy errors while you try a room you shouldn’t reach.

Troubleshooting

Channels

The full channel API.

Channel rules

Patterns, kinds and limits.