MQTT

Understanding MQTT as a lightweight messaging protocol alternative to REST APIs for IoT applications

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

FeatureMQTTREST
PatternPublish/SubscribeRequest/Response
ConnectionPersistent (long-lived)Transient (short-lived)
ProtocolMQTT over TCP/WebSocketsHTTP
OverheadLowHigher
Data FormatBinary payload (format-agnostic)Typically JSON/XML
DirectionalityBidirectionalClient initiates
Delivery GuaranteesQoS 0, 1, 2Requires implementation
Real-time UpdatesNative supportRequires polling or WebSockets
Resource ConstraintsDesigned for constrained devicesMore resource-intensive
DiscoveryTopic-basedURL/Hypermedia-based

When to Consider MQTT instead of REST

MQTT may be a better alternative to REST when:

  1. Real-time data is required: MQTT’s publish/subscribe model naturally supports real-time updates
  2. Network bandwidth is limited: MQTT’s minimal overhead reduces data transfer
  3. Power consumption is critical: MQTT’s efficiency preserves battery life on mobile/IoT devices
  4. Many-to-many communication: Multiple subscribers need data from multiple publishers
  5. Intermittent connectivity: QoS levels and session persistence handle unreliable networks
  6. Push notifications are needed: MQTT allows server-initiated messages
  7. Broadcast communication: Information needs to be sent to many clients simultaneously

When to Stick with REST

REST remains the better choice when:

  1. Resource-oriented operations: Creating, reading, updating, or deleting resources
  2. HTTP ecosystem integration: Leveraging HTTP caching, proxies, and tools
  3. Web browser compatibility: Direct access without specialized libraries
  4. Statelessness is desired: No need to maintain connection state
  5. Standard CRUD operations: Mapping to HTTP methods (GET, POST, PUT, DELETE)
  6. Discoverability: Hypermedia-driven APIs (HATEOAS)
  7. 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:

  1. QoS 0 (At most once): Fire and forget - no guarantee of delivery
  2. QoS 1 (At least once): Guarantees message delivery but may deliver duplicates
  3. 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:

  1. Mosquitto: Lightweight, open-source broker
  2. HiveMQ: Enterprise-ready broker with community edition
  3. EMQ X: Highly scalable MQTT broker
  4. VerneMQ: High-performance, distributed MQTT broker

Cloud-based MQTT Services

  1. AWS IoT Core: Managed MQTT service with AWS integration
  2. Azure IoT Hub: Microsoft’s IoT platform with MQTT support
  3. Google Cloud IoT Core: Google’s managed MQTT service
  4. HiveMQ Cloud: Dedicated MQTT broker service
  5. 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

  1. Choose the Right QoS Level: Balance reliability requirements against overhead
  2. Design a Hierarchical Topic Structure: Well-organized topics improve scalability
  3. Keep Payloads Efficient: Use compact formats like Protocol Buffers or MessagePack
  4. Implement Security: Always use TLS and proper authentication in production
  5. Plan for Scalability: Use broker clustering for high availability
  6. Handle Connection Failures: Implement reconnection logic with exponential backoff
  7. Monitor Broker Health: Track connections, message throughput, and errors
  8. Define Client IDs Carefully: Ensure uniqueness to avoid connection conflicts
  9. Use Last Will and Testament: For proper disconnection handling
  10. 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.

Additional Resources