Jayson

JSON-RPC 2.0/1.0 client and server for Node.js

README

Jayson


Jayson is a [JSON-RPC 2.0][jsonrpc-spec] and [1.0][jsonrpc1-spec] compliant server and client written in JavaScript for [node.js][node.js] that aims to be as simple as possible to use.

[jsonrpc-spec]: http://jsonrpc.org/spec.html
[jsonrpc1-spec]: http://json-rpc.org/wiki/specification
[node.js]: http://nodejs.org/
[jayson-npm]: https://www.npmjs.com/package/jayson
[jayson-travis]: https://travis-ci.org/tedeh/jayson
[badge-travis]: https://img.shields.io/travis/tedeh/jayson/master.svg
[badge-npm]: https://img.shields.io/npm/v/jayson.svg
[badge-downloads-month]: https://img.shields.io/npm/dm/jayson.svg
[![travis build status][badge-travis]][jayson-travis]  Coverage Status
GitHub issues
Dependents (via libraries.io)
node-current
Libraries.io dependency status for latest release [![npm version][badge-npm]][jayson-npm] [![npm][badge-downloads-month]][jayson-npm]
npm bundle size
npm bundle size Known Vulnerabilities

Table of contents


  - Client
     - Browser usage
     - Notifications
     - Batches
     - Events
  - Server
     - Method routing
     - Events
     - Errors
     - CORS
     - Context
  - Batches
- FAQ

Features


Supports both HTTP and TCP client and server connections
Fully tested to comply with the [official JSON-RPC 2.0 specification][jsonrpc-spec]
Also supports [JSON-RPC 1.0][jsonrpc1-spec]

Example


A basic JSON-RPC 2.0 server via HTTP:


  1. ``` js
  2. const jayson = require('jayson');

  3. // create a server
  4. const server = new jayson.Server({
  5.   add: function(args, callback) {
  6.     callback(null, args[0] + args[1]);
  7.   }
  8. });

  9. server.http().listen(3000);
  10. ```

Client example in examples/simple_example/client.js invokingadd on the above server:

  1. ``` js
  2. const jayson = require('jayson');

  3. // create a client
  4. const client = jayson.Client.http({
  5.   port: 3000
  6. });

  7. // invoke "add"
  8. client.request('add', [1, 1], function(err, response) {
  9.   if(err) throw err;
  10.   console.log(response.result); // 2
  11. });
  12. ```

Installation


Install the latest version of _jayson_ from npm by executingnpm install jayson in your shell. Do a global install with npm install --global jayson if you want the jayson client CLI in your PATH.

Changelog (only notable milestones/changes)


- 4.0.0
  - Remove lodash dependency which should halve bundle size. There might be minor incompatibilities if you pass funky object or array types to jayson methods.
- 3.6.4
  - Websocket client and server support
- 3.6.1
  - JSON-RPC 2.0 notifications no longer have id property unless overridden
- 3.3.3
  - Promise support for browser client
  - TypeScript declaration for promise browser client
  - TypeScript declaration for browser client
- 3.3.0
  - Remove URL parsing when passing a string option to the TLS and TCP client, string options are instead treated as an IPC path
- 3.0.0
  - Can pass a context object to handlers
  - _Breaking_: collect option removed from jayson.Server/Method. JSON-RPC params to handlers are now always in the first argument.
- 2.1.0
  - Experimental typescript support
- 2.0.6
  - Clarified how to use in the browser
- 2.0.0
  - _Breaking_: collect: true is now the default option for a new jayson.Server and jayson.Method
- 1.2.0
  - 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.9
  Add support for TCP servers and clients

CLI client


There is a basic CLI client in bin/jayson.js and it should be available as jayson in your shell if you installed the package globally. Run jayson --help to see how it works.

Requirements


Jayson does not have any special dependencies that cannot be resolved with a simple npm install. It is being [continuously tested][jayson-travis] using travis-ci. You can look inside .travis.yml if you want to see which versions are tested against.

Class documentation


In addition to this document, a comprehensive class documentation made with [jsdoc][jsdoc-spec] is available at jayson.tedeh.net.

[jsdoc-spec]: http://usejsdoc.org/

Running tests


- Change directory to the repository root
- Install the development packages by executing npm install --dev
- Run the tests with npm run test
- Run the typescript tests with npm run test-tsc
- Run the coverage tests with npm run coverage

Typescript


Since v2.1.0 there is typescript support available with jayson.

If you encounter any problems with the type definitions, see the Contributing section.

Usage


Client


The client is available as the Client or client property of require('jayson').

Client interface description


NameDescription
|--------------------|---------------------|
`Client`Base
`Client.tcp`TCP
`Client.tls`TLS
`Client.http`HTTP
`Client.https`HTTPS
`Client.browser`Standalone
`Client.websocket`Websocket

Every client supports these options:

