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 —
.fooandfoo.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)
});
| Pattern | Matches | Doesn't Match |
|---|---|---|
users.*.notifications | users.123.notifications | users.notifications |
*.created | orders.created, users.created | orders.item.created |
users.*.* | users.123.deleted | users.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)
});
| Pattern | Matches | Doesn't Match |
|---|---|---|
chat.> | chat.room1, chat.room1.messages | chat |
logs.*.> | logs.api.error, logs.api.debug.verbose | logs.api |
> | Everything | Nothing |
> Must Be LastThe > 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:
CONNECTEDDISCONNECTEDRECONNECTRECONNECTEDRECONNECTINGRECONN_FAILMESSAGE_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
| Invalid | Reason |
|---|---|
$foo, foo$, foo.$.bar | Contains $ |
foo..bar, .. | Empty segment (consecutive dots) |
.foo, foo. | Leading/trailing dot |
foo bar | Contains space |
foo/bar, foo#bar, foo:bar | Invalid 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.createdis 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('.');
});
Join our Discord server, post your concern & someone from our team will help you out ✌️