BaasixBaasix

Socket.IO Integration

← Back to Documentation Home

This guide describes how to use the real-time functionality in BAASIX using Socket.IO integration.

Table of Contents

  1. Overview
  2. Configuration
  3. Available Events
  4. Client Integration
  5. Authentication
  6. Custom Events
  7. Room Management
  8. Client Examples

Overview

BAASIX provides real-time functionality through Socket.IO integration, allowing you to implement features like live updates, notifications, chat functionality, and collaborative editing.

Configuration

Socket.IO integration is optional and can be enabled through environment variables:

# Enable Socket.IO functionality
SOCKET_ENABLED=true

# Optional: Configure Socket.IO path (default in this repo: /realtime)
SOCKET_PATH=/realtime

# Optional: CORS origins for Socket.IO (comma-separated)
SOCKET_CORS_ENABLED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com

# Redis configuration for Socket.IO clustering (optional)
CACHE_REDIS_URL=redis://localhost:6379

Redis Adapter for Scaling

BAASIX automatically configures Redis adapter for Socket.IO when Redis is available, enabling horizontal scaling across multiple server instances:

// Automatic Redis adapter configuration
if (process.env.CACHE_REDIS_URL) {
  const redisAdapter = createAdapter(redisPublisher, redisSubscriber);
  io.adapter(redisAdapter);
}

Available Events

When Socket.IO is enabled, BAASIX emits events for various system actions:

Item Events

  • item.created: Emitted when an item is created
  • item.updated: Emitted when an item is updated
  • item.deleted: Emitted when an item is deleted

Notification Events

  • notification.new: Emitted when a new notification is sent to a user
  • notification.seen: Emitted when a notification is marked as seen
  • notification.deleted: Emitted when a notification is deleted

Authentication Events

  • user.connected: Emitted when a user connects to Socket.IO
  • user.disconnected: Emitted when a user disconnects

Event payload structure:

// Item events
{
  collection: 'collection_name',
  item: {
    id: 'item-id',
    // ... item data
  },
  userId: 'user-id',
  timestamp: '2025-01-15T10:30:00.000Z'
}

// Notification events
{
  notification: {
    id: 'notification-id',
    type: 'info',
    title: 'Notification Title',
    message: 'Notification message',
    data: { /* additional data */ },
    seen: false,
    userId: 'user-id',
    createdAt: '2025-01-15T10:30:00.000Z'
  }
}

File Events

  • file.uploaded: When a file is uploaded
  • file.deleted: When a file is deleted

Event payload structure:

{
  file: {
    id: 'file_id',
    filename: 'example.jpg',
    size: 1024,
    mimetype: 'image/jpeg'
  },
  action: 'uploaded|deleted',
  user: 'user_id'
}

Custom Events

  • Custom events can be emitted from hooks or extensions

Client Integration

Connecting to Socket

import { io } from 'socket.io-client';

const socket = io('http://your-api-domain', {
  path: '/realtime',  // Match your server configuration (SOCKET_PATH)
  transports: ['websocket', 'polling'],
  autoConnect: true
});

// Handle connection
socket.on('connect', () => {
  console.log('Connected to BAASIX socket server');
});

// Handle disconnection
socket.on('disconnect', () => {
  console.log('Disconnected from BAASIX socket server');
});

// Handle errors
socket.on('connect_error', (err) => {
  console.error('Socket connection error:', err.message);
});

Subscribing to Events

// Listen for item events in a specific collection
socket.on('item.created', (data) => {
  if (data.collection === 'posts') {
    console.log('New post created:', data.item);
    // Update UI with new post
  }
});

// Listen for updates to a specific item
socket.on('item.updated', (data) => {
  if (data.collection === 'posts' && data.item.id === currentPostId) {
    console.log('Current post updated:', data.item);
    // Update UI with changes
  }
});

Authentication

To authenticate socket connections, include the BAASIX authentication token:

import { io } from 'socket.io-client';

// Get the token from your authentication flow
const token = localStorage.getItem('auth_token');

const socket = io('http://your-api-domain', {
  path: '/realtime',
  auth: {
    token: token
  }
});

Custom Events

You can emit custom events from hooks:

// In a hook file
export default {
  id: 'notification-hook',
  events: [
    {
      collection: 'notifications',
      action: 'items.create.after',
      handler: async (data, context) => {
        // Get the socket service from context
        const { socket } = context.services;
        
        // Emit to specific user
        socket.emitToUser(data.user_id, 'notification.new', {
          id: data.id,
          message: data.message,
          type: data.type
        });
        
        return data;
      }
    }
  ]
};

Room Management

BAASIX automatically manages rooms for authenticated users and collections:

  • User-specific room: user:{userId}
  • Collection room: collection:{collectionName}
  • Item-specific room: item:{collectionName}:{itemId}

Users are automatically joined to their user-specific room on authentication.

Joining Custom Rooms

Client can request to join custom rooms:

// Join a specific discussion room
socket.emit('join', 'discussion:123', (response) => {
  if (response.success) {
    console.log('Joined discussion room');
  } else {
    console.error('Failed to join room:', response.message);
  }
});

Client Examples

Vanilla JavaScript

const socket = io('http://your-api-domain', {
  path: '/realtime',
  auth: { token: 'your-auth-token' }
});

// Listen for real-time updates
socket.on('item.created', handleNewItem);
socket.on('item.updated', handleItemUpdate);

function handleNewItem(data) {
  if (data.collection === 'messages') {
    const messagesList = document.getElementById('messages');
    const messageItem = document.createElement('li');
    messageItem.textContent = `${data.item.user}: ${data.item.text}`;
    messagesList.appendChild(messageItem);
  }
}

React Integration

import React, { useEffect, useState } from 'react';
import { io } from 'socket.io-client';

const LiveFeed = () => {
  const [messages, setMessages] = useState([]);
  const [socket, setSocket] = useState(null);
  
  useEffect(() => {
    // Get token from your auth context/store
    const token = localStorage.getItem('auth_token');
    
    // Initialize socket
    const newSocket = io('http://your-api-domain', {
      path: '/realtime',
      auth: { token }
    });
    
    newSocket.on('connect', () => {
      console.log('Socket connected');
    });
    
    newSocket.on('item.created', (data) => {
      if (data.collection === 'messages') {
        setMessages(prev => [...prev, data.item]);
      }
    });
    
    setSocket(newSocket);
    
    // Cleanup on unmount
    return () => {
      newSocket.disconnect();
    };
  }, []);
  
  return (
    <div className="live-feed">
      <h2>Live Messages</h2>
      <ul>
        {messages.map(msg => (
          <li key={msg.id}>{msg.text}</li>
        ))}
      </ul>
    </div>
  );
};

export default LiveFeed;

Vue.js Integration

import { io } from 'socket.io-client';

export default {
  data() {
    return {
      socket: null,
      messages: []
    };
  },
  created() {
    this.initializeSocket();
  },
  methods: {
    initializeSocket() {
      const token = localStorage.getItem('auth_token');
      this.socket = io('http://your-api-domain', {
        path: '/realtime',
        auth: { token }
      });
      
      this.socket.on('connect', () => {
        console.log('Socket connected');
      });
      
      this.socket.on('item.created', (data) => {
        if (data.collection === 'messages') {
          this.messages.push(data.item);
        }
      });
    }
  },
  beforeUnmount() {
    if (this.socket) {
      this.socket.disconnect();
    }
  },
  template: `
    <div class="live-feed">
      <h2>Live Messages</h2>
      <ul>
        <li v-for="msg in messages" :key="msg.id">{{ msg.text }}</li>
      </ul>
    </div>
  `
};

On this page