OptionDefaultTypeDescription
|----------------------|------------------------------------|------------|------------------------------------------------------------------------------------------|
`reviver``undefined``Function``JSON.parse`
`replacer``undefined``Function``JSON.stringify`
`generator`[RFC4122][rfc_4122_spec]`Function`Generates
`version`2`Number`JSON-RPC
`notificationIdNull``false``Boolean`*Since

[rfc_4122_spec]: http://www.ietf.org/rfc/rfc4122.txt

Client.http

Uses the same options as [http.request][nodejs_docs_http_request] in addition to these options:

OptionDefaultTypeDescription
|------------|------------|----------|----------------------------------------
`encoding``utf8``String`Determines
`headers``undefined``Object`Extend

Client.http Events

The HTTP client will emit the following events:

EventWhenArgumentsNotes
|-----------------|-----------------------------------|---------------------------------------------------------------------------|-------------------------------------------
`httpCreated1.|
`httpReceived1.|
`httpUnderlying1.|
`httpUnderlying|

It is possible to pass a string URL as the first argument. The URL will be run through [url.parse][nodejs_docs_url_parse]. Example:

  1. ``` js
  2. const jayson = require('jayson');
  3. const client = jayson.Client.http('http://localhost:3000');
  4. // client.options is now the result of url.parse
  5. ```

[nodejs_docs_http_request]: http://nodejs.org/docs/latest/api/http.html#http_http_request_options_callback
[nodejs_docs_url_parse]: http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost

Client.https

Uses the same options as [https.request][nodejs_docs_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][nodejs_docs_url_parse].

Will emit the same custom events asClient.http.

[nodejs_docs_https_request]: http://nodejs.org/api/all.html#all_https_request_options_callback

Client.tcp

Uses the same options as [net.connect][nodejs_docs_net_connect].

Client.tcp Events

Since version 3.5.1

The TCP client will emit the following events:

EventWhenArgumentsNotes
|------------|------------------------|-------------------------------|-------------------------------|
tcpTCP1.Can
tcpTCP1.|

[nodejs_docs_net_connect]: https://nodejs.org/api/net.html#net_net_connect

Client.tls

Uses the same options as [tls.connect][nodejs_docs_tls_connect].

Client.tls Events

Since version 3.5.1

The TLS client will emit the following events:

EventWhenArgumentsNotes
|------------|------------------------|-------------------------------|-------------------------------|
tcpTCP1.Can
tcpTCP1.|

[nodejs_docs_tls_connect]: https://nodejs.org/api/tls.html#tls_tls_connect_options_callback

Client.browser

The browser client is a simplified version of the regular client for use browser-side. It does not have any dependencies on node.js core libraries, but does depend on the uuid package for generating request ids. It also does not know how to "send" a request to a server like the other clients.

Because it does not depend on any core libraries, the browser client is not an instance of JaysonClient or EventEmitter and therefore does not emit any of the normal request events that the other clients do.

To use the browser client, require('jayson/lib/client/browser') and pass a calling/transport function as the first argument. The transport function receives a JSON-RPC string request and is expected to callback with a string response received from the server (not JSON) or an error (not a JSON-RPC error).

The reason for dealing with strings is to support the reviver and replacer options like the other clients.

This client example in examples/browser_client/client.js below uses node-fetch in the transport function, but a dropin replacement for use in anactual browser could instead use whatwg-fetch.

The browser client has a separate TypeScript type declaration available in jayson/lib/client/browser/index.d.ts which depends on the main Jayson type declaration.

  1. ``` js
  2. 'use strict';

  3. const jaysonBrowserClient = require('jayson/lib/client/browser');
  4. const fetch = require('node-fetch');

  5. const callServer = function(request, callback) {
  6.   const options = {
  7.     method: 'POST',
  8.     body: request,
  9.     headers: {
  10.       'Content-Type': 'application/json',
  11.     }
  12.   };

  13.   fetch('http://localhost:3000', options)
  14.     .then(function(res) { return res.text(); })
  15.     .then(function(text) { callback(null, text); })
  16.     .catch(function(err) { callback(err); });
  17. };

  18. const client = new jaysonBrowserClient(callServer, {
  19.   // other options go here
  20. });

  21. client.request('multiply', [5, 5], function(err, error, result) {
  22.   if(err) throw err;
  23.   console.log(result); // 25
  24. });
  25. ```

Client.websocket

Since v3.6.4

Experimental websocket client that wraps around an isomorphic-ws instance. Will listen to every received (JSON) message and see if it matches any of the currently outstanding requests made, in which case the callback of that outstanding request will fire. If you do not provide the timeout option it will wait forever. Has a promise-based equivalent receiving the same options, and a companion jayson server where you can find an example.

Has the following options:

OptionDefaultTypeDescription
|-----------|-------------|-------------------------------------|----------------------------------------------------------------------|
`url``undefined``String`First
`ws``undefined``require('isomorphic-ws')`WebSocket
`timeout``undefined``Number`Timeout

