{"id":19753225,"url":"https://github.com/philmander/magic-bridge","last_synced_at":"2026-05-13T18:02:50.110Z","repository":{"id":37972817,"uuid":"498477786","full_name":"philmander/magic-bridge","owner":"philmander","description":null,"archived":false,"fork":false,"pushed_at":"2022-07-18T00:51:16.000Z","size":13114,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-11T05:18:36.479Z","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/philmander.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}},"created_at":"2022-05-31T19:52:12.000Z","updated_at":"2022-06-20T21:09:09.000Z","dependencies_parsed_at":"2022-07-18T05:21:44.466Z","dependency_job_id":null,"html_url":"https://github.com/philmander/magic-bridge","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philmander%2Fmagic-bridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philmander%2Fmagic-bridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philmander%2Fmagic-bridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/philmander%2Fmagic-bridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/philmander","download_url":"https://codeload.github.com/philmander/magic-bridge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241084573,"owners_count":19907132,"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-11-12T02:52:11.624Z","updated_at":"2026-05-13T18:02:45.078Z","avatar_url":"https://github.com/philmander.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"Magic Bridge\n====================\n\nMagic Bridge lets you call functions on a Node.JS server from a client, abstracting away HTTP.\n\nRegister a function on the server:\n\n```js\nconst newBridge = require('@magic-bridge/bridge')\n// import bridge from '@magic-bridge/bridge'\nconst bridge = newBridge()\n\n// register functions (or classes)\nbridge.register(function getServerThing() {\n  return 'i am a string from server'\n}\n\n// use with express \nconst app = express()\napp.use('/jsonrpc/default', bridge.middleware())\n```\n\nNow you can call it from the client:\n\n```js\nconst bridge = require('@magic-bridge/client')\n// import bridge from '@magic-bridge/client'\nconst bridge = newBridge()\n\n// call a function on the server!\nconst thingFromServer = await bridge.getServerThing()\n// thingFromServer === 'i am a string from server'\n```\n\n## Install\n\nAt your server:\n\n```\nnpm install @magic-bridge/bridge\n```\n\nAt your client:\n\n```\nnpm install @magic-bridge/client\n```\n\n## Usage\n\n- [Create a bridge instance](#create-a-bridge-instance)\n- [Registering functions](#registering-functions)\n  - [Just a function](#just-a-function)\n  - [A function with context](#a-function-with-context)\n  - [A function with a given name](#a-function-with-a-given-name)\n  - [A function with a given name and context](#a-function-with-a-given-name-and-context)\n  - [An instance of a class](#an-instance-of-a-class)\n  - [A plain object](#functions-on-a-plain-object)\n- [Using middleware with Express](#using-middleware-with-express)\n- [Multple bridges](#multple-bridges)\n- [Local arg resolvers](#local-arg-resolvers)\n\n### Create a bridge instance\n\nThe Magic Bridge module exports a factory function for making new bridges both on the client and the server. \nThe server optionally accepts some advanced options:\n\n#### On the server\n\n```js\nconst newBridge = require('@magic-bridge/bridge')\n\nconst bridge = newBridge()\n// or\nconst bridge = newBridge(opts)\n```\n\n| Option | Description | Type | Default |\n|----------|-------------|----|---------|\n| `ignoreMethodNameRegex` | Functions registered that match this regex will be ignored. Such as methods on a class starting with an underscore to denote that they are private | `regex` | `/^_/` |\n| `throwOnDup` | Will throw an error if more than one function is added with the same name | `boolean` | `true` | \n\n\n#### On the client\n\nThe client accepts one optional arguemnt; the url/path of the [Magic Bridge middleware](#express)\n\n```js\nconst newBridge = require('@magic-bridge/client')\n\nconst bridge = newBridge() // uses /jsonrpc/default\n// or \nconst bridge = newBridge('/my-magic-bridge-url')\n```\n\nthe client bridge is now ready to call functions on the server:\n\n```js\n// bridge calls must always be async\nconst thingFromServer = await bridge.doServerThing()\n```\n\n### Registering functions\n\nMagic Bridge provides a few ways of registering functions via `register()`:\n\n#### Just a function\n\n```js\nbridge.register(function func() { ... })\n// client: await bridge.func()\n```\n\n#### A function with context\n\n```js\nconst obj = {\n  x: 99,\n  func: function () { \n    return this.x\n  }\n}\nbridge.register(obj.func, obj)\n// client: await bridge.func() --\u003e 99\n```\n\n#### A function with a given name\n\n```js\nbridge.register('myFunc', () =\u003e { ... })\n// client: await bridge.myFunc()\n```\n\n#### A function with a given name and context\n\n```js\nconst obj = {\n  x: 99,\n  func: function() { \n    return this.x\n  }\n}\nbridge.register('myFunc', obj.func, obj)\n// client: await bridge.myFunc() --\u003e 99\n```\n\n#### An instance of a class\n\nThis is quite handy, espcially in combination with [multiple bridges](#multiple-bridges)\n\n```js\nclass Clazz {\n  myMethod() {\n    return 99\n  }\n  anotherMethod() {\n    return 88\n  }\n}\nconst clazz = new Clazz()\nbridge.register(clazz)\n// client: await bridge.myMethod() --\u003e 99\n// client: await bridge.anotherMethod() --\u003e 88\n```\n\n#### Functions on a plain object\n\nThis is quite handy, espcially in combination with [multiple bridges](#multiple-bridges)\n\n```js\nconst obj {\n  myFunction() {\n    return 99\n  }\n  anotherFunction() {\n    return 88\n  }\n}\n\nbridge.register(obj)\n// client: await bridge.myFunction() --\u003e 99\n// client: await bridge.anotherFunction() --\u003e 88\n```\n\n### Using middleware with Express\n\nMagic Bridge is designed to be used as Express middleware. The default path used by\nthe client is `/jsonrpc/default`, so the default set up is to do this:\n\n```js\nconst app = express()\napp.use('/jsonrpc/default', bridge.middleware())\n```\n\nIf you do use a different path, you must also configure the bridge side on the client to use it:\n\n```js\n// client side:\nconst bridge = newBridge('/my-magic-bridge-path')\n```\n\n### Multple bridges\n\nYou can create multiple bridge instances in a single application, this useful for two main reasons:\n\n1. It acts as a namespace for classes or collections of related functions\n2. Registered functions/methods requiring different credentials can be used as separate middleware\n\n```js\nconst newBridge = require('@magic-bridge/bridge')\n\nconst auth = new Auth();\nconst account = new Account()\n\nconst authBridge = newBridge()\nconst accountBridge = newBridge()\n\napp.use('/jsonrpc/auth', authBridge.middleware())\napp.use('/jsonrpc/account', ensureLoggedIn(), accountBridge.middleware())\n```\n\nand on the client:\n\n```js\nconst newBridge = require('@magic-bridge/client')\n\nconst authBridge = newBridge('/jsonrpc/auth')\nconst accountBridge = newBridge('/jsonrpc/account')\n\nawait authBridge.login()\nawait accountBridge.changeUsername()\n```\n\n### Local arg resolvers\n\nFunctions on the server can be injected with `request`, `response`, `request.session` and `request.cookie` arguments that \nare invisible to the client.\n\nUse arguments with these method names in any postion to have them injected:\n\n| Argument | Resolves to |\n|----------|-------------|\n| `_request_` | The (express) request object |\n| `_response_` | The (express) response object |\n| `_session_` | The (express) request session object (if using [express-session](https://github.com/expressjs/session) middleware|\n| `_cookies_`   | Parsed request cookies |\n\nExamples: \n\n```js\nfunction changeUsername(_session_, newUsername) {\n  const user = db.getUser(_session_.userId)\n  user.setUsername(newUsername)\n  ...\n}\n```\n\n```js\nfunction changeUsername(newUsername, _cookies_) {\n  const user = db.getUser(_cookies_.token)\n  user.setUsername(newUsername)\n  ...\n}\n```\n\n## Credits\n\nMagic Bridge is inspired by [JSON-RPC-Java (later renamed Jabsorb)](https://arthurblake.wordpress.com/2007/09/14/announcing-jabsorb-a-new-json-rpc-library/) a library I used circa 2006-8 to do Ajaxy stuff.\n\nMagic bridge partially implements the [JSON-RPC 2.0 specification](https://www.jsonrpc.org/specification) (it doesn't do batches)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilmander%2Fmagic-bridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphilmander%2Fmagic-bridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphilmander%2Fmagic-bridge/lists"}