IWEY API

IWEY API — Messagerie temps réel

Guide Socket.IO — conversations privées et groupes

Messagerie temps réel (Socket.IO)

Guide d’intégration client pour le namespace WebSocket de messagerie individuelle et de groupe.

Version HTML servie par l’API : /messaging-realtime · source : docs/messaging-realtime.md

Connexion

Élément Valeur
URL http://localhost:{PORT}/messaging (même host/port que l’API HTTP)
Transport Socket.IO v4 (socket.io-client)
CORS origin: true, credentials: true

Authentification (obligatoire)

Passer le JWT access token au handshake (une des trois formes) :

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

const socket = io('http://localhost:3000/messaging', {
  auth: { token: accessToken },
  // alternatives :
  // query: { token: accessToken },
  // extraHeaders: { Authorization: `Bearer ${accessToken}` },
});

Sans token valide (ou session révoquée / expirée), le serveur coupe immédiatement la connexion.

  1. POST /api/auth/login → récupérer accessToken
  2. Ouvrir le socket avec ce token
  3. En cas de 401 / déconnexion → refresh ou nouveau login, puis reconnect

Rooms

Room Format Rôle
Utilisateur user:{accountId} Auto-join à la connexion
Conversation conversation:{id} Après conversation:join (privé et groupe)

Les événements message:new et typing:update sont émis dans la room conversation. Il faut donc joindre la conversation avant de recevoir les messages des autres.

Événements client → serveur

conversation:join

socket.emit('conversation:join', { conversationId: 'conv-priv-1' });
  • Prérequis : être participant de la conversation
  • Ack Socket.IO : { conversationId }

conversation:leave

socket.emit('conversation:leave', { conversationId: 'conv-priv-1' });

message:send

Envoi dans une conversation existante ou démarrage d’un privé via destinataire :

// Conversation existante (privée ou groupe)
socket.emit('message:send', {
  conversationId: 'conv-grp-1',
  content: 'On se retrouve demain',
});

// Nouveau privé
socket.emit('message:send', {
  recipientId: '00000000-0000-4000-8000-000000000003',
  content: 'Bonjour',
});

// Pièces jointes (URLs / ids fichier) — content optionnel si attachments non vide
socket.emit('message:send', {
  conversationId: 'conv-priv-1',
  attachments: ['https://…/fichier.pdf'],
});

Ack Socket.IO (même payload aussi broadcast en message:new aux clients déjà dans la room) :

{
  "id": "msg-…",
  "conversationId": "conv-priv-1",
  "authorId": "…",
  "content": "Bonjour",
  "date": "2026-09-14T16:00:00.000Z",
  "attachments": []
}

typing:start / typing:stop

socket.emit('typing:start', { conversationId: 'conv-priv-1' });
socket.emit('typing:stop', { conversationId: 'conv-priv-1' });

Événements serveur → client

message:new

Émis dans conversation:{id} après un envoi WebSocket ou REST (POST /api/messages).

socket.on('message:new', (payload) => {
  // payload: { id, conversationId, authorId, content, date, attachments }
});

typing:update

socket.on('typing:update', ({ conversationId, accountId, isTyping }) => {
  // …
});

presence:update

Broadcast global à la connexion / déconnexion d’un compte :

socket.on('presence:update', ({ accountId, online }) => {
  // online: true | false
});

Flux recommandé (UI chat)

login REST
  → connect /messaging + auth.token
  → GET /api/messages/conversations
  → conversation:join pour la conversation ouverte
  → écouter message:new / typing:update / presence:update
  → message:send (ou POST /api/messages) pour envoyer
  → conversation:leave à la fermeture de la vue

REST complémentaire

Méthode Route Notes
GET /api/messages/conversations Listes favoris / groups / privées
GET /api/messages/conversations/:id Historique
POST /api/messages Envoi HTTP — broadcast aussi en message:new
PATCH /api/messages/conversations/:id/* favorite, archive, mute, block

Permission requise : message.view (JWT Bearer).

Conversations privées vs groupes

Type Création Participants
Privé message:send / POST /api/messages avec recipientId, ou seed participantIds = auteur + destinataire
Groupe Création d’un work group (POST /api/work-groups) Membres du groupe ; sync à l’ajout / retrait de membres

L’accès (join, send, typing) est refusé si accountIdparticipantIds.

Seed de démo

Conversation Id Participants
Privé teacher ↔ student conv-priv-1 teacher …0002, student …0003
Groupe A conv-grp-1 idem (+ workGroupId: wg-1)

Comptes utiles :

  • Teacher : teacher@iwey.io / teacher123
  • Student : shab@gmail.com / dfTS8S5$H

Exemple complet (navigateur / Node)

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

async function openChat(accessToken: string, conversationId: string) {
  const socket: Socket = io('http://localhost:3000/messaging', {
    auth: { token: accessToken },
  });

  await new Promise<void>((resolve, reject) => {
    socket.on('connect', () => resolve());
    socket.on('connect_error', reject);
  });

  socket.on('message:new', (msg) => console.log('nouveau message', msg));
  socket.on('typing:update', (t) => console.log('typing', t));
  socket.on('presence:update', (p) => console.log('presence', p));

  const joined = await socket
    .timeout(5000)
    .emitWithAck('conversation:join', { conversationId });
  console.log(joined); // { conversationId }

  socket.emit('message:send', {
    conversationId,
    content: 'Salut en temps réel',
  });

  return socket;
}

Si emitWithAck n’est pas dispo côté client, utiliser le callback Socket.IO classique : socket.emit('conversation:join', payload, (ack) => …).

Erreurs fréquentes

Symptôme Cause probable
Déconnexion immédiate Token manquant, invalide ou session inactive
Pas de message:new des autres Room non jointe (conversation:join)
Erreur forbidden / exception WS Compte hors participantIds
Message REST reçu mais pas WS Client non connecté ou pas dans la room

Fichiers source

  • Gateway : src/modules/communication/gateways/messaging.gateway.ts
  • Constantes events : src/modules/communication/constants/messaging-realtime.constants.ts
  • Broadcast (REST + WS) : src/modules/communication/adapters/messaging-broadcast.adapter.ts
  • Service métier : src/modules/communication/services/conversation.service.ts