a tiny multi-room chat showing off briven's reactive query primitives:
useQuery('listMessages', { roomId }) re-runs automatically whenever the
messages table changes for that room. no polling, no manual cache
invalidation, no websocket plumbing in your code.
realtime-chat/
├─ briven.json
├─ briven/
│ ├─ schema.ts two tables: rooms, messages
│ └─ functions/
│ ├─ listRooms.ts reactive · all rooms newest-first
│ ├─ listMessages.ts reactive · messages in a room, newest-first
│ ├─ createRoom.ts mutation · creates a room
│ └─ sendMessage.ts mutation · validates room exists, inserts
└─ README.md you are here
cp -r examples/realtime-chat my-chat
cd my-chat
briven link
briven deploy
# create a room and post a message
briven invoke createRoom --body '{"name":"general"}'
briven invoke sendMessage --body '{"roomId":"rm_<id>","authorName":"j","body":"hello"}'
briven invoke listMessages --body '{"roomId":"rm_<id>"}'
import { BrivenProvider, useQuery, useMutation } from '@briven/react';
function ChatRoom({ roomId, me }: { roomId: string; me: string }) {
const { data: messages = [] } = useQuery('listMessages', { roomId });
const send = useMutation('sendMessage');
const [draft, setDraft] = useState('');
return (
<>
<ul>
{messages.map((m) => (
<li key={m.id}>
<strong>{m.authorName}:</strong> {m.body}
</li>
))}
</ul>
<form
onSubmit={async (e) => {
e.preventDefault();
if (!draft.trim()) return;
await send({ roomId, authorName: me, body: draft });
setDraft('');
}}
>
<input value={draft} onChange={(e) => setDraft(e.target.value)} />
<button type="submit">send</button>
</form>
</>
);
}
When sendMessage commits on the server, briven re-runs every active
listMessages query that read the touched row. Subscribers in every tab,
every device, get re-rendered without any extra code in the client.
sendMessage validates room exists before insert)pg_dump whenever you wantbefore (cursor) to listMessages and switch the
client to infinite scrolltyping ephemeral table or a presence channel via
the realtime websocketctx.auth.userId into sendMessage and reject anonymous
posts; reuse the same query function unchanged