Table of Contents
- Realtime
- Subscribing to Collections
- Event Types
- Filtering Subscriptions
- Multiple Subscriptions
- Real-time Groups
- WebSocket Connection Management
- Practical Examples
- Performance Considerations
- Selective Subscriptions
- Error Handling
- Authentication
- Testing Real-time Features
- Security
- WebSocket Protocol
- Best Practices
Realtime
The real-time engine allows you to subscribe to collection changes and receive instant updates via WebSocket connections.
Subscribing to Collections
Basic Subscription
import { VskiClient } from "@vski/sdk";
const client = new VskiClient("http://localhost:3000");
// Subscribe to a collection
const unsubscribe = client.collection("posts").subscribe((event) => {
console.log("Event received:", event);
});
// Event structure:
{
action: "create" | "update" | "delete",
record: { id, title, ... }, // The record that changed
}
Unsubscribing
// Stop receiving events
unsubscribe();
Event Types
VSKI emits three types of events:
Create Events
client.collection("posts").subscribe((event) => {
if (event.action === "create") {
console.log("New post created:", event.record);
}
});
Update Events
client.collection("posts").subscribe((event) => {
if (event.action === "update") {
console.log("Post updated:", event.record);
}
});
Delete Events
client.collection("posts").subscribe((event) => {
if (event.action === "delete") {
console.log("Post deleted:", event.record.id);
}
});
Filtering Subscriptions
Subscribe only to specific records matching a filter:
// Only receive events for published posts
client.collection("posts").subscribe((event) => {
console.log("Published post changed:", event.record);
}, {
filter: "published = true",
});
Multiple Subscriptions
You can subscribe to multiple collections simultaneously:
const unsub1 = client.collection("posts").subscribe((event) => {
console.log("Post event:", event);
});
const unsub2 = client.collection("comments").subscribe((event) => {
console.log("Comment event:", event);
});
// Unsubscribe from all when done
unsub1();
unsub2();
Real-time Groups
Real-time groups allow you to distribute events among multiple consumers using groups:
Subscribing to Groups
When multiple clients subscribe to the same collection with a group, events are distributed among them:
// Client 1 subscribes to "worker-group"
client.collection("posts").subscribe((event) => {
console.log("Client 1 received event:", event);
}, {
group: "worker-group",
});
// Client 2 also subscribes to "worker-group"
client.collection("posts").subscribe((event) => {
console.log("Client 2 received event:", event);
}, {
group: "worker-group",
});
// Events are distributed between client 1 and client 2
Default Group (Fan-out)
If no group is specified, all clients receive all events:
// All clients receive every event
client.collection("posts").subscribe((event) => {
console.log("Received event:", event);
});
Acknowledging Events
When using groups, you can acknowledge events to receive the next one:
// When subscribing with a group, events are sent with an ID
client.collection("posts").subscribe((event) => {
console.log("Received event with ID:", event.ackId);
// Process the event
// Send ACK to receive next event
// Note: This is handled automatically by the SDK
}, {
group: "worker-group",
});
WebSocket Connection Management
The SDK manages WebSocket connections automatically. When you subscribe to a collection, the SDK:
- Establishes a WebSocket connection if not already connected
- Sends subscription messages to the server
- Automatically reconnects if the connection is lost
- Resends subscriptions after reconnection
Automatic Connection
WebSocket connections are established automatically when you subscribe:
// This automatically establishes WebSocket connection if needed
const unsubscribe = client.collection("posts").subscribe((event) => {
console.log("Event:", event);
});
Disconnection
Close the client to stop all WebSocket connections:
// Stop all subscriptions and close WebSocket
client.close();
Practical Examples
Chat Application
// Send a message
await client.collection("messages").create({
roomId: "room-123",
userId: "user-123",
text: "Hello!",
});
// Subscribe to new messages in the room
client.collection("messages").subscribe((event) => {
if (event.action === "create" && event.record.roomId === "room-123") {
displayMessage(event.record);
}
}, {
filter: "roomId = 'room-123'",
});
Live Dashboard
// Subscribe to metrics collection
client.collection("metrics").subscribe((event) => {
if (event.action === "update") {
updateDashboard(event.record);
}
});
// Update metrics
await client.collection("metrics").update("metric-id", {
value: calculateNewValue(),
});
Collaborative Editing
// Document changes
client.collection("documents").subscribe((event) => {
if (event.action === "update" && event.record.id === currentDocId) {
// Update local document with remote changes
mergeChanges(event.record.content);
}
});
Notification System
// Subscribe to user notifications
client.collection("notifications").subscribe((event) => {
if (event.action === "create" && event.record.userId === currentUserId) {
showNotification(event.record);
}
}, {
filter: `userId = '${currentUserId}'`,
});
Performance Considerations
Rate Limiting
Avoid overwhelming the client with rapid updates:
let lastUpdate = 0;
client.collection("metrics").subscribe((event) => {
const now = Date.now();
if (now - lastUpdate > 1000) { // Limit to once per second
updateUI(event.record);
lastUpdate = now;
}
});
Batch Processing
Process multiple events in batches:
const eventQueue = [];
client.collection("posts").subscribe((event) => {
eventQueue.push(event);
});
// Process queue periodically
setInterval(() => {
if (eventQueue.length > 0) {
processBatch(eventQueue.splice(0));
}
}, 1000);
Selective Subscriptions
Only subscribe to collections you need:
// Unsubscribe when done
const unsubscribe = client.collection("posts").subscribe((event) => {
console.log("Event:", event);
});
// Later, stop receiving events
unsubscribe();
Error Handling
client.collection("posts").subscribe(
(event) => {
console.log("Event:", event);
},
{
filter: "published = true",
},
(error) => {
console.error("Subscription error:", error);
// Handle connection errors, etc.
},
);
Authentication
WebSocket connections use the same authentication as HTTP requests. The SDK automatically includes your authentication token in the WebSocket connection URL:
// Set authentication token
client.setToken("your-jwt-token");
// WebSocket connection will include token automatically
const unsubscribe = client.collection("posts").subscribe((event) => {
console.log("Event:", event);
});
Testing Real-time Features
// In tests, you can simulate real-time events
import { VskiClient } from "@vski/sdk";
const client = new VskiClient("http://localhost:3000");
let receivedEvent = null;
client.collection("posts").subscribe((event) => {
receivedEvent = event;
});
// Create a record
await client.collection("posts").create({
title: "Test Post",
});
// Wait for event
await new Promise((resolve) => setTimeout(resolve, 500));
assert(receivedEvent !== null);
assert(receivedEvent.action === "create");
assert(receivedEvent.record.title === "Test Post");
Security
Authentication Required
All real-time subscriptions require authentication:
// Unauthenticated connections will be rejected
const client = new VskiClient("http://localhost:3000");
// client.setToken("your-token"); // Must set token before subscribing
client.collection("posts").subscribe((event) => {
console.log(event);
}); // Will throw error if not authenticated
Authorization
Real-time events respect collection rules:
// If a user doesn't have permission to see a record,
// they won't receive events for that record
await client.settings.rules.create({
name: "Owner Only",
collection: "posts",
rule: "userId = @request.auth.id",
});
WebSocket Protocol
Connection URL
ws://localhost:3000/api/realtime?db=default&auth=your-token
The SDK automatically constructs this URL based on your baseUrl, dbName, and
authentication token.
Subscription Messages
The SDK sends subscription messages to the server:
{
"type": "SUBSCRIBE",
"collection": "posts",
"filter": "published = true",
"group": "worker-group"
}
Event Messages
The server sends event messages to subscribed clients:
{
"type": "EVENT",
"collection": "posts",
"events": [
{
"id": "event-id",
"action": "create",
"ackId": "ack-id",
"data": {
"id": "record-id",
"title": "Hello",
...
}
}
]
}
ACK Messages
When using groups, clients can acknowledge events:
{
"type": "ACK",
"collection": "posts",
"group": "worker-group",
"id": "ack-id"
}
Best Practices
- Always unsubscribe - Remove subscriptions when components unmount
- Use filters - Reduce unnecessary event traffic
- Handle errors - Implement proper error handling for connections
- Limit subscriptions - Don't subscribe to everything at once
- Use presence wisely - Track only what you need
- Optimize reconnection - Configure appropriate reconnection intervals
- Rate limit updates - Prevent overwhelming clients with rapid changes