Magic Bridge lets you call functions on a Node.JS server from a client, abstracting away HTTP.
Register a function on the server:
const newBridge = require('@magic-bridge/bridge')
// import bridge from '@magic-bridge/bridge'
const bridge = newBridge()
// register functions (or classes)
bridge.register(function getServerThing() {
return 'i am a string from server'
}
// use with express
const app = express()
app.use('/jsonrpc/default', bridge.middleware())
Now you can call it from the client:
const bridge = require('@magic-bridge/client')
// import bridge from '@magic-bridge/client'
const bridge = newBridge()
// call a function on the server!
const thingFromServer = await bridge.getServerThing()
// thingFromServer === 'i am a string from server'
At your server:
npm install @magic-bridge/bridge
At your client:
npm install @magic-bridge/client
- Create a bridge instance
- Registering functions
- Using middleware with Express
- Multple bridges
- Local arg resolvers
The Magic Bridge module exports a factory function for making new bridges both on the client and the server. The server optionally accepts some advanced options:
const newBridge = require('@magic-bridge/bridge')
const bridge = newBridge()
// or
const bridge = newBridge(opts)
Option | Description | Type | Default |
---|---|---|---|
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 |
/^_/ |
throwOnDup |
Will throw an error if more than one function is added with the same name | boolean |
true |
The client accepts one optional arguemnt; the url/path of the Magic Bridge middleware
const newBridge = require('@magic-bridge/client')
const bridge = newBridge() // uses /jsonrpc/default
// or
const bridge = newBridge('/my-magic-bridge-url')
the client bridge is now ready to call functions on the server:
// bridge calls must always be async
const thingFromServer = await bridge.doServerThing()
Magic Bridge provides a few ways of registering functions via register()
:
bridge.register(function func() { ... })
// client: await bridge.func()
const obj = {
x: 99,
func: function () {
return this.x
}
}
bridge.register(obj.func, obj)
// client: await bridge.func() --> 99
bridge.register('myFunc', () => { ... })
// client: await bridge.myFunc()
const obj = {
x: 99,
func: function() {
return this.x
}
}
bridge.register('myFunc', obj.func, obj)
// client: await bridge.myFunc() --> 99
This is quite handy, espcially in combination with multiple bridges
class Clazz {
myMethod() {
return 99
}
anotherMethod() {
return 88
}
}
const clazz = new Clazz()
bridge.register(clazz)
// client: await bridge.myMethod() --> 99
// client: await bridge.anotherMethod() --> 88
This is quite handy, espcially in combination with multiple bridges
const obj {
myFunction() {
return 99
}
anotherFunction() {
return 88
}
}
bridge.register(obj)
// client: await bridge.myFunction() --> 99
// client: await bridge.anotherFunction() --> 88
Magic Bridge is designed to be used as Express middleware. The default path used by
the client is /jsonrpc/default
, so the default set up is to do this:
const app = express()
app.use('/jsonrpc/default', bridge.middleware())
If you do use a different path, you must also configure the bridge side on the client to use it:
// client side:
const bridge = newBridge('/my-magic-bridge-path')
You can create multiple bridge instances in a single application, this useful for two main reasons:
- It acts as a namespace for classes or collections of related functions
- Registered functions/methods requiring different credentials can be used as separate middleware
const newBridge = require('@magic-bridge/bridge')
const auth = new Auth();
const account = new Account()
const authBridge = newBridge()
const accountBridge = newBridge()
app.use('/jsonrpc/auth', authBridge.middleware())
app.use('/jsonrpc/account', ensureLoggedIn(), accountBridge.middleware())
and on the client:
const newBridge = require('@magic-bridge/client')
const authBridge = newBridge('/jsonrpc/auth')
const accountBridge = newBridge('/jsonrpc/account')
await authBridge.login()
await accountBridge.changeUsername()
Functions on the server can be injected with request
, response
, request.session
and request.cookie
arguments that
are invisible to the client.
Use arguments with these method names in any postion to have them injected:
Argument | Resolves to |
---|---|
_request_ |
The (express) request object |
_response_ |
The (express) response object |
_session_ |
The (express) request session object (if using express-session middleware |
_cookies_ |
Parsed request cookies |
Examples:
function changeUsername(_session_, newUsername) {
const user = db.getUser(_session_.userId)
user.setUsername(newUsername)
...
}
function changeUsername(newUsername, _cookies_) {
const user = db.getUser(_cookies_.token)
user.setUsername(newUsername)
...
}
Magic Bridge is inspired by JSON-RPC-Java (later renamed Jabsorb) a library I used circa 2006-8 to do Ajaxy stuff.
Magic bridge partially implements the JSON-RPC 2.0 specification (it doesn't do batches)