Skip to main content

Topics


Topics are named addresses that messages are published to and delivered from. They exist for one purpose: routing messages to the right subscribers.

What Topics Are (and Aren't)

When you publish a message, you publish it to a topic. When you subscribe, you subscribe to one or more topics. RelayX matches published messages with subscriptions based on topic names.

Topics do not:

  • Control message ordering
  • Control retries or redelivery
  • Affect deduplication
  • Isolate failures between subscribers
  • Act as queues or workflow stages

All message semantics are defined by the messaging system itself, not by the topic.


Topic Structure

Topics are strings with segments separated by dots:

chat.room1.messages
orders.created
users.123.notifications
analytics.events.pageview

Anatomy of a Topic

  analytics.events.pageview
─────────┬─────────────
│ │ │
│ │ └── Specific event type
│ └── Category
└── Domain/service

Naming Rules

Topic names must follow these rules for consistent behavior across all SDKs:

Allowed Characters

Each segment (token) can contain:

  • Letters: A-Z, a-z
  • Numbers: 0-9
  • Special: _, -, ~, *

Forbidden Characters

  • No $ — The dollar sign is reserved
  • No spaces — Topics cannot contain whitespace
  • No empty segments — No consecutive dots (..)
  • No leading/trailing dots.foo and foo. are invalid

Case Sensitivity

Topic names are case-sensitive. Foo.Bar and foo.bar are different topics.

Format

token[.token …][.> or .*]

Wildcards

Wildcards let you subscribe to multiple topics with a single pattern. They're only supported in subscriptions, not when publishing.

Single-Segment: *

Matches exactly one segment:

// Subscribe to all room messages
await client.on('chat.*.messages', (msg) => {
// ✓ chat.room1.messages
// ✓ chat.lobby.messages
// ✗ chat.messages (missing segment)
// ✗ chat.room1.sub.messages (extra segment)
});
PatternMatchesDoesn't Match
users.*.notificationsusers.123.notificationsusers.notifications
*.createdorders.created, users.createdorders.item.created
users.*.*users.123.deletedusers.123

Multi-Segment: >

Matches one or more segments at the end:

// Subscribe to everything in orders
await client.on('orders.>', (msg) => {
// ✓ orders.created
// ✓ orders.item.added
// ✓ orders.a.b.c.d (any depth)
// ✗ orders (must have at least one more segment)
});
PatternMatchesDoesn't Match
chat.>chat.room1, chat.room1.messageschat
logs.*.>logs.api.error, logs.api.debug.verboselogs.api
>EverythingNothing
> Must Be Last

The > wildcard can only appear at the end of a pattern. orders.>.items is invalid.

Mixing Wildcards

You can combine * and >:

// All events from any service in US regions
await client.on('*.us.>', (msg) => {
// ✓ temp.us.nyc
// ✓ orders.us.west.processed
});

Reserved Topics

These topic names are reserved for system events and cannot be used for your messages:

  • CONNECTED
  • DISCONNECTED
  • RECONNECT
  • RECONNECTED
  • RECONNECTING
  • RECONN_FAIL
  • MESSAGE_RESEND

Valid and Invalid Examples

✅ Valid Topics

sensor.data.temperature
user.signup.europe.london
foo
foo.bar
system.error.critical
alpha_beta
alpha-beta
alpha~beta
metric.cpu.load

✅ Valid Wildcard Subscriptions

*
foo.*
*.bar
foo.*.baz
>
foo.>
foo.bar.>
*.*.>
metrics.>

❌ Invalid Topics

InvalidReason
$foo, foo$, foo.$.barContains $
foo..bar, ..Empty segment (consecutive dots)
.foo, foo.Leading/trailing dot
foo barContains space
foo/bar, foo#bar, foo:barInvalid characters
foo.>.bar> not at end
>foo, foo>bar> must follow a dot

Practical Examples

Multi-Tenant Application

const tenantId = 'acme-corp';

// Publish to tenant-specific topic
await client.publish(`tenant.${tenantId}.orders.created`, orderData);

// Admin: Monitor all tenants
await client.on('tenant.*.orders.>', (msg) => {
console.log('Order activity:', msg.topic);
});

// Tenant app: Only see own data
await client.on(`tenant.${tenantId}.>`, handler);

Chat Application

// Room-scoped events
await client.publish('chat.room.123.messages', { text: 'Hello!' });
await client.publish('chat.room.123.typing', { userId: 'user1' });

// Subscribe to a specific room
await client.on('chat.room.123.>', roomHandler);

// Admin monitors all messages
await client.on('chat.room.*.messages', moderationHandler);

Performance Tips

  • Be specific when possible: orders.created is faster than *.created
  • Avoid root >: Subscribing to > (everything) is expensive at scale
  • Limit wildcard depth: a.*.*.*.> creates more matching overhead

Accessing the Matched Topic

When using wildcards, you can see which specific topic matched:

await client.on('orders.*', (msg) => {
console.log('Matched:', msg.topic);
// "orders.created" or "orders.shipped" etc.

const [domain, action] = msg.topic.split('.');
});


Need Help?

Join our Discord server, post your concern & someone from our team will help you out ✌️