If you want to "unwrap" the isomorphic-ws instance you can use the Client.websocket.prototype.unlisten which stops listening for messages on the isomorphic-ws instance.


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 example in examples/notifications/client.js doing a notification request:

  1. ``` js
  2. const jayson = require('jayson');

  3. const client = jayson.Client.http({
  4.   port: 3000
  5. });

  6. // the third parameter is set to "null" to indicate a notification
  7. client.request('ping', [], null, function(err) {
  8.   if(err) throw err;
  9.   console.log('ok'); // request was received successfully
  10. });
  11. ```


  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   ping: function(args, callback) {
  5.     // do something, do nothing
  6.     callback();
  7.   }
  8. });

  9. server.http().listen(3000);
  10. ```

Notes

Any value that the server returns will be discarded when doing a notification request.
Omitting the third argument null to Client.prototype.request does not generate a notification request. This argument has to be set explicitly to null 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][jsonrpc-spec] for additional information on how Jayson handles notifications that are erroneous.
Since 3.6.1 When making a JSON-RPC 2.0 notification request the "id" property will be omitted in the request object. In previous versions it was set to null against the recommendation of the official specification. This behaviour can be overridden with the notificationIdNull option.


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 Jayson 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.

Combined server/client example in examples/batch_request/index.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   add: function(args, callback) {
  5.     callback(null, args[0] + args[1]);
  6.   }
  7. });

  8. const client = new jayson.Client(server);

  9. const batch = [
  10.   client.request('does_not_exist', [10, 5]),
  11.   client.request('add', [1, 1]),
  12.   client.request('add', [0, 0], null) // a notification
  13. ];

  14. client.request(batch, function(err, errors, successes) {
  15.   if(err) throw err;
  16.   console.log('errors', errors); // array of requests that errored
  17.   console.log('successes', successes); // array of requests that succeeded
  18. });

  19. client.request(batch, function(err, responses) {
  20.   if(err) throw err;
  21.   console.log('responses', responses); // all responses together
  22. });
  23. ```

Notes

See the [Official JSON-RPC 2.0 Specification][jsonrpc-spec] for additional information on how Jayson 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_ a result or a error property) or null for notifications.
3 arguments: first argument is an error or null, second argument is a JSON-RPC error property or null (if success), third argument is a JSON-RPC result property or null (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):

EventWhenArgumentsNotes
|------------|-----------------------------|-----------------------------------------------|-------
`request`About1:|
`response`Received1:|

Server


The server classes are available as the Server or server property of require('jayson').

The server also sports several interfaces that can be accessed as properties of an instance of Server.

Server interface description


NameDescription
|---------------------|--------------------------------------------------------------------------------------------
`Server`Base
`Server.tcp`TCP
`Server.tls`TLS
`Server.http`HTTP
`Server.https`HTTPS
`Server.websocket`Websocket
`Server.middleware`Method

[isomorphic-ws-docs]: https://github.com/heineiuo/isomorphic-ws
[nodejs_doc_net_server]: http://nodejs.org/docs/latest/api/net.html#net_class_net_server
[nodejs_doc_http_server]: http://nodejs.org/docs/latest/api/http.html#http_class_http_server
[nodejs_doc_https_server]: http://nodejs.org/docs/latest/api/https.html#https_class_https_server
[nodejs_doc_tls_server]: https://nodejs.org/api/tls.html#tls_class_tls_server
[connect]: http://www.senchalabs.org/connect/
[express]: http://expressjs.com/

Servers supports these options:

OptionDefaultTypeDescription
|---------------------|-----------------|---------------------|-----------------------------------------------------------
`reviver``null``Function``JSON.parse`
`replacer``null`Function``JSON.stringify`
`router``null`Function`Return
`useContext``false``Boolean`Passed
`params``undefined``Array/Object/null`Passed
`methodConstructor``jayson.Method``Function`Server
`version`2`Number`JSON-RPC

Server.tcp

Uses the same options as the base class. Inherits from [net.Server][nodejs_doc_net_server].

Server.tls

Uses the same options as the base class. Inherits from [tls.Server][nodejs_doc_tls_server].

Server.http

Uses the same options as the base class. Inherits from [http.Server][nodejs_doc_http_server].

Server.http Events

EventWhenArgumentsNotes
|-----------------|-------------------------------|------------------------------------------------------------------------------|-------
`httpIncoming1.|
`httpAbout1.|

Server.https

Uses the same options as the base class. Inherits from [https.Server][nodejs_doc_https_server] and jayson.Server.http. For information on how to configure certificates, [see the documentation on https.Server][nodejs_doc_https_server].

Will emit the same custom events asServer.http.

Server.middleware

