This episode covers NATS integration as an MQTT broker for IoT devices via the mqtt configuration block, then summarizes best practices: subject naming conventions, backpressure, timeouts, and the correct event-driven patterns for microservices.

Episode 14 built the scale. Episode 15 connects NATS to the outside world and tidies up your practices: you'll learn to use NATS as an MQTT broker for IoT devices, then review the best practices that keep an architecture healthy — subject naming, backpressure, timeouts, and the correct event-driven patterns.
This is a bridge episode: most of its content is the habits you'll carry all the way to episode 22.
MQTT is a lightweight protocol for IoT devices. Since version 2.10, NATS can act as an MQTT broker directly — MQTT-speaking devices connect to NATS without a separate gateway.
mqtt {
listen: "0.0.0.0:1883"
no_auth_user: mqtt-user
ack_wait: 30s
max_ack_pending: 100
}The mqtt block enables an MQTT listener on port 1883. IoT devices use the regular MQTT protocol, and NATS maps MQTT topics to NATS subjects transparently.
MQTT has topics, NATS has subjects. NATS bridges the two:
nats sub 'sensors.>'
mosquitto_pub -h localhost -t "sensors/temp" -m "24.5"nats sub 'sensors.>' listens on the sensors.> subject, while the MQTT device publishes to the sensors/temp topic, which is automatically mapped to the sensors.temp subject. One server serves IoT devices and microservices at the same time.
Because MQTT maps to NATS subjects, all NATS features apply to IoT data right away: JetStream streams for recording sensor data, work queues for processing, and request-reply for device control. No additional systems needed.
Naming consistency determines how readable your system is:
orders.created.eu.domain.entity.action
orders.created, orders.cancelled
payments.charged, payments.refunded
sensors.temp.room-1The domain.entity.action schema makes every subject readable at a glance. You'll be grateful for this convention when writing permissions or debugging in episode 19.
Backpressure prevents consumers from being overwhelmed. In Core NATS, an unlimited publisher can flood a slow subscriber. The solutions:
nats consumer report ORDERSnats consumer report ORDERS shows the number of pending messages per consumer. If the number keeps rising, that's a signal backpressure is needed — add workers or slow the publisher down.
Timeouts keep your system responsive in the face of failures:
nats request auth.login '{"user":"arman"}' --timeout=2sThe --timeout=2s flag forces the request to stop waiting after 2 seconds. Chain rule: a caller's timeout must be smaller than the handler's timeout, so errors don't pile up in layers. Publishing to a stream also needs a bounded ack to know whether the message was really received.
Several proven patterns for microservices:
orders service --> publish orders.created --> stream ORDERS
├── notification service
├── inventory service
└── analytics workerThe orders service -> publish orders.created flow shows one event consumed by many services without direct coupling. This is the essence of a decoupled architecture — and the theme we'll refine in episode 21.
Tip
Start with one pattern per service: don't mix request-reply and streaming in a single flow without a clear reason. Simplicity is the greatest asset of a messaging architecture.
Episode 15 connected NATS to the world of IoT devices via the MQTT bridge and tidied up engineering practices: consistent subject naming conventions, backpressure to protect consumers, disciplined timeouts, and the correct event-driven patterns for microservices.
Key takeaways:
mqtt block in the configuration.In episode 16 next, we'll discuss JetStream performance — high-throughput publishing in version 2.14+, batching for bulk publishing, server-side message scheduling, scaling with replication and partition across subjects, storage tuning, and benchmarks toward millions of messages per second. NATS starts showing its teeth.