Chat Backend Documentation

Socket.IO Implementation Guide (updated for rooms, auth + Redis/Mongo)

📦 Installation

Install the Socket.IO client library:

// NPM
npm install socket.io-client

// Yarn
yarn add socket.io-client

🔌 Connection

Connect to the BuyTrade chat backend via the API Gateway.

Gateway WS URL: https://api.buytrade.app with path=/chat/socket.io
// JavaScript/TypeScript Example
import { io } from 'socket.io-client';

// Connect via API Gateway
const socket = io('https://api.buytrade.app', {
    auth: { token: 'your-jwt-token' },
    path: '/chat/socket.io',
    transports: ['websocket'],
    reconnection: true,
    reconnectionDelay: 1000,
    reconnectionAttempts: 5
});

socket.on('connect', () => {
    console.log('Connected to chat server');
    console.log('Socket ID:', socket.id);
});

socket.on('disconnect', (reason) => {
    console.log('Disconnected:', reason);
});

socket.on('connect_error', (error) => {
    console.error('Connection error:', error.message);
});

🔐 Authentication

Authenticate using a JWT token in the connection options. The token must include userId, role, and a valid iss (issuer).

// Pass token during connection
const token = localStorage.getItem('accessToken'); // issued by API Gateway

const socket = io('https://api.buytrade.app/chat', {
  auth: { token },
    path: '/chat/socket.io',
  transports: ['websocket']
});

// Token payload requirements:
// {
//   userId: '...',
//   role: 'admin' | 'provider' | 'moderator' | 'member' | 'subscriber',
//   iss: 'buytrade-auth', // recommended; server enforces secret + claims
//   exp: 
// }

📤 Rooms & Messaging

Use rooms to group participants. Room names are unique; you can join by name or by the ObjectId returned from create-room.

// Create a room (admin/provider only)
socket.emit('create-room', 'traders-room', (ack) => {
  // ack: { ok, roomId?, error? }
  console.log('create-room =>', ack);
});

// Join a room (by name or ObjectId)
socket.emit('join-room', 'traders-room', (ack) => {
  // ack: { ok, roomId, size }
  console.log('join-room =>', ack);
});

// Leave a room
socket.emit('leave-room', 'traders-room', (ack) => {
  console.log('leave-room =>', ack);
});

// Typing indicator (ephemeral; TTL 5s in Redis)
socket.emit('typing', { roomId: 'traders-room', isTyping: true });

// Send message to a room (server resolves name to id)
socket.emit('send-message', { roomId: 'traders-room', message: 'Hello everyone!' }, (ack) => {
  // ack: { ok, id, roomId }
  console.log('send-message ack =>', ack);
});

📥 Receiving Events

Listen for room messages, presence, typing, and receipts:

// New room message
socket.on('new-message', (msg) => {
  // { id, roomId, sender, message, timestamp }
  console.log('new-message', msg);
});

// Delivery + Read receipts
socket.on('delivery', (d) => console.log('delivery', d));
socket.on('read', (r) => console.log('read', r));

// Typing indicator
socket.on('typing', (t) => console.log('typing', t));

// Presence updates
socket.on('presence:join', (p) => console.log('presence:join', p));
socket.on('presence:leave', (p) => console.log('presence:leave', p));
socket.on('presence:offline', (p) => console.log('presence:offline', p));

// System notices (via Redis Pub/Sub)
socket.on('system:notice', (n) => console.log('system:notice', n));

📋 Event Reference

Client → Server Events

Event Payload Description
create-room roomName Create room (admin/provider only). Ack: { ok, roomId?, error? }
join-room roomIdOrName Join room by name or ObjectId. Ack: { ok, roomId, size }
leave-room roomId Leave room. Ack: { ok, size }
typing { roomId, isTyping } Typing indicator in a room
send-message { roomId (name or id), message } Send room message. Ack: { ok, id, roomId }
message-read { roomId, id } Mark message read

Server → Client Events

Event Payload Description
new-message { id, roomId, sender, message, timestamp } Receive new room message
delivery { id, roomId, sender, status } Delivery receipt (delivered)
read { id, roomId, reader, timestamp } Read receipt
typing { userId, roomId, isTyping } Typing status in room
presence:join { userId, roomId, size } User joined room
presence:leave { userId, roomId, size } User left room
presence:offline { userId, roomId } User went offline
system:notice { text, ts } System broadcast via Pub/Sub

⚠️ Error Handling

// Handle connection errors
socket.on('connect_error', (error) => {
    console.error('Connection failed:', error.message);
    // Show user-friendly error message
});

// Handle general errors
socket.on('error', (error) => {
    console.error('Socket error:', error);
    handleError(error);
});

// Handle disconnection
socket.on('disconnect', (reason) => {
    if (reason === 'io server disconnect') {
        // Server disconnected, reconnect manually
        socket.connect();
    }
    // Otherwise, socket will auto-reconnect
});

🔄 React/Next.js Example

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

function ChatComponent() {
  const [socket, setSocket] = useState(null);
  const [messages, setMessages] = useState([]);

  useEffect(() => {
        const newSocket = io('https://api.buytrade.app/chat', {
            auth: { token: localStorage.getItem('accessToken') },
            path: '/chat/socket.io',
      transports: ['websocket']
    });

    newSocket.on('connect', () => console.log('Connected'));
    newSocket.on('new-message', (m) => setMessages(prev => [...prev, m]));

    setSocket(newSocket);
    return () => newSocket.close();
  }, []);

  const sendMessage = (roomId, content) => {
    socket?.emit('send-message', { roomId, message: content }, (ack) => {
      console.log('send-message ack', ack);
    });
  };

  return (
    // Your chat UI
    null
  );
}

📱 React Native Example

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

const ChatScreen = () => {
  const [socket, setSocket] = useState(null);

  useEffect(() => {
    (async () => {
      const token = await AsyncStorage.getItem('accessToken');
            const newSocket = io('https://api.buytrade.app/chat', {
        auth: { token },
                path: '/chat/socket.io',
        transports: ['websocket']
      });

      newSocket.on('new-message', (message) => {
        // Handle new room message
      });

      setSocket(newSocket);
    })();
    return () => socket?.close();
  }, []);

  // Rest of your component
};

📊 Admin & Testing

Admin endpoints (HTTP):

GET /chat/health → service status
GET /chat/admin/rooms → list active rooms and sizes

Performance testing:

🔐 Security Notes