---
title: Topics & Pub/Sub
description: Learn about NoLag topics and the pub/sub messaging model. Understand topic patterns, wildcards, and message routing.
---

# Topics & Pub/Sub

Topics are the foundation of NoLag's messaging system. Learn how to use topics for efficient pub/sub communication.

## What are Topics?

Topics are named channels that clients can subscribe to and publish messages on. When a message is published to a topic, all subscribed clients receive it in real-time.

```ts [TypeScript]
import { NoLag } from '@nolag/js-sdk'

const client = NoLag('your_access_token')
await client.connect()

const room = client.setApp('my-app').setRoom('general')

// Subscribe to a topic
room.subscribe('notifications')

// Listen for messages (data + meta)
room.on('notifications', (data, meta) => {
  console.log('Received:', data)
  console.log('Is replay:', meta.isReplay)
  console.log('Message ID:', meta.msgId)
  if (meta.filter) console.log('Filter:', meta.filter)
})

// Publish a message
room.emit('notifications', {
  type: 'alert',
  message: 'New notification!'
})
```
```python [Python]
from nolag import NoLag

client = NoLag('your_access_token')
await client.connect()

room = client.set_app('my-app').set_room('general')

# Subscribe to a topic
await room.subscribe('notifications')

# Listen for messages (data + meta)
def handle_notification(data, meta):
    print('Received:', data)
    print('Is replay:', meta.is_replay)

room.on('notifications', handle_notification)

# Publish a message
await room.emit('notifications', {
    'type': 'alert',
    'message': 'New notification!'
})
```
```go [Go]
package main

import (
    "fmt"

    nolag "github.com/NoLagApp/nolag-go"
)

func main() {
    client := nolag.New("your_access_token")
    client.Connect()

    room := client.SetApp("my-app").SetRoom("general")

    // Subscribe to a topic with message handler
    room.Subscribe("notifications", func(data any, meta nolag.MessageMeta) {
        fmt.Println("Received:", data)
        fmt.Println("Is replay:", meta.IsReplay)
    })

    // Publish a message
    room.Emit("notifications", map[string]any{
        "type":    "alert",
        "message": "New notification!",
    })
}
```

## Topic Naming

Topics use a hierarchical naming convention with forward slashes as separators:

- `chat/general` - General chat room
- `chat/room-1` - Specific chat room
- `users/123/notifications` - User-specific notifications
- `orders/status/processing` - Order status updates

### Naming Best Practices

- Use lowercase letters and hyphens
- Keep hierarchy logical and consistent
- Include identifiers for user/resource-specific topics
- Avoid special characters except `/` and `-`

## Wildcards

NoLag supports wildcard subscriptions for flexible topic matching:

### Single-Level Wildcard (+)

Matches exactly one level in the topic hierarchy:

```ts [TypeScript]
// Subscribe to all room messages
room.subscribe('chat/+/messages')
// Matches: chat/room-1/messages, chat/room-2/messages
// Does NOT match: chat/messages, chat/room-1/private/messages
```
```python [Python]
# Subscribe to all room messages
await room.subscribe('chat/+/messages')
# Matches: chat/room-1/messages, chat/room-2/messages
# Does NOT match: chat/messages, chat/room-1/private/messages
```
```go [Go]
// Subscribe to all room messages with handler
room.Subscribe("chat/+/messages", func(data any, meta nolag.MessageMeta) {
    fmt.Println("Received:", data)
})
// Matches: chat/room-1/messages, chat/room-2/messages
// Does NOT match: chat/messages, chat/room-1/private/messages
```

### Multi-Level Wildcard (#)

Matches any number of levels (must be at the end):

```ts [TypeScript]
// Subscribe to all user events
room.subscribe('users/123/#')
// Matches: users/123/notifications, users/123/orders/new, users/123
```
```python [Python]
# Subscribe to all user events
await room.subscribe('users/123/#')
# Matches: users/123/notifications, users/123/orders/new, users/123
```
```go [Go]
// Subscribe to all user events with handler
room.Subscribe("users/123/#", func(data any, meta nolag.MessageMeta) {
    fmt.Println("User event:", data)
})
// Matches: users/123/notifications, users/123/orders/new, users/123
```

## Message Structure

Messages can contain any JSON-serializable data:

```ts [TypeScript]
// Publish structured message
room.emit('chat', {
  type: 'chat_message',
  sender: {
    id: 'user-123',
    name: 'Alice'
  },
  content: 'Hello, World!',
  timestamp: Date.now(),
  metadata: {
    room: 'general',
    thread: null
  }
})
```
```python [Python]
# Publish structured message
await room.emit('chat', {
    'type': 'chat_message',
    'sender': {
        'id': 'user-123',
        'name': 'Alice'
    },
    'content': 'Hello, World!',
    'timestamp': time.time(),
    'metadata': {
        'room': 'general',
        'thread': None
    }
})
```
```go [Go]
// Publish structured message
room.Emit("chat", map[string]any{
    "type": "chat_message",
    "sender": map[string]string{
        "id":   "user-123",
        "name": "Alice",
    },
    "content":   "Hello, World!",
    "timestamp": time.Now().Unix(),
    "metadata": map[string]any{
        "room":   "general",
        "thread": nil,
    },
})
```

## Message Events

Subscribe to various events on a topic. Message handlers receive two arguments: the message `data` and a `meta` object containing:

- `isReplay` - Whether the message is being replayed from history
- `msgId` - Unique message ID (for acknowledgement)
- `filter` - The filter value the message was published with (if any)

```ts [TypeScript]
// Subscribe and handle events
room.subscribe('chat')

// Incoming messages (includes meta with isReplay, msgId, filter)
room.on('chat', (data, meta) => {
  console.log('Data:', data, 'Meta:', meta)
})

// Connection events
client.on('connect', () => {
  console.log('Successfully connected')
})

client.on('error', (err) => {
  console.error('Connection error:', err)
})
```
```python [Python]
# Subscribe and handle events
await room.subscribe('chat')

# Incoming messages (includes meta with isReplay, msgId, filter)
def handle_message(data, meta):
    print('Data:', data, 'Meta:', meta)

room.on('chat', handle_message)

# Connection events
def on_connect():
    print('Successfully connected')

def on_error(err):
    print('Connection error:', err)

client.on('connect', on_connect)
client.on('error', on_error)
```
```go [Go]
// Subscribe with handler. Messages arrive directly in the callback
room.Subscribe("chat", func(data any, meta nolag.MessageMeta) {
    fmt.Println("Data:", data, "Meta:", meta)
})

// Connection events
client.On("connected", func(args ...any) {
    fmt.Println("Successfully connected")
})

client.On("error", func(args ...any) {
    fmt.Println("Connection error:", args[0])
})
```

## Unsubscribing

Unsubscribe when you no longer need to receive messages:

```ts [TypeScript]
// Unsubscribe from a topic
room.unsubscribe('chat')

// Disconnect client (synchronous, no await needed)
client.disconnect()
```
```python [Python]
# Unsubscribe from a topic
await room.unsubscribe('chat')

# Disconnect client (synchronous)
client.disconnect()
```
```go [Go]
// Unsubscribe from a topic
room.Unsubscribe("chat")

// Disconnect client
client.Close()
```

## Next Steps

- [Topic Filters](/docs/concepts/filters) - narrow delivery to specific entities within a topic
- [Learn about Rooms](/docs/concepts/rooms)
- [Configure Quality of Service](/docs/concepts/qos)
- [Set up Access Control](/docs/concepts/acl)
