Subscriptions
Learn how to subscribe to real-time data changes with Query-2jz's subscription system.
What are Subscriptions?
Subscriptions in Query-2jz allow you to receive real-time updates when data changes. They use WebSockets or Server-Sent Events to push updates to your clients automatically.
Subscription Features
Basic Subscriptions
Subscribe to changes on specific models or records.
Model Subscription
Subscribe to all changes on a model
WebSocket Connection
const ws = new WebSocket('ws://localhost:3000/subscribe');
// Subscribe to User model changes
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
action: 'all' // create, update, delete, or all
}));Received Updates
// Create event
{
"type": "create",
"model": "User",
"data": {
"id": "1",
"name": "John Doe",
"email": "john@example.com"
},
"timestamp": "2024-01-01T00:00:00Z"
}
// Update event
{
"type": "update",
"model": "User",
"data": {
"id": "1",
"name": "John Updated",
"email": "john@example.com"
},
"changes": {
"name": "John Updated"
},
"timestamp": "2024-01-01T12:00:00Z"
}
// Delete event
{
"type": "delete",
"model": "User",
"data": {
"id": "1"
},
"timestamp": "2024-01-01T18:00:00Z"
}Record Subscription
Subscribe to changes on a specific record
WebSocket Connection
// Subscribe to specific user changes
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
id: '1',
action: 'all'
}));Received Updates
{
"type": "update",
"model": "User",
"id": "1",
"data": {
"id": "1",
"name": "John Updated",
"email": "john@example.com",
"lastLoginAt": "2024-01-01T12:00:00Z"
},
"changes": {
"lastLoginAt": "2024-01-01T12:00:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}Filtered Subscriptions
Subscribe to changes that match specific criteria.
Conditional Subscriptions
Only receive updates for records matching your criteria
WebSocket Connection
// Subscribe to active users only
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
where: {
"status": "active"
},
action: 'all'
}));
// Subscribe to posts by specific author
ws.send(JSON.stringify({
type: 'subscribe',
model: 'Post',
where: {
"authorId": "1",
"status": "published"
},
action: 'all'
}));Complex Filters
// Subscribe with complex conditions
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
where: {
"$and": [
{"status": "active"},
{"$or": [
{"role": "admin"},
{"lastLoginAt": {"$gte": "2024-01-01"}}
]}
]
},
action: 'all'
}));Field Selection
Choose which fields to include in subscription updates.
Selective Field Updates
Only receive updates for specific fields
WebSocket Connection
// Subscribe to specific fields only
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
select: ['id', 'name', 'status', 'lastLoginAt'],
action: 'all'
}));Received Updates
{
"type": "update",
"model": "User",
"data": {
"id": "1",
"name": "John Doe",
"status": "active",
"lastLoginAt": "2024-01-01T12:00:00Z"
},
"changes": {
"lastLoginAt": "2024-01-01T12:00:00Z"
},
"timestamp": "2024-01-01T12:00:00Z"
}Relationship Subscriptions
Subscribe to changes in related models.
Related Model Updates
Get notified when related data changes
WebSocket Connection
// Subscribe to user and their posts
ws.send(JSON.stringify({
type: 'subscribe',
model: 'User',
id: '1',
include: ['posts'],
action: 'all'
}));
// Subscribe to posts with author info
ws.send(JSON.stringify({
type: 'subscribe',
model: 'Post',
include: ['author'],
action: 'all'
}));Received Updates
{
"type": "create",
"model": "Post",
"data": {
"id": "2",
"title": "New Post",
"content": "This is a new post",
"authorId": "1",
"author": {
"id": "1",
"name": "John Doe",
"email": "john@example.com"
}
},
"timestamp": "2024-01-01T12:00:00Z"
}Connection Management
Handle WebSocket connections, reconnection, and error handling.
WebSocket Client
Complete WebSocket client implementation
class Query-2jzSubscription {
constructor(url) {
this.url = url;
this.ws = null;
this.subscriptions = new Map();
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected to Query-2jz subscription server');
this.reconnectAttempts = 0;
// Resubscribe to all previous subscriptions
this.subscriptions.forEach((config, id) => {
this.ws.send(JSON.stringify({
id,
...config
}));
});
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
this.ws.onclose = () => {
console.log('Connection closed');
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
subscribe(config) {
const id = Math.random().toString(36).substr(2, 9);
this.subscriptions.set(id, config);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
id,
...config
}));
}
return id;
}
unsubscribe(id) {
this.subscriptions.delete(id);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({
type: 'unsubscribe',
id
}));
}
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`Reconnecting... (attempt ${this.reconnectAttempts})`);
setTimeout(() => {
this.connect();
}, this.reconnectDelay * this.reconnectAttempts);
} else {
console.error('Max reconnection attempts reached');
}
}
handleMessage(message) {
// Handle different message types
switch (message.type) {
case 'create':
case 'update':
case 'delete':
this.onDataChange(message);
break;
case 'error':
this.onError(message);
break;
case 'pong':
// Heartbeat response
break;
}
}
onDataChange(message) {
// Override this method to handle data changes
console.log('Data changed:', message);
}
onError(message) {
console.error('Subscription error:', message);
}
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// Usage
const subscription = new Query-2jzSubscription('ws://localhost:3000/subscribe');
subscription.onDataChange = (message) => {
// Handle real-time updates
if (message.model === 'User') {
updateUserList(message);
}
};
subscription.connect();
// Subscribe to user changes
const userSubId = subscription.subscribe({
type: 'subscribe',
model: 'User',
action: 'all'
});Server-Sent Events
Alternative to WebSockets for one-way communication.
SSE Implementation
Use Server-Sent Events for subscriptions
Client Implementation
// Create SSE connection
const eventSource = new EventSource('/api/query-2jz/subscribe?model=User&action=all');
eventSource.onmessage = (event) => {
const message = JSON.parse(event.data);
handleSubscriptionUpdate(message);
};
eventSource.onerror = (error) => {
console.error('SSE error:', error);
// SSE automatically reconnects
};
eventSource.onopen = () => {
console.log('SSE connection opened');
};
// Close connection
eventSource.close();URL Parameters
// Basic subscription
/api/query-2jz/subscribe?model=User&action=all
// Filtered subscription
/api/query-2jz/subscribe?model=User&where={"status":"active"}&action=all
// Field selection
/api/query-2jz/subscribe?model=User&select=id,name,status&action=all
// Specific record
/api/query-2jz/subscribe?model=User&id=1&action=allBest Practices
Do
- • Use filtered subscriptions to reduce noise
- • Select only needed fields
- • Implement proper error handling
- • Handle reconnection gracefully
- • Clean up subscriptions when done
- • Use heartbeats for connection health
- • Batch updates when possible
Don't
- • Subscribe to everything without filters
- • Ignore connection errors
- • Forget to unsubscribe
- • Process every update immediately
- • Use subscriptions for one-time data
- • Ignore memory leaks
- • Skip error recovery