Real-time
CellCMS provides real-time features via WebSocket: presence indicators (see who's editing), live query subscriptions (get notified when content changes), and mutation broadcasts.
Connecting
Connect to the WebSocket endpoint with a JWT token and dataset:
wss://api.cellcms.com/ws?token=YOUR_JWT&dataset=production
Welcome Message
On successful connection, the server sends a welcome message:
{
"type": "welcome",
"clientId": "unique-client-id",
"userId": "user-uuid"
}
Presence Snapshot
Immediately after the welcome, you receive a snapshot of all users currently editing documents:
{
"type": "presence.snapshot",
"documents": [
{
"documentId": "doc-uuid-1",
"users": [
{ "userId": "user-1", "userName": "Alice" },
{ "userId": "user-2", "userName": "Bob" }
]
}
]
}
Authentication Errors
If the token is missing or invalid, the connection is closed with code 4001.
Presence
Presence tracking shows which users are actively editing which documents. This powers the avatar indicators in the Studio editor.
Join a Document
When a user starts editing a document, send:
{
"type": "presence.join",
"documentId": "doc-uuid"
}
All other connected clients receive:
{
"type": "presence.join",
"documentId": "doc-uuid",
"userId": "user-uuid",
"userName": "Alice"
}
Leave a Document
When a user stops editing:
{
"type": "presence.leave",
"documentId": "doc-uuid"
}
All other clients receive:
{
"type": "presence.leave",
"documentId": "doc-uuid",
"userId": "user-uuid",
"userName": "Alice"
}
Automatic Cleanup
When a WebSocket connection is closed (user closes the tab, network loss, etc.), the server automatically removes that user from all document presence lists and broadcasts the appropriate leave messages.
Live Query Subscriptions
Subscribe to real-time updates matching a GROQ query pattern. When documents matching your subscription are created, updated, or deleted, you receive a notification.
Subscribe
{
"type": "subscribe",
"query": "*[_type == \"post\"]",
"dataset": "production"
}
Server confirms:
{
"type": "subscribed",
"query": "*[_type == \"post\"]"
}
Mutation Events
When a matching document changes, all subscribers receive:
{
"type": "mutation",
"documentId": "doc-uuid",
"documentType": "post",
"operation": "update",
"ts": "2025-01-15T12:00:00.000Z"
}
Operations: create, update, delete, publish, unpublish
Document Updates
When a document you're tracking via presence is modified:
{
"type": "document.updated",
"documentId": "doc-uuid",
"operation": "update",
"ts": "2025-01-15T12:00:00.000Z"
}
Unsubscribe
{
"type": "unsubscribe",
"query": "*[_type == \"post\"]"
}
Server confirms:
{
"type": "unsubscribed",
"query": "*[_type == \"post\"]"
}
Client SDK Usage
The @cellcms/client SDK provides a listen() method for subscribing to real-time updates:
import { createClient } from '@cellcms/client'
const client = createClient({
apiUrl: 'https://api.cellcms.com',
project: 'default',
dataset: 'production',
token: 'YOUR_TOKEN',
})
// Listen for changes to all blog posts
const subscription = client.listen('*[_type == "post"]')
subscription.subscribe((event) => {
console.log('Document changed:', event.documentId)
console.log('Operation:', event.operation) // create, update, delete, publish, unpublish
console.log('Timestamp:', event.ts)
// Re-fetch the updated data
// ...
})
Event Type
interface ListenEvent {
type: 'mutation'
documentId: string
operation: 'create' | 'update' | 'delete' | 'publish' | 'unpublish'
ts: string
}
Message Reference
Client → Server
| Message Type | Fields | Description |
|---|---|---|
presence.join | documentId | Start tracking presence on a document |
presence.leave | documentId | Stop tracking presence on a document |
subscribe | query, dataset? | Subscribe to mutation events |
unsubscribe | query | Unsubscribe from mutation events |
Server → Client
| Message Type | Fields | Description |
|---|---|---|
welcome | clientId, userId | Connection established |
presence.snapshot | documents[] | Current presence state for all documents |
presence.join | documentId, userId, userName | User started editing |
presence.leave | documentId, userId, userName | User stopped editing |
subscribed | query | Subscription confirmed |
unsubscribed | query | Unsubscription confirmed |
mutation | documentId, documentType, operation, ts | Document changed |
document.updated | documentId, operation, ts | Tracked document changed |
Dataset Isolation
WebSocket connections are scoped to a single dataset (specified in the connection URL). Presence and mutation events from other datasets are not visible.
Studio Real-time Features
The Studio uses WebSocket for:
- Presence avatars: Colored circles in the editor toolbar showing who else is editing the current document
- Connection indicator: Green dot in the top bar when WebSocket is connected
- Live updates: Document list refreshes automatically when documents are created, updated, or deleted by other users
Related Documentation
- Client SDK —
listen()method reference - API Reference — REST endpoints
- Studio Guide — Real-time features in the editor