Skip to main content

Overview

The publish/subscribe pattern is the foundation of MQTT communication. Publishers send messages to topics without knowing who receives them, while subscribers receive messages from topics they’re interested in. This decoupling enables scalable, flexible IoT architectures.

Topic Structure

MQTT topics are hierarchical strings separated by forward slashes, similar to file paths.

Topic Best Practices

sensors/building1/floor2/temperature
devices/esp32/001/status
vehicles/truck/vin123/location/gps
factory/assembly-line/machine5/alerts
Topics are case-sensitive. sensors/temp and sensors/Temp are different topics.

Topic Wildcards

Subscribers can use wildcards to match multiple topics:
The + wildcard matches exactly one topic level:
sensors/+/temperature
Matches:
  • sensors/room1/temperature
  • sensors/room2/temperature
Doesn’t match:
  • sensors/building1/room1/temperature
  • sensors/temperature
Avoid subscribing to # (all topics) in production as it receives every message on the broker.

Publishing Messages

Python Examples

import paho.mqtt.client as mqtt
import json
import time

client = mqtt.Client("publisher_001")
client.connect("localhost", 1883, 60)

# Publish temperature reading
temperature = 22.5
client.publish("sensors/temperature", str(temperature))

# Publish JSON payload
data = {
    "sensor_id": "temp_001",
    "value": 22.5,
    "unit": "celsius",
    "timestamp": time.time()
}
client.publish("sensors/data", json.dumps(data))

client.disconnect()

JavaScript Examples

const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://localhost:1883');

client.on('connect', () => {
  console.log('Connected to broker');
  
  // Publish simple message
  client.publish('sensors/temperature', '22.5');
  
  // Publish with options
  client.publish('devices/status', 'online', {
    qos: 1,
    retain: true
  }, (error) => {
    if (error) {
      console.error('Publish error:', error);
    } else {
      console.log('Message published');
    }
  });
});

Java Examples

import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import java.nio.charset.StandardCharsets;

public class SimplePublisher {
    public static void main(String[] args) {
        Mqtt5BlockingClient client = Mqtt5Client.builder()
                .identifier("java_publisher")
                .serverHost("localhost")
                .serverPort(1883)
                .buildBlocking();

        client.connect();

        // Publish message
        client.publishWith()
                .topic("sensors/temperature")
                .payload("22.5".getBytes(StandardCharsets.UTF_8))
                .qos(MqttQos.AT_LEAST_ONCE)
                .send();

        System.out.println("Message published");
        client.disconnect();
    }
}

Subscribing to Topics

Python Examples

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    print(f"Connected with result code {rc}")
    # Subscribe on connect to handle reconnections
    client.subscribe("sensors/temperature", qos=1)

def on_message(client, userdata, msg):
    print(f"Topic: {msg.topic}")
    print(f"Payload: {msg.payload.decode()}")
    print(f"QoS: {msg.qos}")
    print("---")

client = mqtt.Client("subscriber_001")
client.on_connect = on_connect
client.on_message = on_message

client.connect("localhost", 1883, 60)
client.loop_forever()

JavaScript Examples

const mqtt = require('mqtt');
const client = mqtt.connect('mqtt://localhost:1883');

client.on('connect', () => {
  console.log('Connected to broker');
  
  // Subscribe to topic
  client.subscribe('sensors/temperature', { qos: 1 }, (err) => {
    if (!err) {
      console.log('Subscribed successfully');
    }
  });
});

client.on('message', (topic, payload) => {
  console.log(`Received message on ${topic}: ${payload.toString()}`);
});

Java Examples

import com.hivemq.client.mqtt.mqtt5.Mqtt5BlockingClient;
import com.hivemq.client.mqtt.mqtt5.Mqtt5Client;
import com.hivemq.client.mqtt.datatypes.MqttQos;
import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish;

public class BlockingSubscriber {
    public static void main(String[] args) {
        Mqtt5BlockingClient client = Mqtt5Client.builder()
                .identifier("java_subscriber")
                .serverHost("localhost")
                .buildBlocking();

        client.connect();

        client.subscribeWith()
                .topicFilter("sensors/#")
                .qos(MqttQos.AT_LEAST_ONCE)
                .send();

        try (Mqtt5BlockingClient.Mqtt5Publishes publishes = client.publishes(MqttQos.AT_LEAST_ONCE)) {
            publishes.receive().ifPresent(publish -> {
                String topic = publish.getTopic().toString();
                String payload = new String(publish.getPayloadAsBytes());
                System.out.println(topic + ": " + payload);
            });
        }
    }
}

Publish/Subscribe Patterns

Single publisher, multiple subscribers:
# Publisher
client.publish("broadcast/news", "Breaking news update")

# Subscriber 1, 2, 3... all receive the same message
client.subscribe("broadcast/news")
Use cases: Notifications, broadcasts, alerts

Best Practices

Publishing:
  • Use meaningful, hierarchical topic names
  • Keep payloads small and efficient
  • Choose appropriate QoS levels
  • Avoid publishing to $SYS topics (reserved for broker)
Subscribing:
  • Subscribe in on_connect callback for auto-reconnection
  • Use wildcards judiciously to avoid message overload
  • Unsubscribe from topics when no longer needed
  • Handle malformed messages gracefully

Next Steps

Quality of Service

Understand QoS 0, 1, and 2 for reliable delivery

Retained Messages

Learn about message retention behavior

Build docs developers (and LLMs) love