RealtimeAdapter
Interface for WebSocket-based real-time sync updates.
Import
import type { RealtimeAdapter } from '@gluonic/client'Interface Definition
Exact definition from implementation:
export type RealtimeAdapter = {
connect: (
token: string,
onFrames: (frames: SyncFrame[]) => void,
onError?: (e: any) => void,
onClose?: () => void
) => { close: () => void }
}Single method: connect() establishes WebSocket connection and returns close handler.
Methods
connect()
connect(
token: string,
onFrames: (frames: SyncFrame[]) => void,
onError?: (e: any) => void,
onClose?: () => void
): { close: () => void }Establish WebSocket connection for real-time updates.
Parameters:
| Parameter | Type | Description |
|---|---|---|
token | string | JWT auth token |
onFrames | (frames: SyncFrame[]) => void | Callback when frames received |
onError | (e: any) => void | Optional error callback |
onClose | () => void | Optional close callback |
Returns: { close: () => void } - Handler to close connection
Example:
const connection = realtimeAdapter.connect(
token,
(frames) => {
// Received real-time updates
store.applyFrames(frames)
},
(error) => {
console.error('WebSocket error:', error)
},
() => {
console.log('WebSocket closed, reconnecting...')
}
)
// Later: Close connection
connection.close()Default Implementation
Default WebSocket adapter:
const defaultRealtimeAdapter: RealtimeAdapter = {
connect(token, onFrames, onError, onClose) {
const ws = new WebSocket(`wss://api.example.com/sync/v1/ws`)
ws.onopen = () => {
// Send auth
ws.send(JSON.stringify({ type: 'auth', token }))
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'frames') {
onFrames(data.frames)
}
}
ws.onerror = (error) => {
onError?.(error)
}
ws.onclose = () => {
onClose?.()
}
return {
close: () => ws.close()
}
}
}Custom Implementation
With Heartbeat
import type { RealtimeAdapter } from '@gluonic/client'
class HeartbeatRealtimeAdapter implements RealtimeAdapter {
connect(token, onFrames, onError, onClose) {
const ws = new WebSocket('wss://api.example.com/sync/v1/ws')
let heartbeatTimer: any
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'auth', token }))
// Start heartbeat
heartbeatTimer = setInterval(() => {
ws.send(JSON.stringify({ type: 'ping' }))
}, 30000) // Every 30s
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'frames') {
onFrames(data.frames)
} else if (data.type === 'pong') {
// Heartbeat acknowledged
}
}
ws.onerror = (error) => {
clearInterval(heartbeatTimer)
onError?.(error)
}
ws.onclose = () => {
clearInterval(heartbeatTimer)
onClose?.()
}
return {
close: () => {
clearInterval(heartbeatTimer)
ws.close()
}
}
}
}With Reconnect
class ReconnectRealtimeAdapter implements RealtimeAdapter {
connect(token, onFrames, onError, onClose) {
let ws: WebSocket
let shouldReconnect = true
let reconnectAttempts = 0
const connect = () => {
ws = new WebSocket('wss://api.example.com/sync/v1/ws')
ws.onopen = () => {
reconnectAttempts = 0 // Reset on successful connection
ws.send(JSON.stringify({ type: 'auth', token }))
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'frames') {
onFrames(data.frames)
}
}
ws.onerror = (error) => {
onError?.(error)
}
ws.onclose = () => {
onClose?.()
if (shouldReconnect) {
reconnectAttempts++
const backoff = Math.min(Math.pow(2, reconnectAttempts) * 1000, 30000)
setTimeout(connect, backoff)
}
}
}
connect()
return {
close: () => {
shouldReconnect = false
ws.close()
}
}
}
}Usage in Gluonic
RealtimeAdapter is used by Store for:
Real-Time Updates
// Store manages WebSocket lifecycle
store.startRealtime() // Calls realtimeAdapter.connect()
// Frames received via WebSocket
ws.onmessage → onFrames(frames) → store.applyFrames(frames)
// Stop real-time
store.stopRealtime() // Calls connection.close()Token Changes
// SyncProvider manages real-time lifecycle
<SyncProvider token={token}>
// When token changes:
// 1. Close existing connection
// 2. Open new connection with new tokenFrame Delivery
Frames are pushed from server in real-time:
// Server broadcasts change
// WebSocket message:
{
type: 'frames',
frames: [
{ sid: 42, t: 'task', id: 'abc', op: 'u', p: { done: true } }
]
}
// Client receives and applies immediately
onFrames([frame]) → store.applyFrames([frame])Configuration
const client = SyncClient({
server: 'https://api.example.com/sync/v1',
storage,
models,
realtime: {
connect: (token, onFrames, onError, onClose) => {
// Custom WebSocket implementation
const ws = new WebSocket('wss://custom.com/ws')
ws.onmessage = (event) => {
const frames = parseCustomFormat(event.data)
onFrames(frames)
}
return { close: () => ws.close() }
}
}
})Note: If not provided, Gluonic creates default WebSocket adapter from server URL.
Error Handling
Authentication Errors
connect(token, onFrames, onError, onClose) {
const ws = new WebSocket('wss://api.example.com/sync/v1/ws')
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
if (data.type === 'error' && data.code === 401) {
// Server rejected auth
onError?.(new Error('Unauthenticated'))
ws.close()
}
}
}Network Errors
ws.onerror = (error) => {
onError?.(error)
// Connection will close, trigger onClose for reconnect
}Performance
Instant Updates
WebSocket delivers updates within milliseconds:
// User 1 creates task
await store.create('task', id, { title: 'New' })
// Server broadcasts via WebSocket
// User 2 receives frame immediately
// User 2's UI updates < 100ms ✓Efficient Protocol
Only changed data sent:
// Traditional polling: Full dataset every 5s
GET /api/tasks → 1MB
// WebSocket: Only changes
ws → { frame: { op: 'u', p: { done: true } } } → 50 bytesSee Also
- SyncFrame - Frame format received
- Store - Uses RealtimeAdapter
- SyncProvider - Manages real-time lifecycle
- Real-time Concept - How real-time works
- Server Real-time Guide - Server-side setup
Last updated on