MQTT for IoT APIs: Complete Guide

Meta Description: HTTP is too heavy for IoT devices. Learn how MQTT provides lightweight, reliable messaging for connected devices with minimal bandwidth and battery usage. Keywords: mqtt protocol, iot api, message queue, mqtt vs http, iot communication, lightweight messaging Word Count: ~2,100 words Your IoT device needs to send

TRY NANO BANANA FOR FREE

MQTT for IoT APIs: Complete Guide

TRY NANO BANANA FOR FREE
Contents

Meta Description: HTTP is too heavy for IoT devices. Learn how MQTT provides lightweight, reliable messaging for connected devices with minimal bandwidth and battery usage.

Keywords: mqtt protocol, iot api, message queue, mqtt vs http, iot communication, lightweight messaging

Word Count: ~2,100 words


Your IoT device needs to send sensor data to your API. You try HTTP:

setInterval(() => {
  fetch('https://api.petstoreapi.com/v1/sensors/temperature', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ temperature: 72.5 })
  });
}, 60000); // Every minute

This works, but: - Each request has 500+ bytes of HTTP headers - TLS handshakes drain battery - Network interruptions lose data - The device must stay connected

For IoT devices with limited bandwidth, battery, and connectivity, HTTP is too heavy.

Enter MQTT.

What Is MQTT?

MQTT (Message Queuing Telemetry Transport) is a lightweight publish-subscribe messaging protocol designed for constrained devices and unreliable networks.

Key Characteristics

Lightweight: Minimal protocol overhead (2-byte header minimum) Publish-Subscribe: Devices publish to topics, subscribers receive messages Quality of Service: Three levels of delivery guarantee Persistent Sessions: Messages queued when devices are offline Last Will: Automatic notification when devices disconnect

MQTT vs HTTP

Feature HTTP MQTT
Protocol Overhead 500+ bytes 2+ bytes
Connection Request-response Persistent
Pattern Client-server Publish-subscribe
Delivery Guarantee None QoS 0, 1, 2
Offline Support No Yes (persistent sessions)
Battery Usage High Low
Bandwidth High Low

How MQTT Works

Architecture

┌─────────────┐
│   Device 1  │ ──┐
│  (Publisher)│   │
└─────────────┘   │
                  │    ┌──────────────┐
┌─────────────┐   ├───▶│ MQTT Broker  │
│   Device 2  │ ──┤    │  (Server)    │
│  (Publisher)│   │    └──────────────┘
└─────────────┘   │           │
                  │           │
┌─────────────┐   │           ▼
│   Device 3  │ ──┘    ┌──────────────┐
│ (Subscriber)│ ◀──────│  Subscribers │
└─────────────┘        └──────────────┘

Devices don't communicate directly. They publish messages to topics on a broker. Subscribers receive messages from topics they're interested in.

Topics

Topics are hierarchical paths:

pets/123/temperature
pets/123/location
pets/123/activity
pets/456/temperature

Subscribers use wildcards:

pets/+/temperature     ← All pets' temperature
pets/123/#             ← All data for pet 123
pets/#                 ← All pet data

Quality of Service (QoS)

MQTT has three QoS levels:

QoS 0: At most once- Fire and forget - No acknowledgment - Fastest, but messages may be lost

QoS 1: At least once- Acknowledged delivery - Messages may be duplicated - Good balance of reliability and performance

QoS 2: Exactly once- Guaranteed delivery, no duplicates - Slowest, highest overhead - Use when duplicates are unacceptable

Persistent Sessions

When a device disconnects, the broker can queue messages for it:

// Connect with clean session = false
const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  clientId: 'pet-tracker-123',
  clean: false,  // Persist session
  qos: 1
});

When the device reconnects, it receives queued messages.

Last Will and Testament

Devices can set a "last will" message that the broker sends if the device disconnects unexpectedly:

const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  will: {
    topic: 'pets/123/status',
    payload: JSON.stringify({ status: 'offline', reason: 'disconnected' }),
    qos: 1,
    retain: true
  }
});

This notifies subscribers when devices go offline.

MQTT for Pet Tracking

Let's build a pet tracking system with MQTT.

Device: GPS Tracker

The tracker publishes location data:

const mqtt = require('mqtt');

const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  clientId: 'tracker-pet-123',
  username: 'device',
  password: 'device-token',
  clean: false,
  will: {
    topic: 'pets/123/status',
    payload: JSON.stringify({ online: false }),
    qos: 1,
    retain: true
  }
});

client.on('connect', () => {
  console.log('Connected to MQTT broker');

  // Publish online status
  client.publish('pets/123/status', JSON.stringify({ online: true }), {
    qos: 1,
    retain: true
  });

  // Publish location every 30 seconds
  setInterval(() => {
    const location = getGPSLocation();
    client.publish('pets/123/location', JSON.stringify({
      lat: location.lat,
      lng: location.lng,
      accuracy: location.accuracy,
      timestamp: Date.now()
    }), { qos: 1 });
  }, 30000);

  // Publish activity data
  setInterval(() => {
    const activity = getActivityData();
    client.publish('pets/123/activity', JSON.stringify({
      steps: activity.steps,
      calories: activity.calories,
      timestamp: Date.now()
    }), { qos: 1 });
  }, 60000);
});

client.on('error', (error) => {
  console.error('MQTT error:', error);
});

Server: Message Processor

The server subscribes to topics and processes messages:

const mqtt = require('mqtt');

const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  clientId: 'server-processor',
  username: 'server',
  password: 'server-token'
});

