Subscriptions
Subscriptions control which paths a client materializes and receives during sync. They are a visibility/bandwidth mechanism, not an authority mechanism.Why subscriptions exist
Large rooms often contain more state than one client view needs. Subscriptions help by:- Reducing client-side materialization scope
- Reducing relayed traffic for non-matching paths
- Keeping UI selectors focused on relevant data
Initial subscription
Set subscription patterns at document creation:["**"]) when no subscription is provided.
Runtime subscription updates
Update patterns dynamically:Path checks
Use helper checks before expensive reads:Glob semantics (practical)
Typical pattern behavior:**matches everythingfoo/**matchesfooand descendants*matches one path segment
What subscriptions affect
Subscriptions affect client materialization and relay filtering behavior. They do not replace write authority enforcement. Authority remains governed by policy/capability rules.Design pattern: route-scoped subscriptions
In UI apps:- Map each route/view to a small set of patterns
- Apply
doc.subscribe(...)on route activation - Expand only when user workflows demand wider context
Debugging subscription issues
If expected updates are missing:- Confirm path actually matches active patterns
- Check runtime subscription updates are applied
- Verify view code is not reading outside subscribed scope
- Confirm issue is visibility-scope related, not authority rejection
Common mistakes
- Treating subscription as access control
- Leaving stale narrow patterns active after navigation
- Using complex overlapping patterns without tests
- Assuming
doc.onChangealways implies subscribed materialized changes
Runtime SDK event subscriptions
The runtime SDK (createNodalMergeSdk) uses a separate event subscription model via sdk.on(event, handler). These are process-level lifecycle and protocol events, not path-pattern subscriptions.
NodalMergeSdkEvent reference
| Event | Payload | When it fires |
|---|---|---|
"connected" | none | WebSocket session opened and welcome received |
"disconnected" | none | WebSocket connection closed (clean or error) |
"reconnect" | { attempt: number } | Reconnect attempt starting (with backoff) |
"state" | { state: "connecting" | "connected" | "disconnected" } | Any connection state transition |
"message" | raw WebSocket message string | Every inbound WebSocket frame (low-level) |
"error" | { msg: string } | Server-sent error envelope received |
"presence" | presence envelope object | Presence update received from server |
"signal" | WebRTC signal envelope | webrtc-offer, webrtc-answer, or webrtc-ice received |
"runtime-message" | NodalMergeRuntimeMessage | Any parsed server message that passes parseRuntimeMessage |
"transport" | { active: "ws-only" | "ws+webrtc" } | Active transport layer changes |
Common patterns
Connection lifecycle:NodalMergeRuntimeMessage.type values are exhaustively typed in NodalMergeRuntimeMessageType. Use a switch or type-narrowed handler rather than string comparison against arbitrary values.