{"id":45411439,"url":"https://github.com/rse/mqtt-plus","last_synced_at":"2026-04-02T14:25:51.995Z","repository":{"id":331673392,"uuid":"1128357207","full_name":"rse/mqtt-plus","owner":"rse","description":"MQTT Communication Patterns","archived":false,"fork":false,"pushed_at":"2026-03-01T00:36:55.000Z","size":1012,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-01T03:48:22.884Z","etag":null,"topics":["communication","event","junction","mqtt","rpc","service"],"latest_commit_sha":null,"homepage":"https://npmjs.com/mqtt-plus","language":"TypeScript","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/rse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-01-05T14:16:40.000Z","updated_at":"2026-03-01T00:36:59.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/rse/mqtt-plus","commit_stats":null,"previous_names":["rse/mqtt-plus"],"tags_count":38,"template":false,"template_full_name":null,"purl":"pkg:github/rse/mqtt-plus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fmqtt-plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fmqtt-plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fmqtt-plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fmqtt-plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rse","download_url":"https://codeload.github.com/rse/mqtt-plus/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rse%2Fmqtt-plus/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30101657,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T23:59:36.199Z","status":"ssl_error","status_checked_at":"2026-03-04T23:56:48.556Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["communication","event","junction","mqtt","rpc","service"],"created_at":"2026-02-21T23:12:06.817Z","updated_at":"2026-04-02T14:25:51.988Z","avatar_url":"https://github.com/rse.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\u003cimg src=\"https://raw.githubusercontent.com/rse/mqtt-plus/master/etc/logo.svg\" width=\"400\" align=\"right\" alt=\"\"/\u003e\n\nMQTT+\n=====\n\n[MQTT](http://mqtt.org/) [Communication Patterns](doc/mqtt-plus-comm.md)\n\n[![NPM](https://nodei.co/npm/mqtt-plus.svg?downloads=true\u0026stars=true)](https://nodei.co/npm/mqtt-plus/)\n\n[![github (author stars)](https://img.shields.io/github/stars/rse?logo=github\u0026label=author%20stars\u0026color=%233377aa)](https://github.com/rse)\n[![github (author followers)](https://img.shields.io/github/followers/rse?label=author%20followers\u0026logo=github\u0026color=%234477aa)](https://github.com/rse)\n\nAbout\n-----\n\n**MQTT+** is a companion add-on API for the TypeScript/JavaScript\nAPI [MQTT.js](https://www.npmjs.com/package/mqtt), designed to\nextend [MQTT](http://mqtt.org/) with higher-level\n[communication patterns](doc/mqtt-plus-comm.md) while preserving full type safety.\nIt provides four core communication patterns: fire-and-forget *Event\nEmission*, RPC-style *Service Call*, stream-based *Sink Push*, and\nstream-based *Source Fetch*.\nThese patterns enable structured,\nbi-directional client/server and server/server communication\non top of [MQTT](http://mqtt.org/)’s inherently uni-directional publish/subscribe model.\nInternally, the communication is based on the exchange of typed [CBOR](https://www.rfc-editor.org/rfc/rfc8949.html)\nor [JSON](https://ecma-international.org/publications-and-standards/standards/ecma-404/) messages.\n\nThe result is a more expressive and maintainable messaging layer\nwithout sacrificing [MQTT](http://mqtt.org/)’s excellent robustness and\nscalability.\n**MQTT+** is particularly well-suited for systems built around a\n[*Hub \u0026 Spoke*](https://en.wikipedia.org/wiki/Spoke%E2%80%93hub_distribution_paradigm)\ncommunication architecture, where typed API contracts and controlled interaction flows are\ncritical for reliability and long-term maintainability.\n\nInstallation\n------------\n\n**MQTT+** is published as a Node Package Manager (NPM) package named\n[`mqtt-plus`](https://npmjs.com/mqtt-plus).\nInstall it, together with its peer dependency MQTT.js, with\nthe help of the NPM Command-Line Interface (CLI):\n\n```shell\n$ npm install mqtt mqtt-plus\n```\n\nUsage\n-----\n\nThe following is a simple but self-contained example usage of\n**MQTT+** based on a common API, a server part, a client part,\nand an MQTT infrastructure. It can be found in the file\n[sample.ts](sample/sample.ts) and can be executed from the **MQTT+**\nsource tree via `npm start sample` (assuming the prerequisite *Docker* is\navailable for the underlying *Mosquitto* broker based infrastructure):\n\n```ts\nimport { Readable }                          from \"node:stream\"\nimport chalk                                 from \"chalk\"\nimport Mosquitto                             from \"mosquitto\"\nimport MQTT                                  from \"mqtt\"\nimport MQTTp                                 from \"mqtt-plus\"\nimport type { Event, Service, Source, Sink } from \"mqtt-plus\"\n```\n\n```ts\n/*  ==== SAMPLE COMMON API ====  */\ntype API = {\n    \"example/sample\":   Event\u003c(a1: string, a2: number) =\u003e void\u003e\n    \"example/hello\":    Service\u003c(a1: string, a2: number) =\u003e string\u003e\n    \"example/download\": Source\u003c(filename: string) =\u003e void\u003e\n    \"example/upload\":   Sink\u003c(filename: string) =\u003e void\u003e\n}\n```\n\n```ts\n/*  ==== SAMPLE SERVER ====  */\nconst Server = async (api: MQTTp\u003cAPI\u003e, log: (msg: string, ...args: any[]) =\u003e void) =\u003e {\n    await api.event(\"example/sample\", (a1, a2) =\u003e {\n        log(\"example/sample: SERVER:\", a1, a2)\n    })\n    await api.service(\"example/hello\", (a1, a2) =\u003e {\n        log(\"example/hello: SERVER:\", a1, a2)\n        return `${a1}:${a2}`\n    })\n    await api.source(\"example/download\", async (filename, info) =\u003e {\n        log(\"example/download: SERVER:\", filename)\n        const input = new Readable()\n        input.push(api.str2buf(`the ${filename} content`))\n        input.push(null)\n        info.stream = input\n    })\n    await api.sink(\"example/upload\", async (filename, info) =\u003e {\n        log(\"example/upload: SERVER:\", filename)\n        const chunks: Uint8Array[] = []\n        info.stream.on(\"data\", (chunk: Uint8Array) =\u003e { chunks.push(chunk) })\n        await new Promise\u003cvoid\u003e((resolve) =\u003e { info.stream.once(\"end\", resolve) })\n        const total = chunks.reduce((n, c) =\u003e n + c.length, 0)\n        log(\"received\", total, \"bytes\")\n    })\n}\n```\n\n```ts\n/*  ==== SAMPLE CLIENT ====  */\nconst Client = async (api: MQTTp\u003cAPI\u003e, log: (msg: string, ...args: any[]) =\u003e void) =\u003e {\n    api.emit(\"example/sample\", \"world\", 42)\n\n    const callOutput = await api.call(\"example/hello\", \"world\", 42)\n    log(\"example/hello: CLIENT:\", callOutput)\n\n    const output = await api.fetch(\"example/download\", \"foo\")\n    const chunks: Uint8Array[] = []\n    output.stream.on(\"data\", (chunk: Uint8Array) =\u003e { chunks.push(chunk) })\n    await new Promise\u003cvoid\u003e((resolve) =\u003e { output.stream.on(\"end\", resolve) })\n    const data = api.buf2str(Buffer.concat(chunks))\n    log(\"example/download: CLIENT:\", data)\n\n    const input = new Readable()\n    input.push(api.str2buf(\"uploaded content\"))\n    input.push(null)\n    await api.push(\"example/upload\", input, \"myfile.txt\")\n}\n```\n\n```ts\n/*  ==== SAMPLE INFRASTRUCTURE ====  */\nprocess.on(\"uncaughtException\", (err: Error): void =\u003e {\n    console.error(chalk.red(`ERROR: ${err.stack ?? err.message}`))\n    console.log(chalk.yellow(mosquitto.logs()))\n    process.exit(1)\n})\nconst mosquitto = new Mosquitto({\n    listen: [ { protocol: \"mqtt\", address: \"127.0.0.1\", port: 1883 } ]\n})\nawait mosquitto.start()\nconst mqtt = MQTT.connect(\"mqtt://127.0.0.1:1883\", {\n    username: \"example\", password: \"example\"\n})\nconst api = new MQTTp\u003cAPI\u003e(mqtt)\napi.on(\"log\", async (entry) =\u003e {\n    await entry.resolve()\n    console.log(chalk.grey(`api: ${entry}`))\n})\nconst log = (msg: string, ...args: any[]) =\u003e {\n    console.log(chalk.bold.blue(\"app:\"), chalk.blue(msg), chalk.red(JSON.stringify(args)))\n}\nmqtt.on(\"connect\", async () =\u003e {\n    await Server(api, log)\n    await Client(api, log)\n    await api.destroy()\n    await mqtt.endAsync()\n    await mosquitto.stop()\n})\n```\n\nDocumentation\n-------------\n\nMain documentation:\n\n- [**Communication Patterns**](doc/mqtt-plus-comm.md)\n- [**Application Programming Interface (API)**](doc/mqtt-plus-api.md)\n\nAdditional auxiliary documentation:\n\n- [Extra: Architecture Overview](doc/mqtt-plus-architecture.md)\n- [Extra: Internal Protocol](doc/mqtt-plus-internals.md)\n- [Extra: Broker Setup](doc/mqtt-plus-broker-setup.md)\n\nNotice\n------\n\n\u003e [!Note]\n\u003e **MQTT+** and its peer dependency **MQTT.js** provide a powerful\n\u003e functionality, but are not small in size. **MQTT+** is 4.000 LoC\n\u003e and 75 KB in size (ESM and CJS format). When bundled with all its\n\u003e dependencies, it is 220 KB in size (UMD format). Its peer dependency\n\u003e **MQTT.js** is 370 KB (ESM and CJS format) and 860 KB (UMD format) in\n\u003e size. For a Node.js application, this usually doesn't matter. For a\n\u003e HTML5 SPA it matters more, but usually is still acceptable.\n\n\u003e [!Note]\n\u003e **MQTT+** is still somewhat similar to and originally derived from the weaker\n\u003e [MQTT-JSON-RPC](https://github.com/rse/mqtt-json-rpc) library of the same\n\u003e author. But instead of just JSON, MQTT+ encodes packets as JSON\n\u003e or CBOR (default), uses an own packet format (allowing sender and\n\u003e receiver information), uses shorter NanoIDs instead of longer UUIDs\n\u003e for identification of sender, receiver and requests, and additionally\n\u003e provides source/sink transfer support (with fetch and push capabilities),\n\u003e has an authentication mechanism, supports meta-data passing, and many more.\n\nLicense\n-------\n\nCopyright \u0026copy; 2018-2026 Dr. Ralf S. Engelschall (http://engelschall.com/)\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be included\nin all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frse%2Fmqtt-plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frse%2Fmqtt-plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frse%2Fmqtt-plus/lists"}