Uses the same options as the base class. Returns a function that is compatible with [Connect][connect] or [Express][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:

OptionDefaultTypeDescription
|--------|---------|-----------|-------------------------------------------------------------------------------------------
`end``true``Boolean`If

Middleware example in examples/middleware/server.js:

  1. ``` js
  2. const jayson = require('jayson');
  3. const jsonParser = require('body-parser').json;
  4. const connect = require('connect');
  5. const app = connect();

  6. const server = new jayson.Server({
  7.   add: function(args, callback) {
  8.     callback(null, args[0] + args[1]);
  9.   }
  10. });

  11. // parse request body before the jayson middleware
  12. app.use(jsonParser());
  13. app.use(server.middleware());

  14. app.listen(3000);
  15. ```

Server.websocket

Websocket server that either wraps around a provided require('isomorphic-ws').Server instance or creates one from scratch. Expects every incoming message on every connection to be a valid JSON-RPC call.

The websocket server supports the following options in addition to the base class:

OptionDefaultTypeDescription
|--------|-------------|-----------------------------------|---------------------------------|
`wss``undefined``require('isomorphic-ws').Server`If


Websocket server example in examples/websocket/server.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   add: function (args, done) {
  5.     const sum = args.reduce((sum, val) => sum + val, 0);
  6.     done(null, sum);
  7.   },
  8. });

  9. const wss = server.websocket({
  10.   port: 12345,
  11. });
  12. ```

Websocket client example in examples/websocket/client.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. const client = jayson.Client.websocket({
  4.   url: 'ws://localhost:12345',
  5. });

  6. client.ws.on('open', function () {
  7.   client.request('add', [1,2,3,4], function (err, result) {
  8.     console.log(err, result);
  9.     client.ws.close();
  10.   });
  11. });
  12. ```

Many interfaces at the same time


A Jayson server can use many interfaces at the same time.

Server example in examples/many_interfaces/server.js that listens to bothhttp and a https requests:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server();

  4. // "http" will be an instance of require('http').Server
  5. const http = server.http();

  6. // "https" will be an instance of require('https').Server
  7. const https = server.https({
  8.   //cert: require('fs').readFileSync('cert.pem'),
  9.   //key require('fs').readFileSync('key.pem')
  10. });

  11. http.listen(80, function() {
  12.   console.log('Listening on *:80');
  13. });

  14. https.listen(443, function() {
  15.   console.log('Listening on *:443');
  16. });
  17. ```

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.

Frontend server example in examples/relay/server_public.js listening on*:3000:

  1. ``` js
  2. const jayson = require('jayson');

  3. // create a server where "add" will relay a localhost-only server
  4. const server = new jayson.Server({
  5.   add: jayson.Client.http({
  6.     port: 3001
  7.   })
  8. });

  9. // let the frontend server listen to *:3000
  10. server.http().listen(3000);
  11. ```

Backend server example in examples/relay/server_private.js listening on*:3001:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   add: function(args, callback) {
  5.     callback(null, args[0] + args[1]);
  6.   }
  7. });

  8. // let the backend listen to *:3001
  9. server.http().listen(3001);
  10. ```

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 example with custom routing logic in examples/method_routing/server.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. const methods = {
  4.   add: function(args, callback) {
  5.     callback(null, args[0] + args[1]);
  6.   }
  7. };

  8. const server = new jayson.Server(methods, {
  9.   router: function(method, params) {
  10.     // regular by-name routing first
  11.     if(typeof(this._methods[method]) === 'function') return this._methods[method];
  12.     if(method === 'add_2') {
  13.       const fn = server.getMethod('add').getHandler();
  14.       return new jayson.Method(function(args, done) {
  15.         args.unshift(2);
  16.         fn(args, done);
  17.       });
  18.     }
  19.   }
  20. });

  21. server.http().listen(3000);
  22. ```

Client example in examples/method_routing/client.js invokingadd_2 on the above server:

  1. ``` js
  2. const jayson = require('jayson');

  3. // create a client
  4. const client = jayson.Client.http({
  5.   port: 3000
  6. });

  7. // invoke "add_2"
  8. client.request('add_2', [3], function(err, response) {
  9.   if(err) throw err;
  10.   console.log(response.result); // 5!
  11. });
  12. ```

Server example of nested routes where each property is separated by a dot (you do not need to use the router option for this):

  1. ``` js
  2. const jayson = require('jayson');

  3. const methods = {
  4.   foo: {
  5.     bar: function(callback) {
  6.       callback(null, 'ping pong');
  7.     }
  8.   },
  9.   math: {
  10.     add: function(args, callback) {
  11.       callback(null, args[0] + args[1]);
  12.     }
  13.   }
  14. };

  15. // this reduction produces an object like this: {'foo.bar': [Function], 'math.add': [Function]}
  16. const map = Object.keys(methods).reduce(collapse('', '.', methods), {});
  17. const server = new jayson.Server(map);

  18. function collapse(stem, sep, obj) {
  19.   return function(map, key) {
  20.     const prop = stem ? stem + sep + key : key;
  21.     const value = obj[key];
  22.     if(typeof value === 'function') map[prop] = value;
  23.     else if(typeof value === 'object' && value !== null) map = Object.keys(value).reduce(collapse(prop, sep, value), map);
  24.     return map;
  25.   }
  26. }
  27. ```

Notes

If router does not return anything, the server will respond with a Method Not Found error.
The Server.prototype methods method, methods, removeMethod and hasMethod will not use the router method, but will operate on the internal Server.prototype._methods map.
The router method is expected to return instances of jayson.Method (>=1.2.0)

Method definition


You can also define server methods inside a wrapping object named jayson.Method. This allows additional options about the method to be specified. Using this wrapper - explicitly or implicitly (via server options) - makes it trivial to have your method accept a variable amount of arguments.

The method class is available as the Method or method property of  require('jayson'). It supports these options:

OptionDefaultTypeDescription
|-----------|--------------------------------|---------------------|------------------------------------------------------------------------
`handler`|The
`useContext`false`Boolean`When
`params`null`Array\|Object\|null`Force

Server example showcasing most features and options in examples/method_definitions/server.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. const methods = {

  4.   // this function will be wrapped in jayson.Method with options given to the server
  5.   sum: function(args, done) {
  6.     done(null, sum(args));
  7.   },

  8.   // this function always receives a context object as second arg
  9.   // it can be overriden on the server level
  10.   context: jayson.Method(function(args, context, done) {
  11.     done(null, context);
  12.   }, {useContext: true}),

  13.   // specifies some default values (alternate definition too)
  14.   sumDefault: jayson.Method(function(args, done) {
  15.     const total = sum(args);
  16.     done(null, total);
  17.   }, {
  18.     params: {a: 2, b: 5} // map of defaults
  19.   }),

  20.   // this method returns true when it gets an array (which it always does)
  21.   isArray: new jayson.Method({
  22.     handler: function(args, done) {
  23.       const result = Array.isArray(args);
  24.       done(null, result);
  25.     },
  26.     params: Array // could also be "Object"
  27.   })

  28. };

  29. const server = new jayson.Server(methods, {
  30.   // these options are given as options to jayson.Method when adding the method "sum".
  31.   // this is because it is not wrapped in jayson.Method like the others.
  32.   useContext: false,
  33.   params: Array
  34. });

  35. server.http().listen(3000);

  36. // sums all numbers in an array or object
  37. function sum(list) {
  38.   return Object.keys(list).reduce(function(sum, key) { return sum + list[key]; }, 0);
  39. }
  40. ```


  1. ``` js
  2. const jayson = require('jayson');

  3. const client = jayson.Client.http({
  4.   port: 3000
  5. });

  6. // invoke "sum" with array
  7. client.request('sum', [3, 5, 9, 11], function(err, response) {
  8.   if(err) throw err;
  9.   console.log(response.result); // 28
  10. });

  11. // invoke "sum" with an object
  12. client.request('sum', {a: 2, b: 3, c: 4}, function(err, response) {
  13.   if(err) throw err;
  14.   console.log(response.result); // 9
  15. });

  16. // invoke "sumDefault" with object missing some defined members
  17. client.request('sumDefault', {b: 10}, function(err, response) {
  18.   if(err) throw err;
  19.   console.log(response.result); // 12
  20. });

  21. // invoke "isArray" with an Object
  22. client.request('isArray', {a: 5, b: 2, c: 9}, function(err, response) {
  23.   if(err) throw err;
  24.   console.log(response.result); // true
  25. });

  26. // invoke "context"
  27. client.request('context', {hello: 'world'}, function(err, response) {
  28.   if(err) throw err;
  29.   console.log(response.result); // {} - just an empty object
  30. });
  31. ```

