WebSocket API
WebSocket API
Cloudillo provides three WebSocket endpoints for real-time features: Bus (pub/sub messaging), RTDB (real-time database), and CRDT (collaborative documents).
Message Bus (/ws/bus)
The message bus provides pub/sub messaging, presence tracking, and notifications.
Connection
import * as cloudillo from '@cloudillo/base'
await cloudillo.init('my-app')
const bus = cloudillo.openMessageBus({
channels: ['notifications', 'presence']
})Protocol
Client → Server:
// Subscribe to channel
{
type: 'subscribe',
channel: 'notifications'
}
// Unsubscribe
{
type: 'unsubscribe',
channel: 'notifications'
}
// Publish message
{
type: 'publish',
channel: 'notifications',
data: {
event: 'new-post',
actionId: 'act_123'
}
}
// Set presence
{
type: 'presence',
status: 'online',
data: {
currentPage: '/posts'
}
}Server → Client:
// Message received
{
type: 'message',
channel: 'notifications',
data: {
event: 'new-post',
actionId: 'act_123'
},
from: 'alice@example.com',
timestamp: 1735000000
}
// Presence update
{
type: 'presence',
userId: 'bob@example.com',
status: 'online',
data: {...}
}
// Subscription confirmed
{
type: 'subscribed',
channel: 'notifications'
}Usage Example
const bus = cloudillo.openMessageBus()
// Subscribe to notifications
bus.subscribe('notifications', (message) => {
console.log('Notification:', message)
showToast(message.data)
})
// Publish typing indicator
bus.publish('typing', {
conversationId: 'conv_123',
typing: true
})
// Set presence
bus.setPresence('online', {
currentPage: window.location.pathname
})
// Listen for presence changes
bus.on('presence', (update) => {
console.log(`${update.userId} is ${update.status}`)
})Real-Time Database (/ws/rtdb/:fileId)
Real-time synchronization of structured data. See RTDB for full documentation.
Connection
import { RtdbClient } from '@cloudillo/rtdb'
const rtdb = new RtdbClient({
fileId: 'my-database',
token: cloudillo.accessToken,
url: 'wss://server.com/ws/rtdb'
})Protocol
Client → Server:
// Subscribe to collection
{
type: 'subscribe',
collection: 'todos',
query: {
where: [['completed', '==', false]],
orderBy: [['createdAt', 'desc']],
limit: 20
}
}
// Create document
{
type: 'create',
collection: 'todos',
data: {
title: 'Learn Cloudillo',
completed: false
}
}
// Update document
{
type: 'update',
collection: 'todos',
id: 'todo_123',
data: {
completed: true
}
}
// Delete document
{
type: 'delete',
collection: 'todos',
id: 'todo_123'
}Server → Client:
// Snapshot update
{
type: 'snapshot',
collection: 'todos',
data: [
{ id: 'todo_123', title: 'Learn Cloudillo', completed: false },
{ id: 'todo_456', title: 'Build app', completed: false }
]
}
// Document created
{
type: 'created',
collection: 'todos',
data: { id: 'todo_789', title: 'New todo', completed: false }
}
// Document updated
{
type: 'updated',
collection: 'todos',
id: 'todo_123',
data: { completed: true }
}
// Document deleted
{
type: 'deleted',
collection: 'todos',
id: 'todo_123'
}Collaborative Documents (/ws/crdt/:docId)
CRDT synchronization using Yjs protocol. See CRDT for full documentation.
Connection
import * as Y from 'yjs'
import * as cloudillo from '@cloudillo/base'
const yDoc = new Y.Doc()
const { provider } = await cloudillo.openYDoc(yDoc, 'my-document')Protocol
The CRDT endpoint uses the y-websocket protocol:
Binary messages:
- Sync Step 1: Client sends document state
- Sync Step 2: Server responds with missing updates
- Updates: Incremental document changes
- Awareness: Cursor positions, selections, user info
Awareness format:
{
user: {
name: 'Alice Johnson',
idTag: 'alice@example.com',
color: '#ff6b6b'
},
cursor: {
line: 10,
column: 5
},
selection: {
start: { line: 10, column: 5 },
end: { line: 10, column: 10 }
}
}Usage Example
const yDoc = new Y.Doc()
const { provider } = await cloudillo.openYDoc(yDoc, 'doc_123')
// Use shared types
const yText = yDoc.getText('content')
yText.insert(0, 'Hello, collaborative world!')
// Listen for remote changes
yText.observe((event) => {
console.log('Text changed by:', event.transaction.origin)
})
// Awareness (see other users)
provider.awareness.on('change', () => {
const states = provider.awareness.getStates()
states.forEach((state, clientId) => {
console.log(`User ${state.user.name} at cursor ${state.cursor}`)
})
})
// Set own awareness
provider.awareness.setLocalState({
user: {
name: cloudillo.name,
idTag: cloudillo.idTag,
color: '#' + Math.random().toString(16).slice(2, 8)
},
cursor: { line: 5, column: 10 }
})Authentication
All WebSocket connections require authentication via query parameter:
wss://server.com/ws/bus?token=eyJhbGc...
wss://server.com/ws/rtdb/file_123?token=eyJhbGc...
wss://server.com/ws/crdt/doc_123?token=eyJhbGc...The client libraries handle this automatically.
Reconnection
All WebSocket connections implement automatic reconnection with exponential backoff:
- Initial retry: 1 second
- Max retry delay: 30 seconds
- Exponential factor: 1.5
Handling reconnection:
provider.on('status', ({ status }) => {
if (status === 'connected') {
console.log('Connected to server')
} else if (status === 'disconnected') {
console.log('Disconnected, will retry...')
}
})Best Practices
1. Clean Up Connections
// React example
useEffect(() => {
const bus = cloudillo.openMessageBus()
bus.subscribe('notifications', handleNotification)
return () => {
bus.close() // Clean up on unmount
}
}, [])2. Handle Connection State
const [connected, setConnected] = useState(false)
provider.on('status', ({ status }) => {
setConnected(status === 'connected')
})
// Show offline indicator
{!connected && <div className="offline-banner">Reconnecting...</div>}3. Batch Updates
// ❌ Don't send updates individually
for (let i = 0; i < 100; i++) {
yText.insert(i, 'x')
}
// ✅ Batch in a transaction
yDoc.transact(() => {
for (let i = 0; i < 100; i++) {
yText.insert(i, 'x')
}
})See Also
- RTDB - Real-time database documentation
- CRDT - Collaborative editing documentation
- Authentication - WebSocket authentication