RemJson
RemJson is a JSON-RPC 2.0 compliant server and client written in JavaScript for node.js that aims to be as simple as possible to use.
Table of contents
- Features
- Example
- Installation
- Changelog
- Requirements
- Class Documentation
- Running tests
- Usage
- Revivers and replacers
- Named parameters
- Contributing
Features
- Servers that can listen to several interfaces at the same time
- Supports both HTTP and TCP client and server connections
- Server-side method - [Method routing]
- Relaying of requests to other servers
- JSON reviving and replacing for transparent serialization of complex objects
- CLI client
- Fully tested to comply with the official JSON-RPC 2.0 specification
- Also supports JSON-RPC 1.0
Example
A basic JSON-RPC 2.0 server via HTTP:
Server in examples/simple_example/server.js:
var remjson = ; // create a servervar server = remjson; // Bind a http interface to the server and let it listen to localhost:3000server;
Client in examples/simple_example/client.js invoking add
on the above server:
var remjson = ; // create a clientvar client = remjsonclient; // invoke "add"client;
Installation
Install the latest version of jayson from npm by executing npm install remjson
in your shell. Do a global install with npm install --global remjson
if you want the remjson
client CLI in your PATH.
Changelog (notable milestones)
- 1.2
- Greatly improved server method definition
- 1.1.1
- More http server events
- Remove fork server and client
- Add server routing
- 1.0.11 Add support for a HTTPS client
- 1.0.10 Bugfixes
- 1.0.9 Add support for TCP servers and clients
CLI client
There is a CLI client in bin/remjson.js
and it should be available as remjson
in your shell if you installed the package with the --global
switch. Run remjson --help
to see how it works.
Requirements
RemJson does not have any special dependencies that cannot be resolved with a simple npm install
. It is being continuously tested using travis-ci on the following versions:
- node.js v0.8.x
- node.js v0.10.x
- node.js v0.12.x
- iojs
Class documentation
In addition to this document, a comprehensive class documentation made with jsdoc is available at remjson.tedeh.net.
Running tests
- Change directory to the repository root
- Install the testing framework
(mocha together with
should) by executing
npm install --dev
- Run the tests with
make test
ornpm test
Usage
Client
The client is available as the Client
or client
property of require('remjson')
.
Client interface description
Client
Base class for interfacing with a server.Client.tcp
TCP interfaceClient.tls
TLS interfaceClient.http
HTTP interfaceClient.https
HTTPS interface
Every client supports these options:
reviver
-> Function to use as a JSON reviverreplacer
-> Function to use as a JSON replacergenerator
-> Function to generate request ids with. If omitted, RemJson will just generate a "random" number that is RFC4122 compliant and looks similar to this:3d4be346-b5bb-4e28-bc4a-0b721d4f9ef9
version
-> Can be either1
or2
depending on which specification should be followed in communicating with the server. Defaults to2
for JSON-RPC 2.0
Client.http
Uses the same options as http.request in addition to these options:
encoding
-> String that determines the encoding to use and defaults to utf8
Client.http Events
The HTTP server will emit the following events:
http request
Emitted when the client has just created a HTTP request. First argument is an instance ofhttp.ClientRequest
http response
Emitted when the client has received an HTTP response. First argument is an instance ofhttp.IncomingMessage
and second argument an instance ofhttp.ClientRequest
.http error
Emitted when the underlying stream emitserror
. First argument is the error.http timeout
Emitter when the underlying stream emitstimeout
. When emitted, it will automatically cause the request to abort.
It is possible to pass a string URL as the first argument. The URL will be run through url.parse. Example:
var remjson = ;var client = remjsonclient;// client.options is now the result of url.parse
Client.https
Uses the same options as https.request in addition to the same options as Client.http
. This means it is also possible
to pass a string URL as the first argument and have it interpreted by url.parse.
Client.tcp
Uses the same options as the base class.
Client.tls
Uses the same options as tls.connect.
Notifications
Notification requests are for cases where the reply from the server is not important and should be ignored. This is accomplished by setting the id
property of a request object to null
.
Client in examples/notifications/client.js doing a notification request:
var remjson = ; var client = remjsonclient; // the third parameter is set to "null" to indicate a notificationclient;
A server in examples/notifications/server.js:
var remjson = ; var server = remjson; server;
Notes
- Any value that the server returns will be discarded when doing a notification request.
- Omitting the third argument
null
toClient.prototype.request
does not generate a notification request. This argument has to be set explicitly tonull
for this to happen. - Network errors and the like will still reach the callback. When the callback is invoked (with or without error) one can be certain that the server has received the request.
- See the Official JSON-RPC 2.0 Specification for additional information on how RemJson handles notifications that are erroneous.
Batches
A batch request is an array of individual requests that are sent to the server as one. Doing a batch request is very simple in RemJson and consists of constructing an Array
of individual requests (created by not passing a callback to Client.prototype.request
) that is then itself passed to Client.prototype.request
.
Client example in examples/batch_request/client.js:
var remjson = ;var client = remjsonclient; var batch = client client client // a notification; // callback takes two arguments (first type of callback)client; // callback takes three arguments (second type of callback)client;
Server example in examples/batch_request/server.js:
var remjson = ; var server = remjson; server;
Notes
- See the Official JSON-RPC 2.0 Specification for additional information on how RemJson handles different types of batches, mainly with regards to notifications, request errors and so forth.
- There is no guarantee that the results will be in the same order as request Array
request
. To find the right result, compare the ID from the request with the ID in the result yourself.
Client callback syntactic sugar
When the length (number of arguments) of a client callback function is either 2 or 3 it receives slightly different values when invoked.
- 2 arguments: first argument is an error or
null
, second argument is the response object as returned (containing either aresult
or aerror
property) ornull
for notifications. - 3 arguments: first argument is an error or null, second argument is a JSON-RPC
error
property ornull
(if success), third argument is a JSON-RPCresult
property ornull
(if error).
When doing a batch request with a 3-length callback, the second argument will be an array of requests with a error
property and the third argument will be an array of requests with a result
property.
Client events
A client will emit the following events (in addition to any special ones emitted by a specific interface):
request
Emitted when a client is just about to dispatch a request. First argument is the request object.response
Emitted when a client has just received a reponse. First argument is the request object, second argument is the response as received.
Server
The server classes are available as the Server
or server
property of require('remjson')
.
The server also sports several interfaces that can be accessed as properties of an instance of Server
.
Server interface description
Server
- Base interface for a server that supports receiving JSON-RPC 2.0 requests.Server.tcp
- TCP server that inherits from net.Server.Server.tls
- TLS server that inherits from tls.Server.Server.http
- HTTP server that inherits from http.Server.Server.https
- HTTPS server that inherits from https.Server.Server.middleware
- Method that returns a Connect/Express compatible middleware function.
Every server supports these options:
reviver
-> Function to use as a JSON reviverreplacer
-> Function to use as a JSON replacerrouter
-> Function to find which method to use for a request. See the chapter on method routing.collect
-> Collect JSON-RPC parameters inside a single function argument. Defaultfalse
. See method definitionparams
-> Force collected JSON-RPC parameters to beObject
orArray
. Defaultfalse
. See method definitionversion
-> Can be either1
or2
depending on which specification clients are expected to follow. Defaults to2
for JSON-RPC 2.0
Server.tcp
Uses the same options as the base class. Inherits from net.Server.
Server.tls
Uses the same options as the base class. Inherits from tls.Server.
Server.http
Uses the same options as the base class. Inherits from http.Server.
Server.https
Uses the same options as the base class. Inherits from https.Server and remjson.Server.http
. For information on how to configure certificates, see the documentation on https.Server.
Server.middleware
Uses the same options as the base class. Returns a function that is compatible with Connect or Express. Will expect the request to be req.body
, meaning that the request body must be parsed (typically using connect.bodyParser
) before the middleware is invoked.
The middleware supports the following options:
end
Defaults totrue
. If set tofalse
will cause the middleware tonext()
instead ofres.end()
at the end of a request.res.body
and the response header may or may not be set when the next middleware is called.
Middleware example in examples/middleware/server.js:
var remjson = ;var connect = ;var app = ; var server = remjson; // parse request body before the remjson middlewareapp;app; app;
Using many server interfaces at the same time
A RemJson server can use many interfaces at the same time.
Server in examples/many_interfaces/server.js that listens to both http
and a https
requests:
var remjson = ; var server = remjson; // "http" will be an instance of require('http').Servervar http = server; // "https" will be an instance of require('https').Servervar https = server; http; https;
Using the server as a relay
Passing an instance of a client as a method to the server makes the server relay incoming requests to wherever the client is pointing to. This might be used to delegate computationally expensive functions into a separate server or to abstract a cluster of servers behind a common interface.
Public server in examples/relay/server_public.js listening on *:3000
:
var remjson = ; // create a server where "add" will relay a localhost-only servervar server = remjson; // let the server listen to *:3000server;
Private server in examples/relay/server_private.js listening on localhost:3001:
var remjson = ; var server = remjson; // let the private server listen to localhost:3001server;
Every request to add
on the public server will now relay the request to the private server. See the client example in examples/relay/client.js.
Method routing
Passing a property named router
in the server options will enable you to write your own logic for routing requests to specific functions.
Server with custom routing logic in examples/method_routing/server.js:
var remjson = ;var format = format; var methods = { ; }; var server = remjson; server;
Client in examples/method_routing/client.js invoking add_2
on the above server:
var remjson = ; // create a clientvar client = remjsonclient; // invoke "add_2"client;
An example of nested routes where each property is separated by a dot (you do not need to use the router option for this):
var _ = ;var remjson = ; var methods = foo: { ; } math: { ; } ; // this reduction produces an object like this: {'foo.bar': [Function], 'math.add': [Function]}var map = _;var server = remjson; { return { var prop = stem ? stem + sep + key : key; if_ mapprop = value; else if_ map = _; return map; }}
Notes
- If
router
does not return anything, the reserver will respond with aMethod Not Found
error. - The
Server.prototype
methodsmethod
,methods
,removeMethod
andhasMethod
will not use therouter
method, but will operate on the internalServer.prototype._methods
map. - The
router
method is expected to return instances of remjson.Method (new in 1.2)
Method definition
You can also define server methods inside a wrapping object named remjson.Method
. This allows additional options about the method to be specified. It is possible to define what kind of params it expects, default values for these params, and wheter or not all JSON-RPC params should be collected in a single argument to the function. Using this wrapper, it is for instance trivial to have your method accept a variable amount of arguments.
Server example showcasing most features in examples/method_definitions/server.js:
var remjson = ;var _ = ; var methods = // this method gets the raw params as first arg to handler addCollect: { var total = ; ; } collect: true // means "collect all JSON-RPC parameters in one arg" // specifies some default values (alternate definition too) addDefault: remjson // this method returns true when it gets an array (which it always does) acceptArray: { var result = _; ; } collect: true params: Array // could also be "Object" ; var server = remjson; server; // sums all enumerable properties in a list { return _;}
Client example in examples/method_definitions/client.js:
var remjson = ; // create a clientvar client = remjsonclient; // invoke "addCollect" with arrayclient; // invoke "addCollect" with objectclient; // invoke "addDefault" with object missing some defined membersclient; // invoke "acceptArray" with an Objectclient;
Notes
- Adding methods as a plain JavaScript function creates an instance of
remjson.Method
internally. For backwards compatibility it will be created with the option "collect" set tofalse
. It is possible to affect this by passing thecollect
option to the server. This works similarly for theparams
option.
Server events
In addition to events that are specific to certain interfaces, all servers will emit the following events:
request
Emitted when the server receives an interpretable (non-batch) request. First argument is the request object.response
Emitted when the server is returning a response. First argument is the request object, the second is the response object.batch
Emitted when the server receives a batch request. First argument is an array of requests. Will emitrequest
for each interpretable request in the batch.
Server Errors
If you should like to return an error from an method request to indicate a failure, remember that the JSON-RPC 2.0 specification requires the error to be an Object
with a code (Integer/Number)
to be regarded as valid. You can also provide a message (String)
and a data (Object)
with additional information. Example:
var remjson = ; var server = remjson;
Predefined Errors
It is also possible to cause a method to return one of the predefined JSON-RPC 2.0 error codes using the server helper function Server.prototype.error
inside of a server method. Example:
var remjson = ; var server = remjson;
You can even override the default messages:
var remjson = ; var server = remjson; // Override the default messageservererrorMessagesServererrorsINTERNAL_ERROR = 'I has a sad. I cant do anything right';
Revivers and Replacers
JSON lacks support for representing types other than the simple ones defined in the JSON specification. Fortunately the JSON methods in JavaScript (JSON.parse
and JSON.stringify
) provide options for custom serialization/deserialization routines. RemJson allows you to pass your own routines as options to both clients and servers.
Simple example transferring the state of an object between a client and a server:
Shared code between the server and the client in examples/reviving_and_replacing/shared.js:
var Counter = exports { thiscount = value || 0;}; Counterprototype { thiscount += 1;}; exports { ifvalue instanceof Counter return $class: 'counter' $props: count: valuecount; return value;}; exports { ifvalue && value$class === 'counter' var obj = ; forvar prop in value$props objprop = value$propsprop; return obj; return value;};
Server in examples/reviving_and_replacing/server.js:
var remjson = ;var shared = ; // Set the reviver/replacer optionsvar options = reviver: sharedreviver replacer: sharedreplacer; // create a servervar server = remjson; // let the server listen to for http connections on localhost:3000server;
A client in examples/reviving_and_replacing/client.js invoking "increment" on the server:
var remjson = ;var shared = ; // create a client with the shared reviver and replacervar client = remjsonclient; // create the objectvar instance = 2; // invoke "increment"client;
Notes
- Instead of using a replacer, it is possible to define a
toJSON
method for any JavaScript object. Unfortunately there is no corresponding method for reviving objects (that would not work, obviously), so the reviver always has to be set up manually.
Named parameters
It is possible to specify named parameters when doing a client request by passing an Object instead of an Array.
Client example in examples/named_parameters/client.js:
var remjson = ; var client = remjsonclient; client;
Server example in examples/named_parameters/server.js:
var remjson = ; var server = remjson; server;
Notes
- If requesting methods on a RemJson server, arguments left out will be
undefined
- Too many arguments or arguments with invalid names will be ignored
- It is assumed that the last argument to a server method is the callback and it will not be filled with something else
Contributing
Highlighting issues or submitting pull requests on Github is most welcome.