{"id":13696736,"url":"https://github.com/koorchik/node-mole-rpc","last_synced_at":"2025-07-16T21:16:04.493Z","repository":{"id":33565377,"uuid":"159377029","full_name":"koorchik/node-mole-rpc","owner":"koorchik","description":"Transport agnostic spec compliant JSON RPC client and server","archived":false,"fork":false,"pushed_at":"2024-04-11T08:48:52.000Z","size":209,"stargazers_count":63,"open_issues_count":13,"forks_count":13,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-11-29T02:41:43.380Z","etag":null,"topics":["json-rpc","rpc-server"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/koorchik.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2018-11-27T17:54:58.000Z","updated_at":"2024-08-15T15:13:23.000Z","dependencies_parsed_at":"2024-06-18T17:05:45.146Z","dependency_job_id":"182f3408-2289-4d22-b93e-81b5fd880659","html_url":"https://github.com/koorchik/node-mole-rpc","commit_stats":{"total_commits":88,"total_committers":7,"mean_commits":"12.571428571428571","dds":"0.20454545454545459","last_synced_commit":"e2170e7c6e6e4cc76f2cc500851328e534467eb0"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koorchik%2Fnode-mole-rpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koorchik%2Fnode-mole-rpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koorchik%2Fnode-mole-rpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koorchik%2Fnode-mole-rpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/koorchik","download_url":"https://codeload.github.com/koorchik/node-mole-rpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230028433,"owners_count":18161914,"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":["json-rpc","rpc-server"],"created_at":"2024-08-02T18:00:45.930Z","updated_at":"2024-12-16T21:33:29.051Z","avatar_url":"https://github.com/koorchik.png","language":"JavaScript","funding_links":[],"categories":["Libraries"],"sub_categories":[],"readme":"# mole-rpc\n\nTiny transport agnostic JSON-RPC 2.0 client and server which can work both in NodeJs, Browser, Electron etc\n\n[![npm version](https://badge.fury.io/js/mole-rpc.svg)](https://badge.fury.io/js/mole-rpc)\n[![Known Vulnerabilities](https://snyk.io/test/github/koorchik/node-mole-rpc/badge.svg?targetFile=package.json)](https://snyk.io/test/github/koorchik/node-mole-rpc?targetFile=package.json)\n\n![Mole RPC](docs/images/mole-logo.jpg)\n\n## Table of contents\n\n  - [Features](#features)\n  - [Motivation](#motivation)\n  - [Basic usage](#basic-usage)\n    - [Simple example with websocket transport](#simple-example-with-websocket-transport)\n  - [API examples](#api-examples)\n    - [Proxified client](#proxified-client)\n    - [Client (without Proxy support)](#client-without-proxy-support)\n    - [Client (ping server)](#client-ping-server)\n    - [Server (expose instance)](#server-expose-instance)\n    - [Server (expose functions)](#server-expose-functions)\n    - [Error Handling](#error-handling)\n  - [Advanced usage](#advanced-usage)\n  - [Use cases](#use-cases)\n    - [Case 1: Easy way to communicate with web-workers in your browser](#case-1-easy-way-to-communicate-with-web-workers-in-your-browser)\n    - [Case 2: Bypass firewall](#case-2-bypass-firewall)\n    - [Case 3: Microservices via message broker](#case-3-microservices-via-message-broker)\n    - [Case 4: Lightweight Inter process communication](#case-4-lightweight-inter-process-communication)\n    - [Case 5: Multi transport mode (HTTP, HTTPS, WS the same time, for example)](#case-5-multi-transport-mode-http-https-ws-the-same-time-for-example)\n  - [How to create own transport?](#how-to-create-own-transport)\n\n## Features\n\n-   **Transport agnostic** (works with HTTP, MQTT, Websocket, Browser post message etc). Here is the list of supported transports - https://www.npmjs.com/search?q=keywords:mole-transport\n-   **Zero dependencies**. Mole-RPC itself has zero dependencies.\n-   **Works both in NodeJs and in a browser** (both client anf server). For example, you can use it to send request to webworker in your browser.\n-   **Bidirectional websocket connections support** via WS tranport. For example, you want a JSON RPC server which handles remote calls but the same time you want to send commands in opposite direction using the same connection.So, you can use connection initiated by any of the sides for the server and the client the same time.\n-   **Server can use several transports the same time**. For example, you want an RPC server that accepts connections from your local workers by TCP and from Web browser by websocket. You can pass as many transports as you wish.\n-   **Transport independent ping/pong mechanism**. You can check server availability and connection stability even if your transport doesn't support native ping/pong API.\n-   **Lightweight**\n-   **Modern API**. Totally based on Promises and supports Proxified interface\n-   **Supports all features of JSON-RPC 2.0** (batches, notifications etc)\n-   **Easy to create own transport**. Transports have simple API as possible, so it is very easy to add a new transport. See [\"How to create own transport?\"](how-to-create-own-transport) section.\n\n## Motivation\n\nYet another JSON-RPC library? Why do we need it? The reality, that there is no transport agnostic JSON-RPC library for nodejs. One of our projects required JSON RPC over MQTT and unfortunetely we were not able to find a good solution.\n\n**Key issues with existing libraries:**\n\n1. No multiple transports support.\n2. Not possible to add a new transport.\n3. For some possible, but you need to duplicate almost all protocol logic in every transport.\n4. Bidirectional websocket connections are not supported.\n5. Reverse connection (when server connect to client via websocket and after that the client send commands to the server)\n6. Leaking transport abtractions (like topic name in MQTT needs to be the same as method name).\n7. Huge codebase with tons of classes (JSON RPC should not look like this)\n8. No promise based API.\n9. Everything is bundled to one large package with high level of coupling.\n10. Bugs, not tests.\n11. etc\n\nMole-RPC solves all of the issues described above.\n\n## Basic usage\n\nThis module is transport agnostics. So, [you can choose any transport you need](https://www.npmjs.com/search?q=keywords:mole-transport)\n\n### Simple example with websocket transport\n\nIn this example, we use WebSocketServer for RPC server but you you can use simple WS server transport as well. This can be useful for the case when server connects to client (you can bypass firefall in this way).\n\n**Server**\n```js\nconst MoleServer = require('mole-rpc/MoleServer');\nconst TransportServerWSS = require('mole-rpc-transport-ws/TransportServerWSS');\nconst WebSocket = require('ws');\nconst WSS_PORT = 12345;\n\nfunction sum(a, b) { return a + b }\nfunction multiply(a, b) { return a * b }\n\nasync function main() {\n  const server = new MoleServer({\n    transports: prepareTransports()\n  });\n\n  server.expose({ sum, multiply });\n  await server.run();\n}\n\nfunction prepareTransports() {\n  return [\n    new TransportServerWSS({\n      wss: new WebSocket.Server({\n        port: WSS_PORT\n      })\n    })\n  ];\n}\n\nmain().catch(console.error);\n```\n\n**Client (with Proxy support)**\n\n```js\nconst MoleClient = require('mole-rpc/MoleClientProxified');\nconst X = require('mole-rpc/X');\nconst TransportClientWS = require('mole-rpc-transport-ws/TransportClientWS');\n\nconst WebSocket = require('ws');\nconst WSS_PORT = 12345;\n\nasync function main() {\n  const client = new MoleClient({\n    requestTimeout: 1000,\n    transport: prepareTransport()\n  });\n\n  try {\n      console.log( await client.sum(2, 3) );\n  } catch (error) {\n    if (error instanceof X.ExecutionError) {\n      console.log('ERROR', error.data);\n    } else {\n      throw error;\n    }\n  }\n}\n\nfunction prepareTransport() {\n  return new TransportClientWS({\n    wsBuilder: () =\u003e new WebSocket(`ws://localhost:${WSS_PORT}`)\n  });\n}\n\nmain().then(console.log, console.error);\n```\n\n## API examples\n\n### Proxified client\n\nIf you use modern JavaScript you can use proxified client.\nIt allows you to do remote calls very similar to local calls\n\n```javascript\nimport MoleClientProxified from 'mole-rpc/MoleClientProxified';\n\n// choose any transports here\n// https://www.npmjs.com/search?q=keywords:mole-transport\nconst transport = new TransportClient();\nconst calculator = new MoleClientProxified({ transport });\n\nconst result1 = await calculator.sum(1, 3);\nconst result2 = await calculator.asyncSum(2, 3);\n\n// Send JSON RPC notification (fire and forget)\n// server will send no response\nawait calculator.notify.sum(3, 2);\n```\n\n### Client (without Proxy support)\n\n```javascript\nimport MoleClient from 'mole-rpc/MoleClient';\n\n// choose any transports here\n// https://www.npmjs.com/search?q=keywords:mole-transport\nconst transport = new TransportClient();\nconst client = new MoleClient({ transport });\n\nconst result1 = await client.callMethod('sum', [1, 3]);\nconst result2 = await client.callMethod('sum', [2, 3]);\n\n// Send JSON RPC notification (fire and forget)\n// server will send no response\nawait client.notify('sum', [2, 3]);\n```\n\n### Client (ping server)\n\n```javascript\nconst MoleClient = require('mole-rpc/MoleClient');\nconst X = require('mole-rpc/X');\n\n// choose any transports here\n// https://www.npmjs.com/search?q=keywords:mole-transport\nconst transport = new TransportClient();\nconst client = new MoleClient({ transport });\n\ntry {\n    await client.ping();\n} catch (error) {\n    if (error instanceof X.RequestTimeout) {\n        console.log('Ping failed. Server is unavailable');\n    } else if (error instanceof X.MethodNotFound) {\n        console.log('Ping method not found. Update your mole-rpc server');\n    } else {\n        throw error;\n    }\n}\n```\n\n### Server (expose instance)\n\nYou can expose instance directly.\nMethods which start with underscore will not be exposed.\nBuilt-in methods of Object base class will not be exposed.\n\n```javascript\nimport MoleServer from 'mole-rpc/MoleServer';\n\nclass Calculator {\n  sum(a, b) {\n    return a + b;\n  }\n\n  asyncSum(a, b) {\n    return new Promise((resolve, reject) =\u003e {\n        resolve(this.sum(a, b));\n    });\n  }\n\n  _privateMethod() {\n    // will not be exposed\n  }\n}\n\nconst calculator = new Calculator();\n\n// choose any transports here\n// https://www.npmjs.com/search?q=keywords:mole-transport\nconst transports = [new TransportServer()];\n\nconst server = new MoleServer({ transports: [] });\nserver.expose(calculator);\n\nawait server.run();\n```\n\n### Server (expose functions)\n\nYou can expose functions directly\n\n```javascript\nimport MoleServer from \"mole-rpc/MoleServer\";\n\nfunction sum(a, b) {\n  return a+b;\n}\n\nfunction asyncSum(a, b) {\n  return new Promise((resolve, reject) {\n    resolve( sum(a, b) );\n  });\n}\n\n// choose any transports here\n// https://www.npmjs.com/search?q=keywords:mole-transport\nconst transports = [ new TransportServer() ];\n\nconst server = new MoleServer({ transports });\nserver.expose({\n  sum,\n  asyncSum\n});\n\nawait server.run();\n```\n\n### Error Handling\n\nWhen an rpc call encounters an error, the server will return an object with an error code. See [JSON RPC 2.0 Specification](https://www.jsonrpc.org/specification#error_object) for details.\n\nGetting an error Mole RPC Client will throw (reject promise) a corresponding exception.\n\nList of available exception classes:\n\n* Base\n  * MethodNotFound\n  * InvalidRequest\n  * InvalidParams\n  * InternalError\n  * ParseError\n  * ServerError - custom server errors\n  * RequestTimeout - Request exceeded maximum execution time\n  * ExecutionError - Method has returned an error.\n\nEvery exception object has following properties:\n\n1. \"code\" - numeric code from the spec\n2. \"message\" - human readable message.\n3. \"data\" - additional data. Used only by ExecutionError, contains error returned by method\n\n**How to return an error from method?**\n\nNothing special required. Just reject promise or throw an exception.\n\n```js\n\nfunction divide(a, b) {\n  if (b == 0) throw \"devision by zero\";\n  // throw 'new Error(\"devision by zero\")' will behave the same\n  return a / b;\n}\n\nfunction loadUser(userId) {\n  ...\n  // you can throw an object\n  return Promise.reject({ error: 'NOT_EXISTING_USER'})\n}\n\nserver.expose({ divide, loadUser });\n```\n\n**How to handle the error?**\n\nNothing special. Just catch the exception.\n\n```js\nconst X = require('mole-rpc/X');\n\nasync function main() {\n  ...\n\n  try {\n      await client.divide(2, 3);\n  } catch (error) {\n    if (error instanceof X.ExecutionError) {\n      console.log('METHOD RETURNED ERROR', error.data);\n    } else if (error instanceof X.RequestTimeout) {\n      console.log('METHOD EXCEEDED ALLOWED EXECUTION TIME');\n    } else {\n      throw error;\n    }\n  }\n}\n```\n\n## Advanced usage\n\n```javascript\n\n// Proxified client: explicit call\nawait calculator.callMethod.sum(1, 2); // the same as \"calculator.sum(1, 2)\"\n// Can be usefull if your server method is a reserverd name.\n// For example to make a call to remote \"notify\" method.\nawait proxifiedClient.callMethod.notify(\"Hello\");\n\n// Proxified client: run request in parallel\nconst promises = [\n  calculator.sum(1, 2);\n  calculator.notify.sum(1, 2);\n];\n\nconst results = await Promise.all(promises);\n\n// Simple client: run in parallel\nconst promises = [\n  client.callMethod('sum', [1, 2]);\n  client.notify('sum', [1, 2]);\n];\n\nconst results = await Promise.all(promises);\n\n// Simple client: run batch\nconst results = await client.runBatch([\n  // [methodName, params, mode]\n  ['sum', [1, 3]],\n  ['sum', [2, 5], 'notify'],\n  ['sum', [7, 9], 'callMethod'], // \"callMethod\" is optional\n]);\n\n\n// Result structure\n[\n  {success: true, result: 123},\n  null, // no response for notification\n  {success: false, error: errorObject}\n];\n\n```\n\n## Use cases\n\n### Case 1: Easy way to communicate with web-workers in your browser\n\nTo communicate with web worker, in most cases, you will try to simulate JSON RPC having \"id\", \"method\", \"params\" in each request and \"id\", \"result\" in each response.\n\nWith Mole RCP there is no need to use custom hacks. Just use [mole-rpc-transport-webworker](https://www.npmjs.com/package/mole-rpc-transport-webworker).\n\n\n### Case 2: Bypass firewall\n\nYou have a device (or service) in local network and want to manage it. You cannot get to it from Internet, as the device is hidden behind NAT. But your device can connect to your internet server. So, with Mole RPC your device (RPC Server) can connect to the your internet server (RPC Client) and after that the internet server will be able to call methods on the devices hidden behind NAT.\n\nHere is an example  - https://github.com/koorchik/node-mole-rpc-transport-ws/tree/master/examples/server-connects-to-client\n\nThis case is rather hard to implement with other JSON RPC modules but with Mole RPC it works by design.\n\n\n### Case 3: Microservices via message broker\n\nYou have a lot microservices and you want allow them to communicate with each other. The best solutions here is to have a message broker. Mole RPC has MQTT transport which will allow to setup the communication easily.\n\nSee, [mole-rpc-transport-mqtt](https://www.npmjs.com/package/mole-rpc-transport-mqtt)\n\n### Case 4: Lightweight Inter process communication\n\nWebsocket is a good options for it. With websocker transport you can connect browser to server and the same time it suitable for connecting to server processes. It is not only option, you not limited to use any transport you wish.\n\n### Case 5: Multi transport mode (HTTP, HTTPS, WS the same time, for example)\n\nYou can pass multiple transports to MoleServer. This transport can be of different types. For example, you can expose the same methods via MQTT and WebSockets the same time.\n\n## How to create own transport?\n\nTransports have simple API as possible, so it is very easy to add a new transport. MoleRPC has strong separation between protocol handler and transports. Transports know nothing about what is inside payload. Therefore, they are very simple. Usually, is is just two classes with 1-2 methods.\n\nThe best way to start is just to look at source code of existing implemenations - https://www.npmjs.com/search?q=keywords:mole-transport\n\nMoreover, we have created an [AutoTester](https://www.npmjs.com/package/mole-rpc-autotester) for transports. Use [AutoTester](https://www.npmjs.com/package/mole-rpc-autotester) to cover 95% of cases and just add several tests to cover transport specific logic like reconnections etc.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoorchik%2Fnode-mole-rpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoorchik%2Fnode-mole-rpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoorchik%2Fnode-mole-rpc/lists"}