Real-time subscriptions
Stream collection changes into your UI with SSE or WebSocket subscriptions from the EmuView SDK.
#Real-time subscriptions
The SDK offers two ways to receive live collection events — record created, updated, and deleted — without polling. By the end of this guide you'll have a list in your UI that updates the moment data changes.
#Choosing a transport
SSE subscribe() |
WebSocket subscribeWS() |
|
|---|---|---|
| Best for | Dashboards, admin UIs, live lists | Chat, collaboration, presence |
| Direction | Server → client only | Bi-directional |
| Presence tracking | No | Yes, built-in |
| Auto-reconnect | No — reconnect manually | Yes (default on) |
| Server setup | None | Requires the SUBSCRIPTION_DO Durable Object binding |
#Prerequisites
- A configured SDK instance (see installation)
- An authenticated user or API key with read access to the collection
#Steps
#1. Subscribe with SSE
subscribe() opens a Server-Sent Events stream and invokes your handlers as events arrive. It returns an unsubscribe function.
import sdk from '$lib/sveltesync';
let orders: Record<string, unknown>[] = [];
const unsubscribe = sdk.collection('orders').subscribe({
filter: { status: 'pending' }, // Only receive matching records
fields: ['id', 'total', 'status'], // Trim event payloads
onConnected: ({ connectionId }) => console.log('Stream open:', connectionId),
onCreated: (record) => {
orders = [record, ...orders];
},
onUpdated: (record) => {
orders = orders.map((o) => (o.id === record.id ? record : o));
},
onDeleted: ({ id }) => {
orders = orders.filter((o) => o.id !== id);
},
onError: (err) => console.error('Stream error:', err)
});
#subscribe() options
| Option | Type | Description |
|---|---|---|
filter |
Record<string, unknown> |
Filter object — only events for matching records are delivered |
fields |
string[] |
Limit event payloads to these fields |
onCreated |
(record) => void |
A record matching the filter was created |
onUpdated |
(record) => void |
A record was updated. The record may include a changes object with the modified fields |
onDeleted |
(data) => void |
A record was deleted. data is { id, deleted_at, permanent? } |
onConnected |
(data) => void |
The stream opened. data is { connectionId, collection } |
onError |
(error: Error) => void |
The connection failed or dropped |
#2. Stop listening
Call the returned function when the component unmounts:
unsubscribe();
SSE streams don't reconnect automatically. If onError fires, call subscribe() again to open a new stream — or use subscribeWS(), which reconnects for you.
#3. Subscribe with WebSocket
subscribeWS() connects through a Durable Object for globally consistent delivery, presence tracking, and automatic reconnection. It returns a handle with close, ping, requestPresence, and send methods.
import sdk from '$lib/sveltesync';
const ws = sdk.collection('orders').subscribeWS({
filter: { status: 'pending' },
fields: ['id', 'total', 'status'],
onConnected: (info) => console.log('Connected:', info.connectionId, 'seq:', info.seq),
onCreated: (record) => console.log('New order:', record),
onUpdated: (record, changes) => console.log('Updated fields:', changes),
onDeleted: ({ id }) => console.log('Deleted:', id),
onPresence: (users) => console.log(`${users.length} users online`),
onClose: (code, reason) => console.log('Closed:', code, reason),
onError: (err) => console.error('WS error:', err),
autoReconnect: true, // Default: true
reconnectDelay: 2000 // Default: 2000ms
});
// Bi-directional messaging
ws.ping(); // Keepalive
ws.requestPresence(); // Ask who else is connected — answered via onPresence
ws.close(); // Disconnect (disables auto-reconnect)
#subscribeWS() options
| Option | Type | Description |
|---|---|---|
filter |
Record<string, unknown> |
Filter object sent with the subscribe message |
fields |
string[] |
Limit event payloads to these fields |
onCreated |
(record) => void |
A matching record was created |
onUpdated |
(record, changes?) => void |
A record was updated. changes holds only the modified fields |
onDeleted |
(data) => void |
A record was deleted. data includes id |
onPresence |
(users) => void |
Presence update. Each user is { userId, email, connectedAt } |
onConnected |
(info) => void |
Connection established. info is { connectionId, seq } |
onClose |
(code, reason) => void |
The socket closed |
onError |
(error: Error) => void |
Connection or protocol error |
autoReconnect |
boolean |
Reconnect after unexpected disconnects. Default: true |
reconnectDelay |
number |
Milliseconds to wait before reconnecting. Default: 2000 |
#Returned handle
| Method | Description |
|---|---|
close() |
Close the connection and cancel any pending reconnect |
ping() |
Send a keepalive ping |
requestPresence() |
Request the current presence list (delivered via onPresence) |
send(msg) |
Send a raw JSON message to the server |
The WebSocket sends the auth token via the Sec-WebSocket-Protocol header rather than the URL, so tokens never appear in server access logs.
#Filtering events
Both transports accept the same filter operators as list(). Events are only delivered for records that match:
const unsubscribe = sdk.collection('products').subscribe({
filter: { price: { _gte: 100 }, status: 'active' },
onCreated: (record) => console.log('New premium product:', record)
});
#What you learned
subscribe()opens an SSE stream and returns an unsubscribe functionsubscribeWS()connects via WebSocket with auto-reconnect, presence, and a control handle- Both accept
filterandfieldsto scope what you receive - Use SSE for one-way dashboards, WebSocket for chat and collaboration
#Next steps
- SDK collections — fetch the initial data your subscription keeps fresh
- Filter operators — the full filter grammar