MQTT
13 minute read
Introduction to MQTT
MQTT (Message Queuing Telemetry Transport) is a lightweight, publish-subscribe network protocol designed for constrained devices and low-bandwidth, high-latency, or unreliable networks. Originally developed by IBM in the late 1990s, MQTT has become an OASIS standard and is now one of the most widely used protocols for Internet of Things (IoT) applications.
Key Features of MQTT
- Lightweight: Minimized protocol overhead makes it ideal for constrained environments
- Pub/Sub Pattern: Decouples publishers and subscribers via topics
- Quality of Service (QoS): Support for different delivery guarantees (0, 1, 2)
- Retained Messages: Latest message can be stored for future subscribers
- Last Will and Testament (LWT): Notifications when clients disconnect unexpectedly
- Session Persistence: Durable subscriptions for intermittently connected clients
- Security: Support for authentication, authorization, and TLS encryption
MQTT vs REST: Are They Alternatives?
MQTT and REST represent different architectural approaches with distinct use cases, though they can sometimes serve as alternatives to each other.
Comparison Table
Feature | MQTT | REST |
---|---|---|
Pattern | Publish/Subscribe | Request/Response |
Connection | Persistent (long-lived) | Transient (short-lived) |
Protocol | MQTT over TCP/WebSockets | HTTP |
Overhead | Low | Higher |
Data Format | Binary payload (format-agnostic) | Typically JSON/XML |
Directionality | Bidirectional | Client initiates |
Delivery Guarantees | QoS 0, 1, 2 | Requires implementation |
Real-time Updates | Native support | Requires polling or WebSockets |
Resource Constraints | Designed for constrained devices | More resource-intensive |
Discovery | Topic-based | URL/Hypermedia-based |
When to Consider MQTT instead of REST
MQTT may be a better alternative to REST when:
- Real-time data is required: MQTT’s publish/subscribe model naturally supports real-time updates
- Network bandwidth is limited: MQTT’s minimal overhead reduces data transfer
- Power consumption is critical: MQTT’s efficiency preserves battery life on mobile/IoT devices
- Many-to-many communication: Multiple subscribers need data from multiple publishers
- Intermittent connectivity: QoS levels and session persistence handle unreliable networks
- Push notifications are needed: MQTT allows server-initiated messages
- Broadcast communication: Information needs to be sent to many clients simultaneously
When to Stick with REST
REST remains the better choice when:
- Resource-oriented operations: Creating, reading, updating, or deleting resources
- HTTP ecosystem integration: Leveraging HTTP caching, proxies, and tools
- Web browser compatibility: Direct access without specialized libraries
- Statelessness is desired: No need to maintain connection state
- Standard CRUD operations: Mapping to HTTP methods (GET, POST, PUT, DELETE)
- Discoverability: Hypermedia-driven APIs (HATEOAS)
- Public API exposure: Widespread adoption and developer familiarity
Implementing MQTT in .NET
.NET provides several libraries for implementing MQTT clients. The most popular are MQTTnet and M2Mqtt.
Setting Up MQTT with MQTTnet
First, install the required NuGet package:
dotnet add package MQTTnet
Creating an MQTT Publisher
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MqttPublisherDemo
{
class Program
{
static async Task Main(string[] args)
{
// Create a new MQTT client instance
IMqttClient mqttClient = new MqttFactory().CreateMqttClient();
// Configure connection options
IMqttClientOptions options = new MqttClientOptionsBuilder()
.WithClientId(Guid.NewGuid().ToString())
.WithTcpServer("broker.hivemq.com", 1883) // Public test broker
.WithCleanSession()
.Build();
// Connect to the broker
await mqttClient.ConnectAsync(options, CancellationToken.None);
Console.WriteLine("Connected to MQTT broker!");
// Publish a message every 2 seconds
int messageCounter = 1;
while (true)
{
string messagePayload = $"Message {messageCounter} published at {DateTime.Now}";
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic("sensor/temperature")
.WithPayload(Encoding.UTF8.GetBytes(messagePayload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag()
.Build();
await mqttClient.PublishAsync(message, CancellationToken.None);
Console.WriteLine($"Published: {messagePayload}");
messageCounter++;
await Task.Delay(2000);
}
}
}
}
Creating an MQTT Subscriber
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MqttSubscriberDemo
{
class Program
{
static async Task Main(string[] args)
{
// Create a new MQTT client instance
IMqttClient mqttClient = new MqttFactory().CreateMqttClient();
// Configure connection options
IMqttClientOptions options = new MqttClientOptionsBuilder()
.WithClientId(Guid.NewGuid().ToString())
.WithTcpServer("broker.hivemq.com", 1883) // Public test broker
.WithCleanSession()
.Build();
// Set up message handling
mqttClient.UseApplicationMessageReceivedHandler(e =>
{
string topic = e.ApplicationMessage.Topic;
string payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
Console.WriteLine($"Topic: {topic}");
Console.WriteLine($"Payload: {payload}");
Console.WriteLine();
});
// Connect to the broker
await mqttClient.ConnectAsync(options, CancellationToken.None);
Console.WriteLine("Connected to MQTT broker!");
// Subscribe to topics
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic("sensor/#") // # is a multi-level wildcard
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
Console.WriteLine("Subscribed to topic: sensor/#");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
// Disconnect
await mqttClient.DisconnectAsync();
}
}
}
MQTT in ASP.NET Core Services
You can integrate MQTT in your ASP.NET Core application as a background service:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MqttBackgroundService
{
public class MqttClientService : BackgroundService
{
private readonly ILogger<MqttClientService> _logger;
private IMqttClient _mqttClient;
private IMqttClientOptions _options;
public MqttClientService(ILogger<MqttClientService> logger)
{
_logger = logger;
// Create client and options
MqttFactory mqttFactory = new MqttFactory();
_mqttClient = mqttFactory.CreateMqttClient();
_options = new MqttClientOptionsBuilder()
.WithClientId("AspNetCoreClient-" + Guid.NewGuid())
.WithTcpServer("broker.hivemq.com", 1883)
.WithCleanSession()
.Build();
// Setup handlers
_mqttClient.UseConnectedHandler(e =>
{
_logger.LogInformation("Connected to MQTT broker");
// Subscribe to topics
_mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic("device/+/status") // + is a single level wildcard
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
});
_mqttClient.UseDisconnectedHandler(e =>
{
_logger.LogInformation("Disconnected from MQTT broker");
});
_mqttClient.UseApplicationMessageReceivedHandler(e =>
{
string topic = e.ApplicationMessage.Topic;
string payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
_logger.LogInformation($"Received message on topic {topic}: {payload}");
// Process the message (e.g., update device status in database)
ProcessMessage(topic, payload);
});
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Connect to MQTT broker
await _mqttClient.ConnectAsync(_options, stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
if (!_mqttClient.IsConnected)
{
try
{
await _mqttClient.ConnectAsync(_options, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to connect to MQTT broker");
}
}
// Delay 5 seconds
await Task.Delay(5000, stoppingToken);
}
// Disconnect when service is stopping
if (_mqttClient.IsConnected)
{
await _mqttClient.DisconnectAsync();
}
}
private void ProcessMessage(string topic, string payload)
{
// Extract device ID from topic (assuming topic format device/{device_id}/status)
string[] topicLevels = topic.Split('/');
if (topicLevels.Length == 3 && topicLevels[0] == "device" && topicLevels[2] == "status")
{
string deviceId = topicLevels[1];
_logger.LogInformation($"Updating status for device {deviceId} to {payload}");
// Here you would typically update a database or trigger other actions
}
}
public async Task PublishMessageAsync(string topic, string payload, bool retain = false)
{
if (_mqttClient.IsConnected)
{
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag(retain)
.Build();
await _mqttClient.PublishAsync(message);
_logger.LogInformation($"Published message to topic {topic}");
}
else
{
_logger.LogWarning("Cannot publish message: not connected to broker");
}
}
}
}
Register the service in Program.cs
:
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MqttBackgroundService;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Add services to the container
builder.Services.AddControllers();
builder.Services.AddHostedService<MqttClientService>();
// Register MQTT client service as singleton so it can be injected into controllers
builder.Services.AddSingleton<MqttClientService>();
WebApplication app = builder.Build();
// Configure the HTTP request pipeline
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();
Use the MQTT service in a controller:
using Microsoft.AspNetCore.Mvc;
using MqttBackgroundService;
using System.Threading.Tasks;
namespace MqttAspNetCoreDemo.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class DeviceController : ControllerBase
{
private readonly MqttClientService _mqttService;
public DeviceController(MqttClientService mqttService)
{
_mqttService = mqttService;
}
[HttpPost("{deviceId}/command")]
public async Task<IActionResult> SendCommand(string deviceId, [FromBody] DeviceCommand command)
{
// Validate input
if (string.IsNullOrEmpty(deviceId) || command == null)
{
return BadRequest("Device ID and command are required");
}
// Format the message
string topic = $"device/{deviceId}/command";
string payload = System.Text.Json.JsonSerializer.Serialize(command);
// Publish to MQTT broker
await _mqttService.PublishMessageAsync(topic, payload);
return Ok(new { message = "Command sent" });
}
}
public class DeviceCommand
{
public string Action { get; set; }
public object Parameters { get; set; }
}
}
Advanced MQTT Features
Quality of Service Levels
MQTT provides three QoS levels:
- QoS 0 (At most once): Fire and forget - no guarantee of delivery
- QoS 1 (At least once): Guarantees message delivery but may deliver duplicates
- QoS 2 (Exactly once): Guarantees message is delivered exactly once
// Publishing with different QoS levels
public async Task PublishWithQoSAsync(string topic, string payload, int qosLevel)
{
MQTTnet.Protocol.MqttQualityOfServiceLevel qos;
switch (qosLevel)
{
case 0:
qos = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtMostOnce;
break;
case 1:
qos = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce;
break;
case 2:
qos = MQTTnet.Protocol.MqttQualityOfServiceLevel.ExactlyOnce;
break;
default:
throw new ArgumentException("QoS level must be 0, 1, or 2", nameof(qosLevel));
}
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(qos)
.Build();
await _mqttClient.PublishAsync(message);
}
Retained Messages
Retained messages are stored by the broker and immediately delivered to new subscribers:
public async Task PublishRetainedMessageAsync(string topic, string payload)
{
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag(true) // Set retain flag
.Build();
await _mqttClient.PublishAsync(message);
Console.WriteLine($"Published retained message to {topic}");
}
Last Will and Testament (LWT)
LWT allows a message to be published if a client disconnects unexpectedly:
// Configure LWT in connection options
IMqttClientOptions options = new MqttClientOptionsBuilder()
.WithClientId("ClientWithLWT")
.WithTcpServer("broker.example.com", 1883)
.WithWillMessage(new MqttApplicationMessageBuilder()
.WithTopic("device/status")
.WithPayload(Encoding.UTF8.GetBytes("offline"))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.WithRetainFlag()
.Build())
.Build();
Topic Wildcards
MQTT supports wildcards in topic subscriptions:
- +: Single-level wildcard (e.g.,
sensor/+/temperature
) - #: Multi-level wildcard (e.g.,
sensor/#
)
// Subscribe with wildcards
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic("home/+/temperature") // Match home/livingroom/temperature, home/kitchen/temperature, etc.
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
await mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic("device/#") // Match device/123, device/123/status, etc.
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
Secure Communications
Securing MQTT connections with TLS and authentication:
// Secure MQTT client with TLS and username/password
IMqttClientOptions secureOptions = new MqttClientOptionsBuilder()
.WithClientId("SecureClient")
.WithTcpServer("secure-broker.example.com", 8883)
.WithCredentials("username", "password")
.WithTls(new MqttClientOptionsBuilderTlsParameters
{
UseTls = true,
AllowUntrustedCertificates = false,
IgnoreCertificateChainErrors = false,
IgnoreCertificateRevocationErrors = false,
CertificateValidationHandler = (context) =>
{
// Additional certificate validation if needed
return true;
}
})
.Build();
MQTT Broker Options
Self-hosted Brokers
Several MQTT brokers can be self-hosted:
- Mosquitto: Lightweight, open-source broker
- HiveMQ: Enterprise-ready broker with community edition
- EMQ X: Highly scalable MQTT broker
- VerneMQ: High-performance, distributed MQTT broker
Cloud-based MQTT Services
- AWS IoT Core: Managed MQTT service with AWS integration
- Azure IoT Hub: Microsoft’s IoT platform with MQTT support
- Google Cloud IoT Core: Google’s managed MQTT service
- HiveMQ Cloud: Dedicated MQTT broker service
- CloudMQTT: Fully managed MQTT broker as a service
Implementing an MQTT Broker in .NET
MQTTnet also provides server capabilities to implement a broker:
using MQTTnet;
using MQTTnet.Server;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MqttBrokerDemo
{
class Program
{
static async Task Main(string[] args)
{
// Configure MQTT server options
MqttServerOptions mqttServerOptions = new MqttServerOptionsBuilder()
.WithDefaultEndpoint()
.WithDefaultEndpointPort(1883)
.WithConnectionValidator(context =>
{
// Example connection validation
if (context.Username == "validUser" && context.Password == "validPassword")
{
context.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.Success;
}
else
{
context.ReasonCode = MQTTnet.Protocol.MqttConnectReasonCode.BadUserNameOrPassword;
}
})
.WithSubscriptionInterceptor(context =>
{
Console.WriteLine($"Client {context.ClientId} subscribed to {context.TopicFilter.Topic}");
// Modify subscription if needed
// context.AcceptSubscription = true;
})
.WithApplicationMessageInterceptor(context =>
{
// Log all messages
Console.WriteLine($"Client {context.ClientId} published to {context.ApplicationMessage.Topic}: " +
$"{Encoding.UTF8.GetString(context.ApplicationMessage.Payload)}");
// Modify or reject messages if needed
// context.AcceptPublish = true;
})
.Build();
// Create and start the MQTT server
IMqttServer mqttServer = new MqttFactory().CreateMqttServer(mqttServerOptions);
await mqttServer.StartAsync();
Console.WriteLine($"MQTT broker running on port 1883");
Console.WriteLine("Press any key to exit");
Console.ReadKey();
// Stop the server
await mqttServer.StopAsync();
}
}
}
Real-World Use Cases for MQTT
IoT Sensor Networks
MQTT is ideal for collecting data from large numbers of IoT sensors:
// Structure for sensor data
public class SensorReading
{
public string SensorId { get; set; }
public double Temperature { get; set; }
public double Humidity { get; set; }
public double BatteryLevel { get; set; }
public DateTime Timestamp { get; set; }
}
// Publishing sensor data
public async Task PublishSensorReadingAsync(SensorReading reading)
{
string topic = $"sensors/{reading.SensorId}/data";
string payload = System.Text.Json.JsonSerializer.Serialize(reading);
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
await _mqttClient.PublishAsync(message);
}
Smart Home Automation
MQTT enables real-time control of smart home devices:
// Define command model
public class HomeCommand
{
public string DeviceId { get; set; }
public string CommandType { get; set; } // E.g., "Light", "Thermostat", "Lock"
public string Action { get; set; } // E.g., "On", "Off", "SetTemp", "Lock", "Unlock"
public object Parameters { get; set; } // Additional parameters if needed
}
// Send command to smart home device
public async Task SendHomeCommandAsync(HomeCommand command)
{
string topic = $"home/{command.DeviceId}/{command.CommandType}";
string payload = System.Text.Json.JsonSerializer.Serialize(command);
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.ExactlyOnce)
.Build();
await _mqttClient.PublishAsync(message);
}
Chat Applications
MQTT can power real-time chat applications:
public class ChatMessage
{
public string MessageId { get; set; }
public string RoomId { get; set; }
public string UserId { get; set; }
public string Username { get; set; }
public string Content { get; set; }
public DateTime Timestamp { get; set; }
}
// Send chat message
public async Task SendChatMessageAsync(ChatMessage message)
{
string topic = $"chat/rooms/{message.RoomId}";
string payload = System.Text.Json.JsonSerializer.Serialize(message);
MqttApplicationMessage mqttMessage = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
await _mqttClient.PublishAsync(mqttMessage);
}
// Subscribe to room messages
public async Task SubscribeToRoomAsync(string roomId)
{
await _mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic($"chat/rooms/{roomId}")
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build());
}
Real-time Dashboards and Monitoring
MQTT can feed real-time data to dashboards:
// System metrics model
public class SystemMetrics
{
public string SystemId { get; set; }
public double CpuUsage { get; set; }
public double MemoryUsage { get; set; }
public double DiskUsage { get; set; }
public int ActiveConnections { get; set; }
public DateTime Timestamp { get; set; }
}
// Publish system metrics
public async Task PublishSystemMetricsAsync(SystemMetrics metrics)
{
string topic = $"monitoring/{metrics.SystemId}/metrics";
string payload = System.Text.Json.JsonSerializer.Serialize(metrics);
MqttApplicationMessage message = new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(Encoding.UTF8.GetBytes(payload))
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build();
await _mqttClient.PublishAsync(message);
}
Best Practices for MQTT Implementation
- Choose the Right QoS Level: Balance reliability requirements against overhead
- Design a Hierarchical Topic Structure: Well-organized topics improve scalability
- Keep Payloads Efficient: Use compact formats like Protocol Buffers or MessagePack
- Implement Security: Always use TLS and proper authentication in production
- Plan for Scalability: Use broker clustering for high availability
- Handle Connection Failures: Implement reconnection logic with exponential backoff
- Monitor Broker Health: Track connections, message throughput, and errors
- Define Client IDs Carefully: Ensure uniqueness to avoid connection conflicts
- Use Last Will and Testament: For proper disconnection handling
- Set Appropriate Keep-Alive Values: Balance network traffic with connection detection
Hybrid Approaches: Combining MQTT and REST
Rather than viewing MQTT and REST as mutually exclusive alternatives, many systems combine them to leverage their respective strengths:
REST for Configuration, MQTT for Real-time Data
// REST API for configuring device
[ApiController]
[Route("api/[controller]")]
public class DeviceConfigController : ControllerBase
{
private readonly IDeviceRepository _deviceRepo;
private readonly MqttClientService _mqttService;
public DeviceConfigController(IDeviceRepository deviceRepo, MqttClientService mqttService)
{
_deviceRepo = deviceRepo;
_mqttService = mqttService;
}
[HttpGet("{deviceId}")]
public async Task<IActionResult> GetDeviceConfig(string deviceId)
{
DeviceConfiguration config = await _deviceRepo.GetConfigurationAsync(deviceId);
return Ok(config);
}
[HttpPut("{deviceId}")]
public async Task<IActionResult> UpdateDeviceConfig(string deviceId, [FromBody] DeviceConfiguration config)
{
// Update configuration via REST
await _deviceRepo.UpdateConfigurationAsync(deviceId, config);
// Notify device via MQTT that config has changed
await _mqttService.PublishMessageAsync(
$"device/{deviceId}/config_update",
"Configuration updated, please fetch new settings"
);
return Ok(new { message = "Configuration updated" });
}
}
MQTT for Telemetry, REST for Queries
// MQTT background service for collecting telemetry
public class TelemetryService : BackgroundService
{
private readonly IMqttClient _mqttClient;
private readonly ITelemetryRepository _telemetryRepo;
private readonly ILogger<TelemetryService> _logger;
// Implementation omitted for brevity
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Subscribe to telemetry topic
await _mqttClient.SubscribeAsync(new MqttTopicFilterBuilder()
.WithTopic("devices/+/telemetry")
.WithQualityOfServiceLevel(MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce)
.Build(), stoppingToken);
// Process incoming telemetry data and store it
_mqttClient.UseApplicationMessageReceivedHandler(async e =>
{
string topic = e.ApplicationMessage.Topic;
string deviceId = topic.Split('/')[1]; // Extract device ID from topic
string payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
TelemetryData telemetry = System.Text.Json.JsonSerializer.Deserialize<TelemetryData>(payload);
// Store telemetry data in repository
await _telemetryRepo.SaveTelemetryDataAsync(deviceId, telemetry);
_logger.LogInformation($"Stored telemetry from device {deviceId}");
});
}
}
// REST API for querying telemetry data
[ApiController]
[Route("api/[controller]")]
public class TelemetryController : ControllerBase
{
private readonly ITelemetryRepository _telemetryRepo;
public TelemetryController(ITelemetryRepository telemetryRepo)
{
_telemetryRepo = telemetryRepo;
}
[HttpGet("devices/{deviceId}")]
public async Task<IActionResult> GetDeviceTelemetry(
string deviceId,
[FromQuery] DateTime? from = null,
[FromQuery] DateTime? to = null,
[FromQuery] int limit = 100)
{
IEnumerable<TelemetryData> telemetry = await _telemetryRepo.GetTelemetryDataAsync(
deviceId,
from ?? DateTime.UtcNow.AddDays(-1),
to ?? DateTime.UtcNow,
limit);
return Ok(telemetry);
}
[HttpGet("devices/{deviceId}/latest")]
public async Task<IActionResult> GetLatestTelemetry(string deviceId)
{
TelemetryData latest = await _telemetryRepo.GetLatestTelemetryAsync(deviceId);
if (latest == null)
return NotFound();
return Ok(latest);
}
}
Conclusion
MQTT serves as a complementary alternative to REST rather than a direct replacement. While REST excels at resource-oriented interactions with its request-response model, MQTT provides significant advantages for real-time communication, especially in constrained environments.
When to Choose MQTT
- Real-time data streaming requirements
- IoT and device communication
- Low-bandwidth networks
- Battery-powered devices
- Broadcast or many-to-many messaging patterns
- Push notifications
When to Choose REST
- Resource-oriented operations (CRUD)
- Web application APIs
- Client-initiated interactions
- Stateless communication
- Well-understood caching mechanisms
- Wide developer familiarity
When to Use Both Together
Many modern applications use both protocols together, with REST handling resource management and MQTT managing real-time data flows. This hybrid approach allows developers to leverage the strengths of both technologies while minimizing their respective limitations.