Realtime Messaging
Publish/subscribe (pub/sub) is a messaging pattern where producers send messages to a topic, and all subscribers listening to that topic receive a copy.
How It Works
┌──────────┐ ┌─────────┐ ┌──────────────┐
│ Producer │ ──────▶ │ Topic │ ──────▶ │ Subscriber A │
└──────────┘ │ │ └──────────────┘
│ │ ┌──────────────┐
│ │ ──────▶ │ Subscriber B │
│ │ └──────────────┘
│ │ ┌──────────────┐
│ │ ──────▶ │ Subscriber C │
└─────────┘ └──────────────┘
- Producer publishes a message to a topic
- RelayX routes the message to all active subscribers
- Each subscriber receives its own copy
Publishers don't know who's subscribed. Subscribers don't know who published. The only shared agreement is the topic name.
Fire-and-Forget
From the producer's perspective, publishing is fire-and-forget:
// Producer doesn't wait for subscribers to process
await client.publish('notifications.user.123', {
type: 'friend_request',
from: 'user_456'
});
// Execution continues immediately
console.log('Message sent!');
The producer doesn't know (or care) about:
- How many subscribers exist
- Whether subscribers processed successfully
- How long processing takes
This decoupling allows producers and consumers to evolve independently.
Fan-Out: Multiple Subscribers
Every subscriber receives every message. This is called fan-out:
// Subscriber 1: Send push notification
await client.on('orders.created', async (msg) => {
await sendPushNotification(msg.data.userId, 'Order confirmed!');
});
// Subscriber 2: Update analytics
await client.on('orders.created', async (msg) => {
await analytics.track('order_created', msg.data);
});
// Subscriber 3: Notify warehouse
await client.on('orders.created', async (msg) => {
await warehouse.queueForPacking(msg.data.orderId);
});
// When ONE message is published, ALL THREE subscribers execute
When to Use Messaging
Messaging is ideal when:
| Use Case | Example |
|---|---|
| Event broadcasting | User signs up → send email, create CRM record, notify sales |
| Real-time updates | Chat messages, live notifications, collaborative editing |
| Decoupled services | Order service publishes; inventory, shipping, analytics consume |
| Audit logging | Every action publishes an event; logging subscriber captures all |
When NOT to Use Messaging
Messaging is not the right choice when:
- Only one consumer should handle each message → Use Queues
- You need delivery confirmation → Messaging is fire-and-forget
- Subscriber might be offline → Use Queues for durable delivery
Example: Real-Time Chat
A classic messaging use case:
const roomId = 'room-123';
// Subscribe to room messages
await client.on(`chat.room.${roomId}`, (msg) => {
displayMessage(msg.data.userId, msg.data.text);
});
// Send a message
await client.publish(`chat.room.${roomId}`, {
userId: currentUser.id,
text: 'Hello everyone!',
timestamp: Date.now()
});
Every user subscribed to the room topic receives every message in real-time.
Subscriber Lifecycle
Subscribers only receive messages while actively connected:
// Start receiving messages
await client.on('events.live', handler);
// ... messages flow to handler ...
// Stop receiving messages
await client.off('events.live');
// Messages sent now are NOT received
If a subscriber disconnects and reconnects, it misses messages sent during the gap. For durable, guaranteed delivery, use Queues.
Common Mistakes
Treating messaging like a queue
Messaging delivers to all subscribers, not just one. If you need load balancing (one worker handles each job), use Queues.
Expecting exactly-once delivery
Subscribers may receive the same message more than once if they crash during processing. Design your handlers to be idempotent.
Relying on offline delivery
If no subscribers are connected, messages are not stored (unless you use message history). For guaranteed delivery, use Queues.
Join our Discord server, post your concern & someone from our team will help you out ✌️