{"id":14971062,"url":"https://github.com/artcom/mqtt-topping","last_synced_at":"2025-10-26T14:31:03.413Z","repository":{"id":2560986,"uuid":"42291000","full_name":"artcom/mqtt-topping","owner":"artcom","description":"Syntactical sugar on top of the MQTT client cake","archived":false,"fork":false,"pushed_at":"2024-04-01T14:31:42.000Z","size":2136,"stargazers_count":4,"open_issues_count":5,"forks_count":2,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-09-28T14:01:22.132Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/artcom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-09-11T06:12:29.000Z","updated_at":"2024-06-20T07:33:54.000Z","dependencies_parsed_at":"2024-04-01T15:32:52.261Z","dependency_job_id":"81891d4d-be1d-40f3-86f3-8aa0f578c961","html_url":"https://github.com/artcom/mqtt-topping","commit_stats":{"total_commits":285,"total_committers":5,"mean_commits":57.0,"dds":0.4666666666666667,"last_synced_commit":"f1bfc34d18d6ff21eede89fa3c7a75b2ba5d3af4"},"previous_names":[],"tags_count":71,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artcom%2Fmqtt-topping","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artcom%2Fmqtt-topping/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artcom%2Fmqtt-topping/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artcom%2Fmqtt-topping/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/artcom","download_url":"https://codeload.github.com/artcom/mqtt-topping/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219862782,"owners_count":16555951,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T13:44:39.887Z","updated_at":"2025-10-26T14:31:02.953Z","avatar_url":"https://github.com/artcom.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# mqtt-topping\n\nWraps the MQTT.js client to multiplex incoming messages to the subscribed handlers and supports querying retained topics via HTTP.\n\n[MQTT.js events](https://github.com/mqttjs/MQTT.js#event-connect) can also be registered on the mqtt-topping client.\n\nExpects that the default MQTT message payload is formatted as JSON.\n\n## MQTT Client\n\n### Features\n\n- Subscribe and unsubscribe handler callbacks to individual (wildcard) topics\n- `JSON.stringify` all published payloads\n- `JSON.parse` all incoming payloads\n- Ignore non-JSON payloads\n- Decide whether to retain a message or not depending on the topic name (retained unless topic is prefixed with `on` or `do`)\n- Publishes and subscriptions are sent with quality-of-service 2\n\n### Connect, Subscribe, Publish, Unpublish and Register Event \"offline\"\n\n```javascript\nconst { connectAsync } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n\n  client.on(\"offline\", () =\u003e console.error(\"Client is offline. Trying to reconnect.\"))\n\n  await client.subscribe(\"my/topic\", (payload, topic, packet) =\u003e {\n    console.log(\n      \"Received Payload \" + payload + \" for Topic \" + topic + \" (retained = \" + packet.retain + \")\"\n    )\n  })\n\n  await client.publish(\"my/topic\", \"myPayload\")\n\n  await client.unpublish(\"my/topic\")\n}\n```\n\n## HTTP Client\n\n### Features\n\n- Works with the broker plugin [\"HiveMQ Retained Message Query Plugin\"](https://github.com/artcom/hivemq-retained-message-query-plugin)\n- Supports single and batch queries including wildcard topics, additional options are:\n  - `parseJson`: Parse the `result.payload` as JSON. Default is `true`.\n  - `depth`: Specifies the recursive depth of the query. A `depth \u003e 0` returns subtopics in `result.children`. Default is `0`.\n  - `flatten`: Flattens all topics into a flat array. Default is `false`.\n- Supports single and batch json queries which:\n  - return entire topic trees (topics with subtopics) as one JSON object\n  - ignore topic payloads if subtopics exist\n\n### Single Query\n\n```javascript\nconst { connectAsync, HttpClient } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n  const httpClient = new HttpClient(\"http://broker.example.com/query\")\n\n  await client.publish(\"my/topic\", \"myPayload\")\n\n  // wait a few milliseconds to ensure the data is processed on the server\n\n  const result = await httpClient.query({ topic: \"my\", depth: 1 })\n\n  // {\n  // \"topic\": \"my\",\n  // \"children\": [\n  //     {\n  //         \"topic\": \"my/topic\",\n  //         \"payload\": \"myPayload\"\n  //     }\n  //   ]\n  // }\n}\n```\n\n### Batch Query\n\n```javascript\nconst { connectAsync, HttpClient } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n  const httpClient = new HttpClient(\"http://broker.example.com/query\")\n\n  await client.publish(\"my/topic1\", \"myPayload1\")\n  await client.publish(\"my/topic2\", \"myPayload2\")\n\n  // wait a few milliseconds to ensure the data is processed on the server\n\n  const result = await httpClient.queryBatch([{ topic: \"my/topic1\" }, { topic: \"my/topic2\" }])\n\n  // [\n  //   {\n  //       \"topic\": \"my/topic1\",\n  //       \"payload\": \"myPayload1\"\n  //   },\n  //   {\n  //       \"topic\": \"my/topic2\",\n  //       \"payload\": \"myPayload2\"\n  //   }\n  // ]\n}\n```\n\n### QueryJson\n\n```javascript\nconst { connectAsync, HttpClient } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n  const httpClient = new HttpClient(\"http://broker.example.com/query\")\n\n  await client.publish(\"my/topic\", \"myPayload\")\n\n  // wait a few milliseconds to ensure the data is processed on the server\n\n  const result = await httpClient.queryJson(\"my\")\n\n  // {\n  //   \"topic\": \"myPayload\"\n  // }\n}\n```\n\n### QueryJsonBatch\n\n```javascript\nconst { connectAsync, HttpClient } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n  const httpClient = new HttpClient(\"http://broker.example.com/query\")\n\n  await client.publish(\"january/first\", \"eat\")\n  await client.publish(\"january/second\", \"sleep\")\n  await client.publish(\"february/first\", \"work\")\n  await client.publish(\"february/second\", \"repeat\")\n\n  // wait a few milliseconds to ensure the data is processed on the server\n\n  const result = await httpClient.queryJsonBatch([\"january\", \"february\"])\n\n  // [\n  //   {\n  //     \"first\": \"eat\"\n  //     \"second\": \"sleep\"\n  //   },\n  //   {\n  //     \"first\": \"work\"\n  //     \"second\": \"repeat\"\n  //   }\n  // ]\n}\n```\n\n### Unpublish Recursively\n\n```javascript\nconst { connectAsync, HttpClient, unpublishRecursively } = require(\"@artcom/mqtt-topping\")\n\nasync function main() {\n  const client = await connectAsync(\"tcp://broker.example.com\")\n  const httpClient = new HttpClient(\"http://broker.example.com/query\")\n\n  await client.publish(\"january/first\", \"eat\")\n  await client.publish(\"january/second\", \"sleep\")\n  await client.publish(\"february/first\", \"work\")\n  await client.publish(\"february/second\", \"repeat\")\n\n  // wait a few milliseconds to ensure the data is processed on the server\n\n  const result = await unpublishRecursively(mqttClient, httpClient, \"february\")\n\n  // remaining published topics on the broker\n  // january/first: \"eat\"\n  // january/second: \"sleep\"\n}\n```\n\n## Development\n\n### Build\n\n```bash\nnpm install\nnpm run build\n```\n\n### Test\n\nThe tests require a running MQTT broker instance with the [\"HiveMQ Retained Message Query Plugin\"](https://github.com/artcom/hivemq-retained-message-query-plugin).\n\n```bash\nnpm install\nnpm run build\nnpm run test\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartcom%2Fmqtt-topping","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartcom%2Fmqtt-topping","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartcom%2Fmqtt-topping/lists"}