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)