Room
EdgeBase Room is a server-authoritative real-time state synchronization primitive for multiplayer applications. Clients send actions, the server validates and mutates state, and all connected clients receive only the changed parts (deltas) automatically. Ideal for multiplayer games, collaborative editors, live dashboards, voting systems, and auctions — anything where consistency and trust matter.
What is Room?
Room is a real-time state synchronization primitive where the server has full authority over all state changes. Unlike traditional pub/sub (Realtime subscriptions, broadcast), Room doesn't just relay messages — it owns the state and enforces the rules.
Clients can only do two things: read state and send actions. The server validates every action, updates state, and automatically broadcasts only the changed parts (deltas) to connected clients.
This makes Room ideal for anything where consistency and trust matter:
- Multiplayer games — lobbies, turn-based, real-time
- Collaborative editors — shared documents, whiteboards
- Live dashboards — real-time metrics, leaderboards
- Voting / polls — tamper-proof state
- Auctions — server-enforced bidding rules
How is it different from Realtime?
| Realtime (pub/sub) | Room | |
|---|---|---|
| Who controls state? | Clients write directly | Server only |
| Validation | Client-side or access rules | Server-side onAction handlers |
| State | No built-in state | 3 state areas (shared / player / server) |
| Delta sync | Manual | Automatic — only changed fields are sent |
| Best for | Notifications, presence, chat | Games, collaboration, anything needing trust |
Choosing Room vs the Other Realtime Features
Choose Room only when the server must own state and validate client actions. If you mainly need database change feeds, online indicators, or lightweight pub/sub, use Realtime primitives instead. If you need delivery to devices outside the active app session, use Push.
For the full five-way comparison, see Choosing Between Subscriptions, Presence, Broadcast, Room, and Push.
How it Works
Client A ── send('INCREMENT') ──▶ Server
│ onAction['INCREMENT'] runs
│ setSharedState(s => ({ ...s, count: s.count + 1 }))
Client A ◀── action_result ─────────│
Client A ◀── shared_delta ──────────│
Client B ◀── shared_delta ──────────│ (auto-broadcast to all)- Client sends an action (type + payload)
- Server runs the matching onAction handler
- Handler mutates state via
setSharedState/setPlayerState/setServerState - Changed fields are automatically broadcast as deltas to connected clients
- The handler's return value is sent back to the calling client only
Quick Start
Step 1 — Server: Define a Room
// edgebase.config.ts
import { defineConfig } from '@edgebase/shared';
export default defineConfig({
rooms: {
'counter': {
onCreate(room) {
room.setSharedState(() => ({ count: 0 }));
},
onAction: {
INCREMENT: (payload, room, sender) => {
room.setSharedState(s => ({ ...s, count: (s.count as number) + 1 }));
return { newCount: room.getSharedState().count };
},
},
},
},
});
Step 2 — Client A: Connect & Subscribe
const room = client.room('counter', 'room-1');
await room.join();
room.onSharedState((state, changes) => {
console.log('Count:', state.count);
});
Step 3 — Client B: Connect to the Same Room
const room = client.room('counter', 'room-1');
await room.join();
room.onSharedState((state, changes) => {
document.getElementById('count')!.textContent = String(state.count);
});
Step 4 — Client A: Send an Action
const result = await room.send('INCREMENT');
console.log(result.newCount); // 1
Step 1's onAction.INCREMENT runs on the server. setSharedState triggers a delta broadcast to all connected clients — including Client B, whose onSharedState handler fires automatically.
Three State Areas
sharedState
All clients can read. Server writes only. Game board, scores, shared data.
playerState
Only the owning player can read. Server writes only. HP, inventory, private hand.
serverState
Server only — never sent to clients. RNG seed, anti-cheat, internal computation.
Client API Overview
| Method | Description |
|---|---|
room.join() | Connect, authenticate, join |
room.leave() | Disconnect |
room.send(type, payload) | Send action to server, returns Promise<result> |
room.getSharedState() | Read current shared state |
room.getPlayerState() | Read current player state |
room.onSharedState(handler) | Subscribe to shared state changes |
room.onPlayerState(handler) | Subscribe to player state changes |
room.onMessage(type, handler) | Subscribe to server messages |
room.onAnyMessage(handler) | Subscribe to all server messages |
room.onKicked(handler) | Kicked from room |
room.onError(handler) | Error occurred |
Server Lifecycle
| Hook | When | Notes |
|---|---|---|
onCreate(room, ctx) | First player joins (room created) | Initialize shared/server state |
onJoin(sender, room, ctx) | Each player joins | Initialize player state. Throw to reject. |
onAction[type](payload, room, sender, ctx) | Client calls send() | Process action, mutate state, return result |
onLeave(sender, room, ctx, reason) | Player leaves | reason: 'leave' | 'disconnect' | 'kicked' |
onDestroy(room, ctx) | Last player leaves | Persist final results via ctx.admin.db() |
No Other BaaS Has This
Room is not a feature you'll find in Firebase, Supabase, Appwrite, or any other BaaS. Server-authoritative real-time state synchronization has traditionally required a separate multiplayer framework — and a separate server to run it on.
| Colyseus / Hathora | PartyKit / Liveblocks | EdgeBase Room | |
|---|---|---|---|
| What it is | Standalone game server framework | Collaboration SDK | Built-in BaaS feature |
| Requires separate infra? | Yes — dedicated Node.js servers | Yes — separate deployment | No — runs inside your EdgeBase project |
| Auth integration | Build your own | Build your own | Uses EdgeBase Auth (JWT, access rules) |
| Database access | Separate DB needed | Separate DB needed | Direct ctx.admin.db() in handlers |
| Delta sync | Manual or schema-based | CRDT-based | Automatic diff-based deltas |
| 3-tier state | No (single state) | No | Yes — shared / player / server |
| Cost | Server hosting 24/7 | Per-connection pricing | DO duration only — idle = $0 |
Room gives you a multiplayer backend that shares the same auth, database, and deployment as the rest of your app — no additional infrastructure, no separate billing, no integration glue.