Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/abdelhafid37/talkbox/llms.txt

Use this file to discover all available pages before exploring further.

TalkBox uses Socket.IO v4 to power real-time messaging and presence updates. Every WebSocket connection is authenticated using a JSON Web Token (JWT) — the server rejects any connection that does not supply a valid token in the handshake. Once authenticated, clients can emit events to send messages and receive live updates pushed directly from the server.

Connection Setup

The client-side socket instance is created once and exported as a singleton. It is initialised with autoConnect: false so that the connection is only established after a JWT has been obtained — preventing unauthenticated connection attempts on page load. Connection URL: resolved from the VITE_API_URL environment variable.
// client/src/services/socket.js
import { io } from "socket.io-client";

const socket = io(import.meta.env.VITE_API_URL, {
  autoConnect: false,
});

export default socket;
To open the connection, pass the JWT in the auth object before calling socket.connect(). Socket.IO forwards this object as socket.handshake.auth on the server.
// Connect with a JWT obtained after login
import socket from "@/services/socket";

const token = localStorage.getItem("token"); // or wherever you store the JWT

socket.auth = { token };
socket.connect();

// Immediately register the user as online
socket.emit("join");

Authentication

The server registers a Socket.IO middleware that runs before any event handler. It reads the token from socket.handshake.auth.token, verifies it with jsonwebtoken, and fetches the matching User document from MongoDB. On success, the user document is attached to socket.user so all downstream event handlers have access to it. Any connection that supplies a missing, expired, or otherwise invalid token is rejected immediately with Error("Unauthorized.").
// server/src/sockets/socket.js — authentication middleware
io.use(async (socket, next) => {
  try {
    const token = socket.handshake.auth?.token;

    if (!token) {
      return next(new Error("Unauthorized."));
    }

    const decoded = jwt.verify(token, process.env.JWT_SECRET);

    const user = await User.findById(decoded.userId);

    if (!user) {
      return next(new Error("Unauthorized."));
    }

    socket.user = user;
    next();
  } catch (error) {
    next(new Error("Unauthorized."));
  }
});

Client → Server Events

These are the events your client application emits to the TalkBox server.

join

Registers the authenticated user as online. Emit this event immediately after connecting — until join is called, the server does not map the user’s ID to their socket, so they will not appear in the online-users list and cannot receive real-time messages. Payload: none Server behavior:
  1. Adds a userId → socketId entry to the in-memory onlineUsers Map.
  2. Broadcasts the updated online-user list to all connected clients via the onlineUsers event.
socket.emit("join");

sendMessage

Sends a message to another user in real time. The server persists the message to MongoDB before delivering it, so the message is durable even if the recipient is currently offline. Payload:
receiverId
string
required
MongoDB ObjectId of the intended recipient. Must correspond to an existing user in the database.
text
string
required
The plain-text content of the message.
Server behavior:
  1. Creates a Message document in MongoDB with sender, receiver, and content fields.
  2. Populates the sender and receiver fields with their username values.
  3. Emits newMessage back to the sender’s socket.
  4. Emits newMessage to the receiver’s socket (if they are currently online).
socket.emit("sendMessage", {
  receiverId: "64abc123def456789abc1234",
  text: "Hello!",
});

Server → Client Events

These are the events the TalkBox server pushes to your client. Attach listeners with socket.on(eventName, handler) and remove them with socket.off(eventName) when the component unmounts to avoid memory leaks.

newMessage

Emitted to both the sender and the receiver after a message has been successfully persisted in MongoDB. Because both parties receive the same populated document, no additional API fetch is required to display a new message in the UI. Payload:
newMessage
object
socket.on("newMessage", (message) => {
  console.log(`New message from ${message.sender.username}: ${message.content}`);

  // Example: append to a messages list only if it belongs to the active conversation
  const isFromSelectedUser = message.sender._id === selectedUser._id;
  const isToSelectedUser   = message.receiver._id === selectedUser._id;
  const isFromMe           = message.sender._id === currentUser._id;
  const isToMe             = message.receiver._id === currentUser._id;

  const belongsToConversation =
    (isFromSelectedUser && isToMe) || (isToSelectedUser && isFromMe);

  if (belongsToConversation) {
    setMessages((prev) => [...prev, message]);
  }
});

// Clean up when component unmounts
return () => socket.off("newMessage");

onlineUsers

Broadcast to all connected clients whenever the presence state changes — that is, whenever any user emits join or disconnects. Use this event to keep an online-indicator UI in sync without polling. Payload: an array of MongoDB ObjectId strings representing every user who is currently connected.
// Example payload
["64abc123def456789abc1234", "64def456abc123789def5678"]
socket.on("onlineUsers", (userIds) => {
  console.log("Currently online:", userIds);
  setOnlineUsers(userIds); // store in component state or a global store
});

return () => socket.off("onlineUsers");

Full Client Example

The snippet below shows a complete, self-contained usage of the TalkBox WebSocket API: importing the socket singleton, authenticating, registering presence, sending messages, listening for incoming messages and presence changes, and cleaning up on teardown.
import socket from "@/services/socket";

// 1. Authenticate and connect
const token = localStorage.getItem("token");
socket.auth = { token };
socket.connect();

// 2. Register as online immediately after connecting
socket.on("connect", () => {
  socket.emit("join");
});

// 3. Send a message
function sendMessage(receiverId, text) {
  socket.emit("sendMessage", { receiverId, text });
}

// 4. Listen for incoming messages
socket.on("newMessage", (message) => {
  console.log(
    `[${message.createdAt}] ${message.sender.username}: ${message.content}`
  );
});

// 5. Track online presence
socket.on("onlineUsers", (userIds) => {
  console.log("Online users:", userIds);
});

// 6. Disconnect when done (e.g. on logout)
function logout() {
  socket.disconnect();
}
The onlineUsers Map lives entirely in server memory. If the Node.js process restarts — due to a crash, a deployment, or a scaling event — the Map is wiped and all presence data is lost. Clients will need to re-emit join after reconnecting to restore their online status. For production deployments with multiple server instances, consider replacing the in-memory Map with a shared store such as Redis.
Socket.IO automatically falls back to HTTP long-polling when a WebSocket connection cannot be established (for example, behind certain proxies or firewalls). Real-time functionality is preserved in fallback mode, though with slightly higher latency. No code changes are needed on the client to enable this behaviour.

Build docs developers (and LLMs) love