Skip to content

Example

Full example

from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi_mqtt.config import MQTTConfig
from fastapi_mqtt.fastmqtt import FastMQTT

fast_mqtt = FastMQTT(config=MQTTConfig())


@asynccontextmanager
async def _lifespan(_app: FastAPI):
    await fast_mqtt.mqtt_startup()
    yield
    await fast_mqtt.mqtt_shutdown()


app = FastAPI(lifespan=_lifespan)


@fast_mqtt.on_connect()
def connect(client, flags, rc, properties):
    fast_mqtt.client.subscribe("mqtt")  # subscribing mqtt topic
    print("Connected: ", client, flags, rc, properties)


@fast_mqtt.on_message()
async def message(client, topic, payload, qos, properties):
    print("Received message: ", topic, payload.decode(), qos, properties)
    return 0


@fast_mqtt.subscribe("my/mqtt/topic/#")
async def message_to_topic(client, topic, payload, qos, properties):
    print("Received message to specific topic: ", topic, payload.decode(), qos, properties)


@fast_mqtt.on_disconnect()
def disconnect(client, packet, exc=None):
    print("Disconnected")


@fast_mqtt.on_subscribe()
def subscribe(client, mid, qos, properties):
    print("subscribed", client, mid, qos, properties)


@app.get("/")
async def func():
    fast_mqtt.publish("mqtt", "Hello from Fastapi")  # publishing mqtt topic

    return {"result": True, "message": "Published"}

Example with pydantic models

The same Temperature model is used for the API body, for publishing and for receiving. Invalid messages go to the on_payload_error handler.

from contextlib import asynccontextmanager

from fastapi import FastAPI
from pydantic import BaseModel, ValidationError

from fastapi_mqtt import FastMQTT, MQTTConfig

mqtt_config = MQTTConfig()

fast_mqtt = FastMQTT(config=mqtt_config)


@asynccontextmanager
async def _lifespan(_app: FastAPI):
    await fast_mqtt.mqtt_startup()
    yield
    await fast_mqtt.mqtt_shutdown()


app = FastAPI(lifespan=_lifespan)


class Temperature(BaseModel):
    sensor: str
    value: float


# last temperature received from each sensor
latest: dict[str, Temperature] = {}


@fast_mqtt.subscribe("sensors/+/temperature", qos=1)
async def temperature(item: Temperature, topic: str):
    """The payload arrives parsed and validated as a Temperature."""
    print("Received temperature: ", topic, item)
    latest[item.sensor] = item


@fast_mqtt.on_payload_error()
async def invalid_payload(client, topic: str, payload: bytes, error: ValidationError):
    """Messages that are not a valid Temperature come here, instead of to the handler."""
    print("Invalid message on", topic, error.errors(include_input=False))


@app.post("/temperature")
async def send_temperature(item: Temperature):
    """The same model is used for the API body and for the MQTT message."""
    fast_mqtt.publish(f"sensors/{item.sensor}/temperature", item, qos=1)
    return {"result": True, "message": "Published"}


@app.get("/temperature")
async def get_temperatures() -> dict[str, Temperature]:
    return latest

Run it against a local broker, and publish a temperature through the API:

uvicorn examples.app_models:app --port 8000
curl -X POST localhost:8000/temperature -H 'content-type: application/json' \
  -d '{"sensor": "kitchen", "value": 21.5}'
curl localhost:8000/temperature

More complex examples

Visit the examples folder for more code examples, including a full fastAPI app organized in multiple files (splitting dependencies, routes, app creation) implementing a dynamic MQTT client through a WebSocket connection.