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