Node.jsRabbitMQSocket.IOBackend

Reliable Chat Messaging with RabbitMQ Integration

Implemented a robust chat system that leverages RabbitMQ for message durability and real-time delivery with Socket.IO.

Reliable Chat Messaging with RabbitMQ Integration

Overview

Modern chat applications demand reliability, low latency, and real-time delivery. A client requested a chat system that could handle high concurrency while ensuring no message is lost, even in cases of server crashes or network interruptions.

The goal was to integrate a message queue (RabbitMQ) into the chat workflow, ensuring data durability and real-time delivery via Socket.IO, while also supporting media, contact sharing, and reply threads.


Challenge

  • Data Loss Risk: Traditional Socket.IO emits could fail if the server crashes or restarts.
  • High Concurrency: Chat rooms may have hundreds of users active simultaneously.
  • Media Support: Messages could include images or contact cards, not just text.
  • Scalable Architecture: The system should support horizontal scaling with multiple server instances.

Solution

The architecture combines Node.js, MongoDB, RabbitMQ, and Socket.IO:

  1. Message Creation:

    • Client sends a message with text, images, or contact info.
    • The server validates and prepares the message payload.
  2. Queue Integration:

    • Messages are published to a durable RabbitMQ queue, ensuring they are not lost.
    • Consumers can process messages for analytics, archiving, or further distribution.
  3. Database Persistence:

    • Messages are stored in MongoDB with metadata including reply references, timestamps, and linked status.
  4. Real-Time Delivery:

    • Socket.IO emits the message to all room participants.
    • Last message preview in the room is updated for all members.
  5. Message Flexibility:

    • Supports text, images, and contacts seamlessly.
    • Detects URLs automatically for link previews.

Implementation Highlights

export const createMessage = async (req: Request, res: Response) => {
  const roomId = get(req, 'params.id', '');
  const userId = get(req, 'user._id', '');
  const text = get(req, 'body.text', '');
  const images = get(req, 'body.images', []);
  const contact = get(req, 'body.contact', null);
  const messageType = get(req, 'body.messageType', '');
  
  const lastMessage: any = images.length > 0
    ? { value: get(last(images), 'filename'), type: 'media' }
    : { value: text, type: 'text' };
  
  const room = await Room.findOne({ _id: roomId });
  const roomMembers = get(room, 'members', []).map((m: any) => m.userId);
  
  await Room.updateOne({ _id: roomId }, { lastMessage, lastMessageVisibleOnUsers: roomMembers });
 
  const dateNow = momentTimezone.tz(moment(), 'Asia/Hong_Kong').toDate();
  const newMessage = new Message({
    text,
    message_type: messageType,
    media: images,
    created_by: userId,
    room: roomId,
    created_at: dateNow,
    updated_at: dateNow,
    seen_by: [],
    contact,
  });
 
  try {
    // Save message to MongoDB
    const savedMessage = await newMessage.save();
 
    // Publish message to RabbitMQ
    const payload = { ...savedMessage.toObject() };
    await channel.assertQueue('chat_messages', { durable: true });
    channel.sendToQueue('chat_messages', Buffer.from(JSON.stringify(payload)), { persistent: true });
 
    // Emit to Socket.IO clients
    let lastMessageText = text;
    if (images[0]) lastMessageText = images[0].type;
    if (contact) lastMessageText = 'contact';
 
    ioContainer.io?.to(roomId).emit('NEW_MESSAGE', {
      senderId: userId,
      roomId,
      text: lastMessageText,
      data: savedMessage,
    });
 
    return res.status(200).json({ status: 'success', data: savedMessage });
  } catch (error) {
    return res.status(400).json({ status: 'error', error });
  }
};

Outcome

  • Zero Message Loss: Integration with RabbitMQ guarantees durable messaging even if the server crashes.
  • Real-Time Delivery: Socket.IO ensures all participants see messages instantly.
  • Supports Multiple Message Types: Text, images, and contact messages handled seamlessly.
  • Horizontal Scalability: Multiple server instances can process messages from the queue.
  • Reliable Metadata: Timestamps and message status are consistent across clients and the database.

Conclusion

By integrating RabbitMQ with Socket.IO and MongoDB, the chat system became highly reliable, fault-tolerant, and scalable.

This approach ensures that messages are never lost, even under high load or unexpected failures, while maintaining real-time interactivity for users.

It demonstrates how combining message queues, persistent storage, and real-time protocols can build a modern, robust chat infrastructure ready for enterprise-scale applications.