client.on('connect', () => {
  console.log('Server connected to MQTT broker');

  // Subscribe to all pet data
  client.subscribe('pets/+/location', { qos: 1 });
  client.subscribe('pets/+/activity', { qos: 1 });
  client.subscribe('pets/+/status', { qos: 1 });
});

client.on('message', async (topic, message) => {
  const parts = topic.split('/');
  const petId = parts[1];
  const dataType = parts[2];
  const data = JSON.parse(message.toString());

  switch (dataType) {
    case 'location':
      await saveLocation(petId, data);
      await checkGeofence(petId, data);
      break;

    case 'activity':
      await saveActivity(petId, data);
      await updateDailyStats(petId, data);
      break;

    case 'status':
      await updateDeviceStatus(petId, data);
      if (!data.online) {
        await notifyOwner(petId, 'Device offline');
      }
      break;
  }
});

async function saveLocation(petId, data) {
  await db.locations.insert({
    petId,
    lat: data.lat,
    lng: data.lng,
    accuracy: data.accuracy,
    timestamp: new Date(data.timestamp)
  });
}

async function checkGeofence(petId, data) {
  const pet = await db.pets.findById(petId);
  if (!pet.geofence) return;

  const distance = calculateDistance(
    data.lat, data.lng,
    pet.geofence.lat, pet.geofence.lng
  );

  if (distance > pet.geofence.radius) {
    await notifyOwner(petId, 'Pet left safe zone');
  }
}

Mobile App: Real-Time Updates

The mobile app subscribes to updates:

const mqtt = require('mqtt');

const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  clientId: `app-user-${userId}`,
  username: 'user',
  password: userToken
});

client.on('connect', () => {
  // Subscribe to user's pets
  userPets.forEach(petId => {
    client.subscribe(`pets/${petId}/location`, { qos: 0 });
    client.subscribe(`pets/${petId}/status`, { qos: 1 });
  });
});

client.on('message', (topic, message) => {
  const parts = topic.split('/');
  const petId = parts[1];
  const dataType = parts[2];
  const data = JSON.parse(message.toString());

  if (dataType === 'location') {
    updateMapMarker(petId, data.lat, data.lng);
  } else if (dataType === 'status') {
    updateDeviceStatus(petId, data.online);
  }
});

MQTT Broker Options

Self-Hosted

Mosquitto (open source):

# Install
apt-get install mosquitto mosquitto-clients

# Configure
cat > /etc/mosquitto/mosquitto.conf <<EOF
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd
EOF

# Add users
mosquitto_passwd -c /etc/mosquitto/passwd device
mosquitto_passwd /etc/mosquitto/passwd server

# Start
systemctl start mosquitto

EMQX (scalable, enterprise features):

docker run -d --name emqx \
  -p 1883:1883 \
  -p 8083:8083 \
  -p 8084:8084 \
  -p 8883:8883 \
  -p 18083:18083 \
  emqx/emqx:latest

Managed Services

AWS IoT Core: Fully managed, integrates with AWS services Azure IoT Hub: Enterprise IoT platform HiveMQ Cloud: Managed MQTT broker CloudMQTT: Simple managed MQTT

MQTT Security

Authentication

Use username/password or client certificates:

// Username/password
const client = mqtt.connect('mqtt://broker.petstoreapi.com', {
  username: 'device-123',
  password: 'secure-token'
});

// Client certificates
const client = mqtt.connect('mqtts://broker.petstoreapi.com', {
  ca: fs.readFileSync('ca.crt'),
  cert: fs.readFileSync('client.crt'),
  key: fs.readFileSync('client.key')
});

Authorization

Control topic access per client:

# Mosquitto ACL file
user device-123
topic write pets/123/#
topic read pets/123/commands/#

user server
topic read pets/#
topic write pets/+/commands/#

user app-user-456
topic read pets/123/#
topic read pets/789/#

Encryption

Use TLS for encrypted communication:

const client = mqtt.connect('mqtts://broker.petstoreapi.com:8883', {
  ca: fs.readFileSync('ca.crt')
});

When to Use MQTT

IoT devices: Sensors, trackers, smart devices Mobile apps: Real-time updates with low battery usage Unreliable networks: Automatic reconnection and message queuing Bandwidth-constrained: Minimal protocol overhead Publish-subscribe: Multiple subscribers for same data

Don't use MQTT for: - Web browsers (limited support, use WebSocket instead) - Request-response patterns (use HTTP) - Large file transfers (use HTTP with resumable uploads)

MQTT + HTTP: Best of Both Worlds

Combine MQTT for real-time data with HTTP for control:

MQTT: Device telemetry, real-time updates HTTP REST: Device configuration, historical data queries

// MQTT for real-time location
client.publish('pets/123/location', JSON.stringify(location));

// HTTP for configuration
fetch('https://api.petstoreapi.com/v1/pets/123/settings', {
  method: 'PUT',
  body: JSON.stringify({ geofenceRadius: 100 })
});

This gives you real-time updates where needed and familiar HTTP APIs for everything else.


MQTT isn't a replacement for HTTP—it's a complement. Use MQTT when you need lightweight, reliable messaging for constrained devices. Use HTTP for everything else.

The Modern PetStore API supports both, giving you the right tool for each job.

Related Articles: - WebSocket vs Server-Sent Events: Real-Time APIs Explained - Building a Multi-Protocol API: REST, GraphQL, and gRPC Together - Webhooks Done Right: Delivery, Retries, and Security