Top

TeamWise documentation

Chat, collaborate, and create teams effortlessly with Teamwise

Socket.IO in Teamwise

Step-by-step Guide

This section explains the socket flow in Teamwise from first principles. It is a hands-on guide for beginners and for developers who want to add custom socket logic. Short, copy-paste-ready snippets are included. Packages are already bundled with Teamwise, so npm install in each folder installs everything you need.

Quick overview:

Frontend opens a socket connection. Backend accepts it, places users in rooms, and broadcasts events.
The pattern is: connect → authenticate → join rooms → emit/listen → cleanup.

Prerequisites

  • Node.js + npm installed.

  • Environment variable for socket URL: e.g. VITE_SOCKET_URL (frontend) and server URL on backend.

  • Teamwise bundle already includes socket.io (backend) and socket.io-client (frontend). No extra install required beyond npm install.

Quick overview:

Create a single socket instance that you can reuse across the app.

  • // src/services/socket.ts
  • import { io } from "socket.io-client";
  • export const socket = io(import.meta.env.VITE_SOCKET_URL, {
  •     transports: ["polling"], // fallback-first; can enable 'websocket' if needed
  •     reconnectionAttempts: 3,
  •     reconnectionDelay: 1000,
  •     timeout: 30000,
  • });
Notes:
  • -autoConnect: false lets you attach auth before connecting.

  • -transports controls fallback behavior.

  • -reconnection* settings tune reliability.

Frontend: connect and authenticate

Wrap your app with a SocketProvider that connects when the user logs in and disconnects on logout.

  • // src/providers/SocketProvider.tsx (concept)
  • useEffect(() => {
  •    if (!user || !token) return;
  •    socket.auth = { token }; // pass token for server validation
  •    socket.connect();
  •    socket.emit("join_room", user.id);
  •    return () => {
  •       socket.disconnect();
  •    };
  • }, [user, token]);

Tips:

  • -Set socket.auth or include token in a query string for server-side validation.

  • -Always remove listeners and stop intervals in cleanup.

Frontend: listening and emitting events

Use small handler functions and remove them on cleanup.

  • // basic listener
  • socket.on("receive_message", (msg) => {
  •    // update redux or react-query cache
  • });

  • // emit a message
  • socket.emit("send_message", { channelId, text });

  • Cleanup:
  • socket.off("receive_message");

Recommended pattern: centralize socket handlers in a hook (e.g., useSocketHandlers) so logic is testable and maintainable.

Backend: minimal server setup

Start a Socket.IO server and handle basic events.

  • // server.js (concept)
  • const httpServer = require("http").createServer(app);
  • const { Server } = require("socket.io");
  • const io = new Server(httpServer, { cors: { origin: "*" } });

  • io.on("connection", (socket) => {
  • console.log("connected", socket.id);

  • socket.on("join_room", (userId) => {
  • socket.join(`user_${userId}`); // personal room
  • });

  • socket.on("send_message", async (payload) => {
  • // persist message in DB
  • const recipientRoom = `user_${payload.recipientId}`;
  • io.to(recipientRoom).emit("receive_message", payload);
  • });
  • });
Notes:
  • -Use socket.join(room) to target users or channels.

  • -io.to(room).emit(...) broadcasts to a room.

  • -Validate everything server-side (auth, payload shape, permissions).

Common event map

Use consistent, descriptive event names. Example mapping:

  • -Client → Server

  • -join_room (userId)

  • -request_status_update

  • -send_message (message payload)

  • -typing:start / typing:stop

  • -mark_seen (messageId)

  • -Server → Server

  • receive_message

  • message_status_updated

  • user_status_update

  • channel_added / channel_deleted

  • member_added / member_left

Keep this list in your constants so both sides match exactly.

Presence and heartbeat

Periodically emit a heartbeat so the server can maintain accurate presence.

  • // client-side
  • const heartbeat = setInterval(() => {
  • if (socket.connected) socket.emit("request_status_update");
  • }, 5 * 60 * 1000); // every 5 minutes

  • Cleanup on unmount or logout:
  • clearInterval(heartbeat);

Server should update last-seen and broadcast user_status_update to relevant rooms.

Reliability and reconnection

Tune options on client and handle connect_error:

  • socket.on("connect_error", (err) => {
  •     console.warn("socket error", err);
  • });

Client options you can tweak:

-reconnectionAttempts, reconnectionDelay, timeout, rememberUpgrade.

Server side, gracefully handle disconnects and re-joins. If you scale horizontally, use an adapter.

Security best practices

  • -Authenticate connections. Validate JWT tokens in socket.handshake.auth or in a middleware.

  • -Authorize every action. Do not trust client-sent IDs. Check permissions against user record.

  • -CORS: explicitly configure allowed origins.

  • Rate-limit or throttle events that can be spammed.

  • Avoid sensitive payloads over sockets without encryption. Use HTTPS/WSS.

  • Sanitize user-generated content on server side.

Adding custom logic

Typing Indicator

  • // client
  • socket.emit("typing:start", { chatId });
  • // server forwards
  • socket.to(`channel_${chatId}`).emit("typing:start", { userId });

Upload progress

  • -Client uploads directly to server or presigned S3.

  • -Emit upload:progress with percent to channel so recipients can show progress.

Integrating with app state

  • -Update local UI via Redux or React Query on socket events.

  • -Use queryClient.setQueryData(...) to insert new messages into the paged cache

  • -Keep UI updates idempotent. Socket events may arrive in any order.

Debugging & testing tips

  • -Open browser console: socket.connected, socket.id, socket.io.engine.transport.name.

  • -Check connect_error messages.

  • -Use server logs to see joins and emits.

  • For quick local tests use socket.io-client in node REPL or simple HTML page.

Troubleshooting (common issues)

  • -Not connecting: check URL, CORS, and transports mismatch.

  • -Events not received: confirm event name matches and room membership.

  • -Duplicate messages: ensure idempotency and server message dedupe.

  • -Auth fails: verify token is passed and validated.

Quick checklist before adding custom socket features

  • -Event name added to shared constants.

  • -Server validates the event payload and permissions.

  • -Client registers and cleans up listeners.

  • -UI updates via state management are idempotent.

  • -Tests for edge-cases (offline, reconnect, multi-tab).

  • -Consider scaling needs and adapters if required.

Final notes

  • -Socket.IO is included in the bundle. Run npm install in frontend and backend; no separate install required.

  • -Default flow is ready-to-use. To add custom features, follow connect → authenticate → join → emit/listen → cleanup.

  • -Keep events centralized in a constants file. That reduces breakage and eases onboarding

  • -UI updates via state management are idempotent.

  • -Tests for edge-cases (offline, reconnect, multi-tab).

  • -Consider scaling needs and adapters if required.