json-rpc-core
Transport agnostic JSON RPC message handling API meant for streams and promises.
Install
npm install --save json-rpc-core
Usage
This library is meant to be used with some transport layer, it can be any type, Redis, TCP, UDP, SocketIO, etc.
All this library does is create a node stream compatible interface to pipe in incoming messages and pipe out outgoing messages.
As long as your server and client classes are syncronous or return promises then you can wrap them with JSON-RPC-Core add a transport layer
and you have a working RPC interface.
var RPC = require('json-rpc-core')
var Emitter = require('events')
var rpcMethods = {
echo:function(msg){
return msg
}
}
var emitter = new Emitter()
function makeEmitterRPC(localid,remoteid,methods){
var rpc = RPC(methods)
emitter.on(localid,function(msg){
rpc.write(msg)
})
rpc.on('data',function(msg){
emitter.emit(remoteid,msg)
})
return rpc
}
var client = makeEmitterRPC('client','server',rpcMethods)
var server = makeEmitterRPC('server','client',rpcMethods)
client.call('echo','message to echo').then(function(result){
})
client.notify('knock knock')
client.discover().then(function(result){
})
API
Initialization
Create the rpc core stream.
var RPC = require('json-rpc-core')
var rpc = RPC(methods,timeoutMS)
Parameters
- methods (optional) - methods which are available to be called over RPC. Functions must be syncronous or use promises.
- timeoutMS (optional) - defaults to 10000, 10 seconds. Milliseconds that messages should timeout when waiting for response. Can be disabled if set to 0, but this is not recommended
as requests will pile up waiting for responses from remote.
Returns
A highland stream. This stream conforms to node streams, but adds additional useful functions.
Call
Call a remote function by name. Allows any number of parameters. Returns a promise which can resolve or reject.
Nested functions are now supported using lodash's "get" syntax. Seperate nested paths as an array or using
"dot" notation. Be aware that nested calls get serialized to a string with '.'. Call can also access non functions,
and will try to return them as is. This will allow you to remotely access to the prototype or other parts of your class.
rpc.call(remoteFunction,param1,param2, etc...).then(function(result){
})
rpc.call(['deeply','nested','method']).then(function(result)...
rpc.call('deeply.nested.method').then(function(result)...
rpc.call('plainObject').then(function(result)...
Parameters
Parameters are variable, they will just be passed through to remote function
- remoteFunction (required) - the name of the remote function you wish to call
- params_n (optional) - N number of parameters which the remote functions requires
Returns
Promise with the result from the remote call
Notify
Notify the remote, essentially cause a specific event to fire. Certain events names are reserved since they conflict
with the node stream api: 'data', 'close', 'drain', 'error', 'finish', 'pipe', 'unpipe', 'end', 'readable'.
The library does not prevent you from emitting these events but the resulting behavior is undefined.
rpc.notify(eventName,param1,param2, etc...)
Parameters
Parameters are variable, they will just be passed through to remote function
- eventName (required) - the name of the event you want to emit on the remote
- params_n (optional) - N number of parameters which get passed into the event callback.
Returns
Nothing
Discover
Utility function to discover remote method names. This is implemented as a custom RPC message "rpc_discover". The
JSON RPC protocol allows for custom methods signified by the "rpc" prefix. If a conflict is found with a local method
the local method will override the extension. Discover will not return nested methods.
rpc.discover().then(function(result){
})
Returns
A promise which resolves to an array of callable remote method names. Use in conjuction with rpc.createRemoteCalls.
Create Remote Calls
Produces a new object with functions keyed by the function names you pass into it.
The functions are just shortcuts to the rpc.call
function. Use in conjuction
with rpc.discover
to create an object which mirrors the remote API calls.
rpc.discover().then(function(methods){
var server = rpc.createRemoteCalls(methods)
})
Parameters
This takes an array of strings, it just creates an object keyed by the strings
- methods (required) - an array of strings which you want to turn into function calls
Returns
An object keyed by the string names which act as a shortcut to rpc.call
Echo
Utility function which allows you to "ping" the remote with a message, like a connectivity test. This is implemented as a custom RPC message "rpc_echo". The
JSON RPC protocol allows for custom methods signified by the "rpc" prefix. If a conflict is found with a local method
the local method will override the extension.
rpc.echo(message)
Parameters
Function takes single paramter
- message (optional) - a message you want echoed back to you.
Returns
A promise which resolves to your original message.
Errors
Errors can be thrown from the remote service syncronously or through an asycnronous promise. The RPC library
will attempt to translate a JSON RPC error into a standard node Error with a message and stack trace
and reject it through the promise. The stacktrace, if available, will be the stack from the server side error.
rpc.call('functionThatThrowsError').catch(function(err){
err == {
message:'your error message',
stack:'stacktrace...'
}
})
rpc.call('functionThatDoesNotExist').catch(function(err){
err == {
message:'The JSON-RPC method does not exist, or is an invalid one.',
stack:null
}
})
You should always add a catch block at some point to your function calls.
The most recent version of json-rpc-core adds stack trace data when available.
Previous Versions
Anyone using a version before 1.2 should upgrade, The previous message stream
implementation messages only allowed messages to resolve in order. This would be an issue if there are
major delays in responding to requests, causing essentially a traffic jam in serving requests.