Server events


In addition to events that are specific to certain interfaces, all servers will emit the following events:

EventWhenArgumentsNotes
|------------|------------------------------------------|--------------------------------------|--------------------------------
`request`Interpretable1:|
`response`Returning1:|
`batch`Interpretable1.Emits

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][jsonrpc-spec] 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:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   i_cant_find_anything: function(args, callback) {
  5.     const error = {code: 404, message: 'Cannot find ' + args.id};
  6.     callback(error); // will return the error object as given
  7.   },
  8.   i_cant_return_a_valid_error: function(callback) {
  9.     callback({message: 'I forgot to enter a code'}); // will return a pre-defined "Internal Error"
  10.   }
  11. });
  12. ```

Predefined Errors

It is also possible to cause a method to return one of the predefined [JSON-RPC 2.0 error codes][jsonrpc-spec#error_object] using the server helper function Server.prototype.error inside of a server method. Example:

[jsonrpc-spec#error_object]: http://jsonrpc.org/spec.html#error_object

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   invalid_params: function(args, callback) {
  5.     const error = this.error(-32602); // returns an error with the default properties set
  6.     callback(error);
  7.   }
  8. });
  9. ```

You can even override the default messages:

  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   error_giver_of_doom: function(callback) {
  5.     callback(true) // invalid error format, which causes an Internal Error to be returned instead
  6.   }
  7. });

  8. // Override the default message
  9. server.errorMessages[Server.errors.INTERNAL_ERROR] = 'I has a sad. I cant do anything right';
  10. ```

Server CORS


Jayson does not include functionality for supporting CORS requests natively but it is easy to use a CORS-enabling middleware
like cors. An example of this can be found in examples/cors/server.js:

  1. ``` js
  2. const jayson = require('jayson');
  3. const cors = require('cors');
  4. const connect = require('connect');
  5. const jsonParser = require('body-parser').json;
  6. const app = connect();

  7. const server = new jayson.Server({
  8.   myNameIs: function(args, callback) {
  9.     callback(null, 'Your name is: ' + args.name);
  10.   }
  11. });

  12. app.use(cors({methods: ['POST']}));
  13. app.use(jsonParser());
  14. app.use(server.middleware());

  15. app.listen(3000);
  16. ```

Server Context


Since version 3.0.0

You can provide an optional context object to JSON-RPC method handlers. This can be used to give extra data to a handler  such as request headers, authentication tokens, and so on.

This feature is unlocked by having jayson.Method accepts a boolean option called useContext. It always defaults to false for backwards compatibility. When it is set to true the method handler that jayson.Method wraps will always receive a context object as the second argument. The object can be given as the third argument to jayson.Server.prototype.call.

Server example in examples/context/server.js:

  1. ``` js
  2. const jayson = require('jayson');
  3. const jsonParser = require('body-parser').json;
  4. const express = require('express');
  5. const app = express();

  6. const server = new jayson.Server({

  7.   getHeaders: function(args, context, callback) {
  8.     callback(null, context.headers);
  9.   },

  10.   // old method not receiving a context object (here for reference)
  11.   oldMethod: new jayson.Method(function(args, callback) {
  12.     callback(null, {});
  13.   }, {
  14.     // this setting overrides the server option set below for this particular method only
  15.     useContext: false
  16.   })

  17. }, {
  18.   // all methods will receive a context object as the second arg
  19.   useContext: true
  20. });

  21. app.use(jsonParser());
  22. app.use(function(req, res, next) {
  23.   // prepare a context object passed into the JSON-RPC method
  24.   const context = {headers: req.headers};
  25.   server.call(req.body, context, function(err, result) {
  26.     if(err) return next(err);
  27.     res.send(result || {});
  28.   });
  29. });

  30. app.listen(3001);
  31. ```

Client example in examples/context/client.js:

  1. ``` js
  2. const jayson = require('jayson');

  3. // create a client
  4. const client = jayson.Client.http({
  5.   port: 3001
  6. });

  7. // invoke "getHeaders"
  8. client.request('getHeaders', {}, function(err, response) {
  9.   if(err) throw err;
  10.   console.log(response.result);
  11. });
  12. ```

Notes

- jayson.Server also accepts useContext as an option, and passes the value on to the jayson.Method constructor. This option can be overriden on a per-method basis as shown above.
- Individual requests in a JSON-RPC batch will all receive the exact same context object in their handler - take care not to mutate it
- If a falsy context value is given to jayson.Server.prototype.call, an empty object will be created
- None of the current jayson server transports (http, https, tls, tcp, middleware) can make use of the context object. You will need to rig your own transport implementation, like the one above based on an express http server. See the FAQ for more info about this.

Revivers and Replacers


JSON lacks support for representing types other than the simple ones defined in the [JSON specification][jsonrpc-spec]. Fortunately the JSON methods in JavaScript (JSON.parse and JSON.stringify) provide options for custom serialization/deserialization routines. Jayson 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:

  1. ``` js
  2. 'use strict';

  3. const Counter = exports.Counter = function(value) {
  4.   this.count = value || 0;
  5. };

  6. Counter.prototype.increment = function() {
  7.   this.count += 1;
  8. };

  9. exports.replacer = function(key, value) {
  10.   if(value instanceof Counter) {
  11.     return {$class: 'counter', $props: {count: value.count}};
  12.   }
  13.   return value;
  14. };

  15. exports.reviver = function(key, value) {
  16.   if(value && value.$class === 'counter') {
  17.     const obj = new Counter();
  18.     for(const prop in value.$props) obj[prop] = value.$props[prop];
  19.     return obj;
  20.   }
  21.   return value;
  22. };
  23. ```


  1. ``` js
  2. const jayson = require('jayson');
  3. const shared = require('./shared');

  4. // Set the reviver/replacer options
  5. const options = {
  6.   reviver: shared.reviver,
  7.   replacer: shared.replacer
  8. };

  9. // create a server
  10. const server = new jayson.Server({
  11.   increment: function(args, callback) {
  12.     args.counter.increment();
  13.     callback(null, args.counter);
  14.   }
  15. }, options);

  16. server.http().listen(3000);
  17. ```

A client example in examples/reviving_and_replacing/client.js invoking "increment" on the server:

  1. ``` js
  2. const jayson = require('jayson');
  3. const shared = require('./shared');

  4. const client = jayson.Client.http({
  5.   port: 3000,
  6.   reviver: shared.reviver,
  7.   replacer: shared.replacer
  8. });

  9. // create the object
  10. const params = {
  11.   counter: new shared.Counter(2)
  12. };

  13. // invoke "increment"
  14. client.request('increment', params, function(err, response) {
  15.   if(err) throw err;
  16.   const result = response.result;
  17.   console.log(
  18.     result instanceof shared.Counter, // true
  19.     result.count, // 3
  20.     params.counter === result // false - result is a new object
  21.   );
  22. });
  23. ```

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.


  1. ``` js
  2. const jayson = require('jayson');

  3. const client = jayson.Client.http({
  4.   port: 3000
  5. });

  6. client.request('add', {b: 1, a: 2}, function(err, response) {
  7.   if(err) throw err;
  8.   console.log(response.result); // 3!
  9. });
  10. ```


  1. ``` js
  2. const jayson = require('jayson');

  3. const server = new jayson.Server({
  4.   add: function(params, callback) {
  5.     callback(null, params.a + params.b);
  6.   }
  7. });

  8. server.http().listen(3000);
  9. ```

Notes


If requesting methods on a Jayson 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
Parsing a function signature and filling in arguments is generally not recommended and should be avoided

Promises


[es6-promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Since version 2.0.0

A separate tree that does limited usage of the [ES6 Promise][es6-promise] object is available. The internal API remains callback based, with the addition that promises may be used for two things:

Returning a Promise when requesting a JSON-RPC method using a Client
Returning a Promise inside of a Server method

To use the separate tree, do a require('jayson/promise') instead of require('jayson').

Server example in examples/promise/server.js showing how to return aPromise in a server method:

  1. ``` js
  2. const jayson = require('jayson/promise');

  3. const server = new jayson.Server({

  4.   add: async function(args) {
  5.     const sum = Object.keys(args).reduce(function(sum, key) { return sum + args[key]; }, 0);
  6.     return sum;
  7.   },

  8.   // example on how to reject
  9.   rejection: async function(args) {
  10.     // server.error just returns {code: 501, message: 'not implemented'}
  11.     throw server.error(501, 'not implemented');
  12.   }

  13. });

  14. server.http().listen(3000);
  15. ```

Client example in examples/promise/client.js showing how to do a request:

  1. ``` js
  2. const jayson = require('jayson/promise');

  3. const client = jayson.Client.http({
  4.   port: 3000
  5. });

  6. const reqs = [
  7.   client.request('add', [1, 2, 3, 4, 5]),
  8.   client.request('rejection', [])
  9. ];

  10. Promise.all(reqs).then(function(responses) {
  11.   console.log(responses[0].result);
  12.   console.log(responses[1].error);
  13. });
  14. ```

Notes


JSON-RPC errors will not result in rejection of the Promise. It is however possible that a future version will include a client setting to have JSON-RPC errors result in rejection. Please note that network errors and the like will result in rejection.
A Promise is considered to have been returned from a server method if the returned object has a property then that is a function.

Promise Batches


Since version 2.0.5

Sometimes you may want to return raw requests from a promise client. This needs to be handled differently, because PromiseClient.prototype.request would normally always be expected to return a Promise which we in this case don't want.

To solve this, we need to set the fourth parameter to PromiseClient.prototype.request explicitly to false in order to not return a Promise.

Client example in examples/promise_batches/client.js showing how to properly execute a batch request:

  1. ``` js
  2. const jayson = require('jayson/promise');

  3. const client = jayson.Client.http({
  4.   port: 3000
  5. });

  6. const batch = [
  7.   client.request('add', [1, 2, 3, 4, 5], undefined, false),
  8.   client.request('add', [5, 6, 7, 8, 9], undefined, false),
  9. ];

  10. client.request(batch).then(function(responses) {
  11.   console.log(responses[0].result); // 15
  12.   console.log(responses[1].result); // 35
  13. });
  14. ```

Notes

The third parameter to PromiseClient.prototype.request above is explicitly set to undefined - this parameter would normally represent the desired ID of the call. Remember that null would mean a notification (which does not return a response) and other falsy values may actually be used as ids. Setting undefined ensures that the id is generated automatically.

Promise Browser Client


A browser client that has no dependencies on node.js core libraries is available too. It works similar to how the regular callback-style Browser Client works. Here is an example:

  1. ``` js
  2. 'use strict';

  3. const jaysonPromiseBrowserClient = require('jayson/promise/lib/client/browser');
  4. const fetch = require('node-fetch');

  5. const callServer = function(request) {
  6.   const options = {
  7.     method: 'POST',
  8.     body: request,
  9.     headers: {
  10.       'Content-Type': 'application/json',
  11.     }
  12.   };
  13.   return fetch('http://localhost:3000', options).then(res => res.text());
  14. };

  15. const client = new jaysonPromiseBrowserClient(callServer, {
  16.   // other options go here
  17. });

  18. client.request('multiply', [5, 5]).then(function(result) {
  19.   console.log(result);
  20. }, function(err) {
  21.   console.error(err);
  22. });
  23. ```

Please refer to the regular browser client section of the README for more information.

FAQ


How can I pass HTTP headers/session/etc into my JSON-RPC request handler?


Support for method context added in version 3.0.0

See Server context section.

What is the recommended way to use jayson?


Using the provided clients and servers for http, https, tls, tcp and the express middleware is fine and works well for most use cases. However, sometimes issues like these crop up (quotes below are not directly from issue posters):

- "The (non-jayson) HTTP/TCP server I'm interacting with expects every call to terminate with \r\n but the jayson client does not"

These are not issues with jayson, but stem from the fact that [JSON-RPC 2.0 specification][jsonrpc-spec] is transport agnostic and these kind of behaviours are not defined by that specification. The clients provided by jayson for http, https, tls, tcp are made to work and tested with their corresponding jayson server implementation. Any other compatibility with any other server or client is accidental when it comes to details of the transport layer. With that said, jayson is made to be 100 % compatible with the [JSON-RPC 2.0 specification][jsonrpc-spec] and compatibility with other non-jayson servers or clients when it comes to the application layer is pretty much guaranteed.

The library author tedeh therefore recommends that if you have particular needs when it comes to the transport layer you create an implementation satisfying these details yourself.Doing this is actually quite simple.

Example of a http server built with express in examples/faq_recommended_http_server/server.js:

  1. ``` js
  2. const jayson = require('jayson');
  3. const jsonParser = require('body-parser').json;
  4. const express = require('express');
  5. const app = express();

  6. // create a plain jayson server
  7. const server = new jayson.Server({
  8.   add: function(numbers, callback) {
  9.     const sum = Object.keys(numbers).reduce(function(sum, key) { return sum + numbers[key]; }, 0);
  10.     callback(null, sum);
  11.   }
  12. });

  13. app.use(jsonParser()); // <- here we can deal with maximum body sizes, etc
  14. app.use(function(req, res, next) {
  15.   const request = req.body;
  16.   // <- here we can check headers, modify the request, do logging, etc
  17.   server.call(request, function(err, response) {
  18.     if(err) {
  19.       // if err is an Error, err is NOT a json-rpc error
  20.       if(err instanceof Error) return next(err);
  21.       // <- deal with json-rpc errors here, typically caused by the user
  22.       res.status(400);
  23.       res.send(err);
  24.       return;
  25.     }
  26.     // <- here we can mutate the response, set response headers, etc
  27.     if(response) {
  28.       res.send(response);
  29.     } else {
  30.       // empty response (could be a notification)
  31.       res.status(204);
  32.       res.send('');
  33.     }
  34.   });
  35. });

  36. app.listen(3001);
  37. ```

Using some of the utilities provided and exported by jayson, creating a client offering the same kind of flexibility is also simple. Example of a compatible http client built with superagent in examples/faq_recommended_http_server/client.js:

  1. ``` js
  2. const jayson = require('jayson');
  3. const request = require('superagent');

  4. // generate a json-rpc version 2 compatible request (non-notification)
  5. const requestBody = jayson.Utils.request('add', [1,2,3,4], undefined, {
  6.   version: 2, // generate a version 2 request
  7. });

  8. request.post('http://localhost:3001')
  9.   // <- here we can setup timeouts, set headers, cookies, etc
  10.   .timeout({response: 5000, deadline: 60000})
  11.   .send(requestBody)
  12.   .end(function(err, response) {
  13.     if(err) {
  14.       // superagent considers 300-499 status codes to be errors
  15.       // @see http://visionmedia.github.io/superagent/#error-handling
  16.       if(!err.status) throw err;
  17.       const body = err.response.body;
  18.       // body may be a JSON-RPC error, or something completely different
  19.       // it can be handled here
  20.       if(body && body.error && jayson.Utils.Response.isValidError(body.error, 2)) {
  21.         // the error body was a valid JSON-RPC version 2
  22.         // we may wish to deal with it differently
  23.         console.err(body.error);
  24.         return;
  25.       }
  26.       throw err; // error was something completely different
  27.     }

  28.     const body = response.body;

  29.     // check if we got a valid JSON-RPC 2.0 response
  30.     if(!jayson.Utils.Response.isValidResponse(body, 2)) {
  31.       console.err(body);
  32.     }

  33.     if(body.error) {
  34.       // we have a json-rpc error...
  35.       console.err(body.error); // 10!
  36.     } else {
  37.       // do something useful with the result
  38.       console.log(body.result); // 10!
  39.     }
  40.   });
  41. ```

Contributing


Highlighting issues or submitting pull
requests on Github is most welcome.

Please make sure to follow the style of the project, and lint your code with npm run lint before submitting a patch.

Submitting issues or pull requests with the Typescript type definitions


You are required to provide an easily reproducible code sample of any errors with the Typescript type definitions so that they can be added to the typescript test file in typescript/test.ts. Better yet, issue a pull request adding a test there yourself that shows up when running thepackage.json script test-tsc.