Skip to main content

Real-time Updates

Build reactive, collaborative applications with instant data synchronization using WebSocket connections across all platforms.
Real-time updates enable live collaboration, instant notifications, and reactive UI updates without polling.

Overview

Cocobase Real-time features:
  • WebSocket connections - Persistent, bidirectional communication
  • Collection watching - Subscribe to document changes
  • Event types - Create, update, delete notifications
  • Filtered subscriptions - Only receive relevant events
  • Broadcast messaging - Real-time communication channels
  • Automatic reconnection - Built-in connection management

WebSocket Connection

Establish a persistent connection to receive real-time updates.

Basic Connection

import { Cocobase } from 'cocobase';

const db = new Cocobase({ apiKey: 'your-api-key' });

// Watch a collection
const connection = db.watchCollection('users', (event) => {
  console.log('Event:', event.type);
  console.log('Data:', event.data);
});

// Close when done
connection.close();

With Connection Callbacks

const connection = db.watchCollection('users',
  (event) => {
    console.log('Event:', event);
  },
  {
    onConnected: () => console.log('✓ Connected'),
    onDisconnected: () => console.log('✗ Disconnected'),
    onError: (error) => console.error('Error:', error)
  }
);

Event Types

Handle different types of real-time events.

Create Events

Triggered when a new document is created.
db.watchCollection('posts', (event) => {
  if (event.type === 'create') {
    console.log('New post created:', event.data.id);
    console.log('Title:', event.data.title);

    // Update UI
    addPostToUI(event.data);
  }
});

Update Events

Triggered when a document is modified.
db.watchCollection('posts', (event) => {
  if (event.type === 'update') {
    console.log('Post updated:', event.data.id);
    console.log('New title:', event.data.title);

    // Update existing UI element
    updatePostInUI(event.data);
  }
});

Delete Events

Triggered when a document is deleted.
db.watchCollection('posts', (event) => {
  if (event.type === 'delete') {
    console.log('Post deleted:', event.data.id);

    // Remove from UI
    removePostFromUI(event.data.id);
  }
});

All Events Handler

db.watchCollection('posts', (event) => {
  switch (event.type) {
    case 'connected':
      console.log('✓ Connected to real-time updates');
      break;

    case 'create':
      console.log('📝 New post:', event.data.id);
      addPostToUI(event.data);
      break;

    case 'update':
      console.log('✏️ Updated post:', event.data.id);
      updatePostInUI(event.data);
      break;

    case 'delete':
      console.log('🗑️ Deleted post:', event.data.id);
      removePostFromUI(event.data.id);
      break;

    default:
      console.log('Unknown event:', event.type);
  }
});

Filtering Real-time Events

Only receive events for documents matching specific criteria.
// Watch only active posts
db.watchCollection('posts',
  (event) => {
    console.log('Active post event:', event);
  },
  {
    filters: { status: 'active' }
  }
);

// Watch posts by specific author
db.watchCollection('posts',
  (event) => {
    console.log('Author post event:', event);
  },
  {
    filters: { author_id: 'user-123' }
  }
);

Broadcast Messaging

Send and receive messages across connected clients.
// Join a room
const room = db.joinRoom('chat-room-1', (message) => {
  console.log('Message from:', message.from);
  console.log('Content:', message.content);
});

// Send a message
room.broadcast({
  type: 'chat',
  content: 'Hello everyone!',
  timestamp: new Date().toISOString()
});

// Leave room
room.leave();

Reconnection Handling

Handle connection drops and automatic reconnection.
class ResilientWatcher {
  constructor(db, collection) {
    this.db = db;
    this.collection = collection;
    this.retryCount = 0;
    this.maxRetries = 5;
    this.connection = null;
  }

  watch(handler) {
    try {
      this.connection = this.db.watchCollection(
        this.collection,
        handler,
        {
          onConnected: () => {
            console.log('✓ Connected');
            this.retryCount = 0; // Reset on success
          },
          onDisconnected: () => {
            console.log('✗ Disconnected, reconnecting...');
            this.reconnect(handler);
          },
          onError: (error) => {
            console.error('Error:', error);
            this.reconnect(handler);
          }
        }
      );
    } catch (error) {
      console.error('Failed to connect:', error);
      this.reconnect(handler);
    }
  }

  reconnect(handler) {
    if (this.retryCount >= this.maxRetries) {
      console.error('Max retries reached');
      return;
    }

    this.retryCount++;
    const delay = Math.min(1000 * Math.pow(2, this.retryCount), 30000);

    console.log(`Retry ${this.retryCount} in ${delay}ms`);

    setTimeout(() => {
      this.watch(handler);
    }, delay);
  }

  close() {
    if (this.connection) {
      this.connection.close();
    }
  }
}

// Usage
const watcher = new ResilientWatcher(db, 'posts');
watcher.watch((event) => {
  console.log('Event:', event);
});

Real-World Examples

Live Chat Application

class ChatRoom {
  constructor(db, roomId, userId) {
    this.db = db;
    this.roomId = roomId;
    this.userId = userId;
    this.messages = [];
  }

  async connect() {
    // Watch for new messages
    this.connection = this.db.watchCollection(
      'messages',
      (event) => {
        if (event.type === 'create') {
          this.messages.push(event.data);
          this.displayMessage(event.data);
        }
      },
      {
        filters: { room_id: this.roomId }
      }
    );
  }

  async sendMessage(content) {
    await this.db.createDocument('messages', {
      room_id: this.roomId,
      user_id: this.userId,
      content: content,
      timestamp: new Date().toISOString()
    });
  }

  displayMessage(message) {
    const messageEl = document.createElement('div');
    messageEl.className = 'message';
    messageEl.textContent = `${message.user_id}: ${message.content}`;
    document.getElementById('messages').appendChild(messageEl);
  }

  disconnect() {
    if (this.connection) {
      this.connection.close();
    }
  }
}

// Usage
const chat = new ChatRoom(db, 'room-123', 'user-456');
await chat.connect();

document.getElementById('send-btn').addEventListener('click', () => {
  const input = document.getElementById('message-input');
  chat.sendMessage(input.value);
  input.value = '';
});

Best Practices

Properly close WebSocket connections when done to free up resources.
// ✓ Good: Close when component unmounts
useEffect(() => {
  const connection = db.watchCollection('posts', handler);

  return () => {
    connection.close();
  };
}, []);
Use filters to reduce bandwidth and only receive relevant events.
// ✓ Good: Filter on server
db.watchCollection('posts', handler, {
  filters: { author_id: userId }
});

// ✗ Bad: Filter on client
db.watchCollection('posts', (event) => {
  if (event.data.author_id === userId) {
    handler(event);
  }
});
Handle connection drops gracefully with exponential backoff.
Don’t update UI on every event - batch updates for better performance.
// ✓ Good: Debounce updates
let updateTimeout;
db.watchCollection('posts', (event) => {
  clearTimeout(updateTimeout);
  updateTimeout = setTimeout(() => {
    updateUI();
  }, 300);
});
Always implement error handlers for WebSocket connections.
// ✓ Good: Error handling
db.watchCollection('posts', handler, {
  onError: (error) => {
    console.error('Connection error:', error);
    showErrorNotification();
  }
});

Next Steps