UNPKG

55.9 kBMarkdownView Raw
1# Jayson
2
3Jayson 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.
4
5[jsonrpc-spec]: http://jsonrpc.org/spec.html
6[jsonrpc1-spec]: http://json-rpc.org/wiki/specification
7[node.js]: http://nodejs.org/
8[jayson-npm]: https://www.npmjs.com/package/jayson
9[jayson-travis]: https://travis-ci.org/tedeh/jayson
10[badge-travis]: https://img.shields.io/travis/tedeh/jayson/master.svg
11[badge-npm]: https://img.shields.io/npm/v/jayson.svg
12[badge-downloads-month]: https://img.shields.io/npm/dm/jayson.svg
13
14[![travis build status][badge-travis]][jayson-travis]
15[![Coverage Status](https://coveralls.io/repos/github/tedeh/jayson/badge.svg?branch=master)](https://coveralls.io/github/tedeh/jayson?branch=master)
16![GitHub issues](https://img.shields.io/github/issues/tedeh/jayson)
17![Dependents (via libraries.io)](https://img.shields.io/librariesio/dependents/npm/jayson)
18![node-current](https://img.shields.io/node/v/jayson)
19![Libraries.io dependency status for latest release](https://img.shields.io/librariesio/release/npm/jayson)
20[![npm version][badge-npm]][jayson-npm]
21[![npm][badge-downloads-month]][jayson-npm]
22![npm bundle size](https://img.shields.io/bundlephobia/min/jayson)
23![npm bundle size](https://img.shields.io/bundlephobia/minzip/jayson)
24[![Known Vulnerabilities](https://snyk.io/test/npm/jayson/badge.svg)](https://snyk.io/test/npm/jayson)
25
26## Table of contents
27
28- [Features](#features)
29- [Example](#example)
30- [Installation](#installation)
31- [Changelog](#changelog-only-notable-milestoneschanges)
32- [Requirements](#requirements)
33- [Class Documentation](#class-documentation)
34- [Running tests](#running-tests)
35- [Typescript](#typescript)
36- [Usage](#usage)
37 - [Client](#client)
38 - [Interface description](#client-interface-description)
39 - [Browser usage](#clientbrowser)
40 - [Notifications](#notifications)
41 - [Batches](#batches)
42 - [Callback syntactic sugar](#client-callback-syntactic-sugar)
43 - [Events](#client-events)
44 - [Server](#server)
45 - [Interface description](#server-interface-description)
46 - [Many interfaces at the same time](#many-interfaces-at-the-same-time)
47 - [Using the server as a relay](#using-the-server-as-a-relay)
48 - [Method routing](#method-routing)
49 - [Method definition](#method-definition)
50 - [Events](#server-events)
51 - [Errors](#server-errors)
52 - [CORS](#server-cors)
53 - [Context](#server-context)
54- [Revivers and replacers](#revivers-and-replacers)
55- [Named parameters](#named-parameters)
56- [Promises](#promises)
57 - [Batches](#promise-batches)
58 - [Browser client](#promise-browser-client)
59- [FAQ](#faq)
60- [Recommended usage](#what-is-the-recommended-way-to-use-jayson)
61- [Contributing](#contributing)
62
63## Features
64
65* [Servers that can listen to several interfaces at the same time](#many-interfaces-at-the-same-time)
66* Supports both HTTP and TCP client and server connections
67* [Server-side method routing](#method-routing)
68* [Relaying of requests to other servers](#using-the-server-as-a-relay)
69* [JSON reviving and replacing for transparent serialization of complex objects](#revivers-and-replacers)
70* [CLI client](#cli-client)
71* [Promises](#promises)
72* Fully tested to comply with the [official JSON-RPC 2.0 specification][jsonrpc-spec]
73* Also supports [JSON-RPC 1.0][jsonrpc1-spec]
74
75## Example
76
77A basic JSON-RPC 2.0 server via HTTP:
78
79Server example in [examples/simple_example/server.js](examples/simple_example/server.js):
80
81```javascript
82const jayson = require('jayson');
83
84// create a server
85const server = new jayson.Server({
86 add: function(args, callback) {
87 callback(null, args[0] + args[1]);
88 }
89});
90
91server.http().listen(3000);
92```
93
94Client example in [examples/simple_example/client.js](examples/simple_example/client.js) invoking `add` on the above server:
95
96```javascript
97const jayson = require('jayson');
98
99// create a client
100const client = jayson.Client.http({
101 port: 3000
102});
103
104// invoke "add"
105client.request('add', [1, 1], function(err, response) {
106 if(err) throw err;
107 console.log(response.result); // 2
108});
109```
110
111## Installation
112
113Install the latest version of _jayson_ from [npm](https://www.npmjs.com) by executing `npm install jayson` in your shell. Do a global install with `npm install --global jayson` if you want the `jayson` client CLI in your PATH.
114
115## Changelog (only notable milestones/changes)
116
117- *3.6.4*
118 - Websocket client and server support
119- *3.6.1*
120 - JSON-RPC 2.0 notifications no longer have id property unless overridden
121- *3.3.3*
122 - Promise support for browser client
123 - TypeScript declaration for promise browser client
124 - TypeScript declaration for browser client
125- *3.3.0*
126 - Remove URL parsing when passing a string option to the TLS and TCP client, string options are instead treated as an IPC path
127- *3.0.0*
128 - Can pass a [context object](#server-context) to handlers
129 - _Breaking_: `collect` option removed from `jayson.Server/Method`. JSON-RPC params to handlers are now **always** in the first argument.
130- *2.1.0*
131 - Experimental typescript support
132- *2.0.6*
133 - Clarified how to use [in the browser](#clientbrowser)
134- *2.0.0*
135 - Added [support for promises](#promises)
136 - _Breaking_: `collect: true` is now the default option for a new `jayson.Server` and `jayson.Method`
137- *1.2.0*
138 - Greatly improved [server method definition](#method-definition)
139- *1.1.1*
140 - More http server events
141 - Remove fork server and client
142 - Add server routing
143- *1.0.11*
144 Add support for a HTTPS client
145- *1.0.9*
146 Add support for TCP servers and clients
147
148### CLI client
149
150There 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.
151
152## Requirements
153
154Jayson 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](https://travis-ci.org/). You can look inside [.travis.yml](.travis.yml) if you want to see which versions are tested against.
155
156## Class documentation
157
158In addition to this document, a comprehensive class documentation made with [jsdoc][jsdoc-spec] is available at [jayson.tedeh.net](http://jayson.tedeh.net).
159
160[jsdoc-spec]: http://usejsdoc.org/
161
162## Running tests
163
164- Change directory to the repository root
165- Install the development packages by executing `npm install --dev`
166- Run the tests with `npm run test`
167- Run the typescript tests with `npm run test-tsc`
168- Run the coverage tests with `npm run coverage`
169
170## Typescript
171
172Since `v2.1.0` there is typescript support available with jayson.
173
174If you encounter any problems with the type definitions, see the [Contributing](#contributing) section.
175
176## Usage
177
178### Client
179
180The client is available as the `Client` or `client` property of `require('jayson')`.
181
182#### Client interface description
183
184| Name | Description |
185|--------------------|---------------------|
186| `Client` | Base class |
187| `Client.tcp` | TCP sub-class |
188| `Client.tls` | TLS sub-class |
189| `Client.http` | HTTP sub-class |
190| `Client.https` | HTTPS sub-class |
191| `Client.browser` | Standalone class |
192| `Client.websocket` | Websocket sub-class |
193
194Every client supports these options:
195
196| Option | Default | Type | Description |
197|----------------------|------------------------------------|------------|------------------------------------------------------------------------------------------|
198| `reviver` | `undefined` | `Function` | `JSON.parse` reviver |
199| `replacer` | `undefined` | `Function` | `JSON.stringify` replacer |
200| `generator` | [RFC4122][rfc_4122_spec] generator | `Function` | Generates a `String` for request ID. |
201| `version` | 2 | `Number` | JSON-RPC version to support (1 or 2) |
202| `notificationIdNull` | `false` | `Boolean` | *Since 3.6.1*. When true "id" property of a request will be set to null when version 2. |
203
204[rfc_4122_spec]: http://www.ietf.org/rfc/rfc4122.txt
205
206##### Client.http
207
208Uses the same options as [http.request][nodejs_docs_http_request] in addition to these options:
209
210| Option | Default | Type | Description |
211|------------ |------------ |---------- |---------------------------------------- |
212| `encoding` | `utf8` | `String` | Determines the encoding to use |
213| `headers` | `undefined` | `Object` | Extend the headers sent by the client |
214
215###### Client.http Events
216
217The HTTP client will emit the following events:
218
219| Event | When | Arguments | Notes |
220|----------------- |----------------------------------- |--------------------------------------------------------------------------- |------------------------------------------- |
221| `http request` | Created an HTTP request | 1. Instance of `http.ClientRequest` | |
222| `http response` | Received an HTTP response | 1. Instance of `http.IncomingMessage` 2. Instance of `http.ClientRequest` | |
223| `http error` | Underlying stream emits `error` | 1. Error | |
224| `http timeout` | Underlying stream emits `timeout` | | Automatically causes the request to abort |
225
226It is possible to pass a string URL as the first argument. The URL will be run through [url.parse][nodejs_docs_url_parse]. Example:
227
228```javascript
229const jayson = require('jayson');
230const client = jayson.Client.http('http://localhost:3000');
231// client.options is now the result of url.parse
232```
233
234[nodejs_docs_http_request]: http://nodejs.org/docs/latest/api/http.html#http_http_request_options_callback
235[nodejs_docs_url_parse]: http://nodejs.org/api/url.html#url_url_parse_urlstr_parsequerystring_slashesdenotehost
236
237##### Client.https
238
239Uses 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
240to pass a string URL as the first argument and have it interpreted by [url.parse][nodejs_docs_url_parse].
241
242Will emit the [same custom events](#clienthttp-events) as `Client.http`.
243
244[nodejs_docs_https_request]: http://nodejs.org/api/all.html#all_https_request_options_callback
245
246##### Client.tcp
247
248Uses the same options as [net.connect][nodejs_docs_net_connect].
249
250###### Client.tcp Events
251
252*Since version 3.5.1*
253
254The TCP client will emit the following events:
255
256| Event | When | Arguments | Notes |
257|------------|------------------------|-------------------------------|-------------------------------|
258| tcp socket | TCP socket is opened | 1. `net.Socket` | Can be used to setup timeouts |
259| tcp error | TCP socket emits error | 1. Error emit by `net.Socket` | |
260
261[nodejs_docs_net_connect]: https://nodejs.org/api/net.html#net_net_connect
262
263##### Client.tls
264
265Uses the same options as [tls.connect][nodejs_docs_tls_connect].
266
267###### Client.tls Events
268
269*Since version 3.5.1*
270
271The TLS client will emit the following events:
272
273| Event | When | Arguments | Notes |
274|------------|------------------------|-------------------------------|-------------------------------|
275| tcp socket | TCP socket is opened | 1. `net.Socket` | Can be used to setup timeouts |
276| tcp error | TCP socket emits error | 1. Error emit by `net.Socket` | |
277
278[nodejs_docs_tls_connect]: https://nodejs.org/api/tls.html#tls_tls_connect_options_callback
279
280##### Client.browser
281
282The 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.
283
284Because 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.
285
286To 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).
287
288The reason for dealing with strings is to support the `reviver` and `replacer` options like the other clients.
289
290This client example in [examples/browser_client/client.js](examples/browser_client/client.js) below uses [node-fetch](https://github.com/bitinn/node-fetch) in the transport function, but a dropin replacement for use in an *actual* browser could instead use [whatwg-fetch](https://github.com/github/fetch/issues/184).
291
292The 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.
293
294```javascript
295'use strict';
296
297const jaysonBrowserClient = require('jayson/lib/client/browser');
298const fetch = require('node-fetch');
299
300const callServer = function(request, callback) {
301 const options = {
302 method: 'POST',
303 body: request,
304 headers: {
305 'Content-Type': 'application/json',
306 }
307 };
308
309 fetch('http://localhost:3000', options)
310 .then(function(res) { return res.text(); })
311 .then(function(text) { callback(null, text); })
312 .catch(function(err) { callback(err); });
313};
314
315const client = new jaysonBrowserClient(callServer, {
316 // other options go here
317});
318
319client.request('multiply', [5, 5], function(err, error, result) {
320 if(err) throw err;
321 console.log(result); // 25
322});
323```
324
325##### Client.websocket
326
327*Since v3.6.4*
328
329Experimental 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](#serverwebsocket) where you can find an example.
330
331Has the following options:
332
333| Option | Default | Type | Description |
334|-----------|-------------|-------------------------------------|----------------------------------------------------------------------|
335| `url` | `undefined` | `String` | First argument to `require('isomorphic-ws')` if `options.ws` not set |
336| `ws` | `undefined` | `require('isomorphic-ws')` instance | WebSocket instance |
337| `timeout` | `undefined` | `Number` | Timeout in ms before callbacking with an error |
338
339If 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.
340
341- [isomorphic-ws docs](https://github.com/heineiuo/isomorphic-ws)
342
343#### Notifications
344
345Notification 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`.
346
347Client example in [examples/notifications/client.js](examples/notifications/client.js) doing a notification request:
348
349```javascript
350const jayson = require('jayson');
351
352const client = jayson.Client.http({
353 port: 3000
354});
355
356// the third parameter is set to "null" to indicate a notification
357client.request('ping', [], null, function(err) {
358 if(err) throw err;
359 console.log('ok'); // request was received successfully
360});
361```
362
363Server example in [examples/notifications/server.js](examples/notifications/server.js):
364
365```javascript
366const jayson = require('jayson');
367
368const server = new jayson.Server({
369 ping: function(args, callback) {
370 // do something, do nothing
371 callback();
372 }
373});
374
375server.http().listen(3000);
376```
377
378##### Notes
379
380* Any value that the server returns will be discarded when doing a notification request.
381* 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.
382* 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.
383* See the [Official JSON-RPC 2.0 Specification][jsonrpc-spec] for additional information on how Jayson handles notifications that are erroneous.
384* *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.
385
386
387#### Batches
388
389A 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`.
390
391Combined server/client example in [examples/batch_request/index.js](examples/batch_request/index.js):
392
393```javascript
394const jayson = require('jayson');
395
396const server = new jayson.Server({
397 add: function(args, callback) {
398 callback(null, args[0] + args[1]);
399 }
400});
401
402const client = new jayson.Client(server);
403
404const batch = [
405 client.request('does_not_exist', [10, 5]),
406 client.request('add', [1, 1]),
407 client.request('add', [0, 0], null) // a notification
408];
409
410client.request(batch, function(err, errors, successes) {
411 if(err) throw err;
412 console.log('errors', errors); // array of requests that errored
413 console.log('successes', successes); // array of requests that succeeded
414});
415
416client.request(batch, function(err, responses) {
417 if(err) throw err;
418 console.log('responses', responses); // all responses together
419});
420```
421
422##### Notes
423
424* 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.
425* 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.
426
427#### Client callback syntactic sugar
428
429When the length (number of arguments) of a client callback function is either 2 or 3 it receives slightly different values when invoked.
430
431* 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.
432* 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).
433
434When 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.
435
436#### Client events
437
438A client will emit the following events (in addition to any special ones emitted by a specific interface):
439
440| Event | When | Arguments | Notes |
441|------------ |----------------------------- |----------------------------------------------- |------- |
442| `request` | About to dispatch a request | 1: Request object | |
443| `response` | Received a response | 1: Request object 2: Response object received | |
444
445### Server
446
447The server classes are available as the `Server` or `server` property of `require('jayson')`.
448
449The server also sports several interfaces that can be accessed as properties of an instance of `Server`.
450
451#### Server interface description
452
453| Name | Description |
454|--------------------- |-------------------------------------------------------------------------------------------- |
455| `Server` | Base interface for a server that supports receiving JSON-RPC requests |
456| `Server.tcp` | TCP server that inherits from [net.Server][nodejs_doc_net_server] |
457| `Server.tls` | TLS server that inherits from [tls.Server][nodejs_doc_tls_server] |
458| `Server.http` | HTTP server that inherits from [http.Server][nodejs_doc_http_server] |
459| `Server.https` | HTTPS server that inherits from [https.Server][nodejs_doc_https_server] |
460| `Server.websocket` | Websocket server that uses [isomorphic-ws Server][isomorphic-ws-docs] |
461| `Server.middleware` | Method that returns a [Connect][connect]/[Express][express] compatible middleware function |
462
463[isomorphic-ws-docs]: https://github.com/heineiuo/isomorphic-ws
464[nodejs_doc_net_server]: http://nodejs.org/docs/latest/api/net.html#net_class_net_server
465[nodejs_doc_http_server]: http://nodejs.org/docs/latest/api/http.html#http_class_http_server
466[nodejs_doc_https_server]: http://nodejs.org/docs/latest/api/https.html#https_class_https_server
467[nodejs_doc_tls_server]: https://nodejs.org/api/tls.html#tls_class_tls_server
468[connect]: http://www.senchalabs.org/connect/
469[express]: http://expressjs.com/
470
471Servers supports these options:
472
473| Option | Default | Type | Description |
474|--------------------- |----------------- |--------------------- |----------------------------------------------------------- |
475| `reviver` | `null` | `Function` | `JSON.parse` reviver |
476| `replacer` | `null ` | `Function` | `JSON.stringify` replacer |
477| `router` | `null ` | `Function` | Return the function for [method routing](#method-routing) |
478| `useContext` | `false` | `Boolean` | Passed to `methodConstructor` options |
479| `params` | `undefined` | `Array/Object/null` | Passed to `methodConstructor` options |
480| `methodConstructor` | `jayson.Method` | `Function` | Server functions are made an instance of this class |
481| `version` | 2 | `Number` | JSON-RPC version to support (1 or 2) |
482
483##### Server.tcp
484
485Uses the same options as the base class. Inherits from [net.Server][nodejs_doc_net_server].
486
487##### Server.tls
488
489Uses the same options as the base class. Inherits from [tls.Server][nodejs_doc_tls_server].
490
491##### Server.http
492
493Uses the same options as the base class. Inherits from [http.Server][nodejs_doc_http_server].
494
495###### Server.http Events
496
497| Event | When | Arguments | Notes |
498|----------------- |------------------------------- |------------------------------------------------------------------------------ |------- |
499| `http request` | Incoming HTTP request | 1. Instance of `http.IncomingMessage` | |
500| `http response` | About to send a HTTP response | 1. Instance of `http.ServerResponse` 2. Instance of `http. IncomingMessage ` | |
501
502##### Server.https
503
504Uses 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].
505
506Will emit the [same custom events](#serverhttp-events) as `Server.http`.
507
508##### Server.middleware
509
510Uses 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.
511
512The middleware supports the following options:
513
514| Option | Default | Type | Description |
515|-------- |--------- |----------- |------------------------------------------------------------------------------------------- |
516| `end` | `true` | `Boolean` | If set to `false` causes the middleware to `next()` instead of `res.end()` when finished. |
517
518Middleware example in [examples/middleware/server.js](examples/middleware/server.js):
519
520```javascript
521const jayson = require('jayson');
522const jsonParser = require('body-parser').json;
523const connect = require('connect');
524const app = connect();
525
526const server = new jayson.Server({
527 add: function(args, callback) {
528 callback(null, args[0] + args[1]);
529 }
530});
531
532// parse request body before the jayson middleware
533app.use(jsonParser());
534app.use(server.middleware());
535
536app.listen(3000);
537```
538
539##### Server.websocket
540
541Websocket 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.
542
543The websocket server supports the following options in addition to the base class:
544
545| Option | Default | Type | Description |
546|--------|-------------|-----------------------------------|---------------------------------|
547| `wss` | `undefined` | `require('isomorphic-ws').Server` | If not provided will be created |
548
549
550Websocket server example in [examples/websocket/server.js](examples/websocket/server.js):
551
552```javascript
553const jayson = require('jayson');
554
555const server = new jayson.Server({
556 add: function (args, done) {
557 const sum = args.reduce((sum, val) => sum + val, 0);
558 done(null, sum);
559 },
560});
561
562const wss = server.websocket({
563 port: 12345,
564});
565```
566
567Websocket client example in [examples/websocket/client.js](examples/websocket/client.js):
568
569```javascript
570const jayson = require('jayson');
571
572const client = jayson.Client.websocket({
573 url: 'ws://localhost:12345',
574});
575
576client.ws.on('open', function () {
577 client.request('add', [1,2,3,4], function (err, result) {
578 console.log(err, result);
579 client.ws.close();
580 });
581});
582```
583
584#### Many interfaces at the same time
585
586A Jayson server can use many interfaces at the same time.
587
588Server example in [examples/many_interfaces/server.js](examples/many_interfaces/server.js) that listens to both `http` and a `https` requests:
589
590```javascript
591const jayson = require('jayson');
592
593const server = new jayson.Server();
594
595// "http" will be an instance of require('http').Server
596const http = server.http();
597
598// "https" will be an instance of require('https').Server
599const https = server.https({
600 //cert: require('fs').readFileSync('cert.pem'),
601 //key require('fs').readFileSync('key.pem')
602});
603
604http.listen(80, function() {
605 console.log('Listening on *:80');
606});
607
608https.listen(443, function() {
609 console.log('Listening on *:443');
610});
611```
612
613#### Using the server as a relay
614
615Passing 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.
616
617Frontend server example in [examples/relay/server_public.js](examples/relay/server_public.js) listening on `*:3000`:
618
619```javascript
620const jayson = require('jayson');
621
622// create a server where "add" will relay a localhost-only server
623const server = new jayson.Server({
624 add: jayson.Client.http({
625 port: 3001
626 })
627});
628
629// let the frontend server listen to *:3000
630server.http().listen(3000);
631```
632
633Backend server example in [examples/relay/server_private.js](examples/relay/server_private.js) listening on `*:3001`:
634
635```javascript
636const jayson = require('jayson');
637
638const server = new jayson.Server({
639 add: function(args, callback) {
640 callback(null, args[0] + args[1]);
641 }
642});
643
644// let the backend listen to *:3001
645server.http().listen(3001);
646```
647
648Every 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](examples/relay/client.js).
649
650#### Method routing
651
652Passing a property named `router` in the server options will enable you to write your own logic for routing requests to specific functions.
653
654Server example with custom routing logic in [examples/method_routing/server.js](examples/method_routing/server.js):
655
656```javascript
657const jayson = require('jayson');
658
659const methods = {
660 add: function(args, callback) {
661 callback(null, args[0] + args[1]);
662 }
663};
664
665const server = new jayson.Server(methods, {
666 router: function(method, params) {
667 // regular by-name routing first
668 if(typeof(this._methods[method]) === 'function') return this._methods[method];
669 if(method === 'add_2') {
670 const fn = server.getMethod('add').getHandler();
671 return new jayson.Method(function(args, done) {
672 args.unshift(2);
673 fn(args, done);
674 });
675 }
676 }
677});
678
679server.http().listen(3000);
680```
681
682Client example in [examples/method_routing/client.js](examples/method_routing/client.js) invoking `add_2` on the above server:
683
684```javascript
685const jayson = require('jayson');
686
687// create a client
688const client = jayson.Client.http({
689 port: 3000
690});
691
692// invoke "add_2"
693client.request('add_2', [3], function(err, response) {
694 if(err) throw err;
695 console.log(response.result); // 5!
696});
697```
698
699Server example of nested routes where each property is separated by a dot (you do not need to use the router option for this):
700
701```javascript
702const _ = require('lodash');
703const jayson = require('jayson');
704
705const methods = {
706 foo: {
707 bar: function(callback) {
708 callback(null, 'ping pong');
709 }
710 },
711 math: {
712 add: function(args, callback) {
713 callback(null, args[0] + args[1]);
714 }
715 }
716};
717
718// this reduction produces an object like this: {'foo.bar': [Function], 'math.add': [Function]}
719const map = _.reduce(methods, collapse('', '.'), {});
720const server = new jayson.Server(map);
721
722function collapse(stem, sep) {
723 return function(map, value, key) {
724 const prop = stem ? stem + sep + key : key;
725 if(_.isFunction(value)) map[prop] = value;
726 else if(_.isObject(value)) map = _.reduce(value, collapse(prop, sep), map);
727 return map;
728 }
729}
730```
731
732##### Notes
733
734* If `router` does not return anything, the server will respond with a `Method Not Found` error.
735* 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.
736* The `router` method is expected to return instances of `jayson.Method` (>=1.2.0)
737
738#### Method definition
739
740You 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.
741
742The method class is available as the `Method` or `method` property of `require('jayson')`. It supports these options:
743
744| Option | Default | Type | Description |
745|----------- |-------------------------------- |--------------------- |------------------------------------------------------------------------ |
746| `handler` | | `Function` | The actual function that will handle a JSON-RPC request to this method |
747| `useContext` | false | `Boolean` | When true, the handler will receive a context object as the second argument
748| `params` | null | `Array\|Object\|null` | Force JSON-RPC parameters to be of a certain type |
749
750Server example showcasing most features and options in [examples/method_definitions/server.js](examples/method_definitions/server.js):
751
752```javascript
753const jayson = require('jayson');
754const _ = require('lodash');
755
756const methods = {
757
758 // this function will be wrapped in jayson.Method with options given to the server
759 sum: function(args, done) {
760 done(null, sum(args));
761 },
762
763 // this function always receives a context object as second arg
764 // it can be overriden on the server level
765 context: jayson.Method(function(args, context, done) {
766 done(null, context);
767 }, {useContext: true}),
768
769 // specifies some default values (alternate definition too)
770 sumDefault: jayson.Method(function(args, done) {
771 const total = sum(args);
772 done(null, total);
773 }, {
774 params: {a: 2, b: 5} // map of defaults
775 }),
776
777 // this method returns true when it gets an array (which it always does)
778 isArray: new jayson.Method({
779 handler: function(args, done) {
780 const result = _.isArray(args);
781 done(null, result);
782 },
783 params: Array // could also be "Object"
784 })
785
786};
787
788const server = new jayson.Server(methods, {
789 // these options are given as options to jayson.Method when adding the method "sum".
790 // this is because it is not wrapped in jayson.Method like the others.
791 useContext: false,
792 params: Array
793});
794
795server.http().listen(3000);
796
797// sums all numbers in an array or object
798function sum(list) {
799 return _.reduce(list, function(sum, val) {
800 return sum + val;
801 }, 0);
802}
803```
804
805Client example in [examples/method_definitions/client.js](examples/method_definitions/client.js):
806
807```javascript
808const jayson = require('jayson');
809
810const client = jayson.Client.http({
811 port: 3000
812});
813
814// invoke "sum" with array
815client.request('sum', [3, 5, 9, 11], function(err, response) {
816 if(err) throw err;
817 console.log(response.result); // 28
818});
819
820// invoke "sum" with an object
821client.request('sum', {a: 2, b: 3, c: 4}, function(err, response) {
822 if(err) throw err;
823 console.log(response.result); // 9
824});
825
826// invoke "sumDefault" with object missing some defined members
827client.request('sumDefault', {b: 10}, function(err, response) {
828 if(err) throw err;
829 console.log(response.result); // 12
830});
831
832// invoke "isArray" with an Object
833client.request('isArray', {a: 5, b: 2, c: 9}, function(err, response) {
834 if(err) throw err;
835 console.log(response.result); // true
836});
837
838// invoke "context"
839client.request('context', {hello: 'world'}, function(err, response) {
840 if(err) throw err;
841 console.log(response.result); // {} - just an empty object
842});
843```
844
845#### Server events
846
847In addition to events that are specific to certain interfaces, all servers will emit the following events:
848
849| Event | When | Arguments | Notes |
850|------------ |------------------------------------------ |-------------------------------------- |-------------------------------- |
851| `request` | Interpretable non-batch request received | 1: Request object | |
852| `response` | Returning a response | 1: Request object 2: Response object | |
853| `batch` | Interpretable batch request received | 1. Array of requests | Emits `request` for every part |
854
855#### Server Errors
856
857If 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:
858
859```javascript
860const jayson = require('jayson');
861
862const server = new jayson.Server({
863 i_cant_find_anything: function(args, callback) {
864 const error = {code: 404, message: 'Cannot find ' + args.id};
865 callback(error); // will return the error object as given
866 },
867 i_cant_return_a_valid_error: function(callback) {
868 callback({message: 'I forgot to enter a code'}); // will return a pre-defined "Internal Error"
869 }
870});
871```
872
873##### Predefined Errors
874
875It 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:
876
877[jsonrpc-spec#error_object]: http://jsonrpc.org/spec.html#error_object
878
879```javascript
880const jayson = require('jayson');
881
882const server = new jayson.Server({
883 invalid_params: function(args, callback) {
884 const error = this.error(-32602); // returns an error with the default properties set
885 callback(error);
886 }
887});
888```
889
890You can even override the default messages:
891
892```javascript
893const jayson = require('jayson');
894
895const server = new jayson.Server({
896 error_giver_of_doom: function(callback) {
897 callback(true) // invalid error format, which causes an Internal Error to be returned instead
898 }
899});
900
901// Override the default message
902server.errorMessages[Server.errors.INTERNAL_ERROR] = 'I has a sad. I cant do anything right';
903```
904
905#### Server CORS
906
907Jayson does not include functionality for supporting CORS requests natively but it is easy to use a CORS-enabling middleware
908like [cors](https://github.com/expressjs/cors). An example of this can be found in [examples/cors/server.js](examples/cors/server.js):
909
910```javascript
911const jayson = require('jayson');
912const cors = require('cors');
913const connect = require('connect');
914const jsonParser = require('body-parser').json;
915const app = connect();
916
917const server = new jayson.Server({
918 myNameIs: function(args, callback) {
919 callback(null, 'Your name is: ' + args.name);
920 }
921});
922
923app.use(cors({methods: ['POST']}));
924app.use(jsonParser());
925app.use(server.middleware());
926
927app.listen(3000);
928```
929
930#### Server Context
931
932*Since version 3.0.0*
933
934You 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.
935
936This 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`.
937
938Server example in [examples/context/server.js](examples/context/server.js):
939
940```javascript
941const _ = require('lodash');
942const jayson = require('jayson');
943const jsonParser = require('body-parser').json;
944const express = require('express');
945const app = express();
946
947const server = new jayson.Server({
948
949 getHeaders: function(args, context, callback) {
950 callback(null, context.headers);
951 },
952
953 // old method not receiving a context object (here for reference)
954 oldMethod: new jayson.Method(function(args, callback) {
955 callback(null, {});
956 }, {
957 // this setting overrides the server option set below for this particular method only
958 useContext: false
959 })
960
961}, {
962 // all methods will receive a context object as the second arg
963 useContext: true
964});
965
966app.use(jsonParser());
967app.use(function(req, res, next) {
968 // prepare a context object passed into the JSON-RPC method
969 const context = {headers: req.headers};
970 server.call(req.body, context, function(err, result) {
971 if(err) return next(err);
972 res.send(result || {});
973 });
974});
975
976app.listen(3001);
977```
978
979Client example in [examples/context/client.js](examples/context/client.js):
980
981```javascript
982const jayson = require('jayson');
983
984// create a client
985const client = jayson.Client.http({
986 port: 3001
987});
988
989// invoke "getHeaders"
990client.request('getHeaders', {}, function(err, response) {
991 if(err) throw err;
992 console.log(response.result);
993});
994```
995
996##### Notes
997
998- `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.
999- Individual requests in a JSON-RPC batch will all receive the exact same context object in their handler - take care not to mutate it
1000- If a falsy context value is given to `jayson.Server.prototype.call`, an empty object will be created
1001- 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](#faq) for more info about this.
1002
1003### Revivers and Replacers
1004
1005JSON 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.
1006
1007Simple example transferring the state of an object between a client and a server:
1008
1009Shared code between the server and the client in [examples/reviving_and_replacing/shared.js](examples/reviving_and_replacing/shared.js):
1010
1011```javascript
1012'use strict';
1013
1014const Counter = exports.Counter = function(value) {
1015 this.count = value || 0;
1016};
1017
1018Counter.prototype.increment = function() {
1019 this.count += 1;
1020};
1021
1022exports.replacer = function(key, value) {
1023 if(value instanceof Counter) {
1024 return {$class: 'counter', $props: {count: value.count}};
1025 }
1026 return value;
1027};
1028
1029exports.reviver = function(key, value) {
1030 if(value && value.$class === 'counter') {
1031 const obj = new Counter();
1032 for(const prop in value.$props) obj[prop] = value.$props[prop];
1033 return obj;
1034 }
1035 return value;
1036};
1037```
1038
1039Server example in [examples/reviving_and_replacing/server.js](examples/reviving_and_replacing/server.js):
1040
1041```javascript
1042const jayson = require('jayson');
1043const shared = require('./shared');
1044
1045// Set the reviver/replacer options
1046const options = {
1047 reviver: shared.reviver,
1048 replacer: shared.replacer
1049};
1050
1051// create a server
1052const server = new jayson.Server({
1053 increment: function(args, callback) {
1054 args.counter.increment();
1055 callback(null, args.counter);
1056 }
1057}, options);
1058
1059server.http().listen(3000);
1060```
1061
1062A client example in [examples/reviving_and_replacing/client.js](examples/reviving_and_replacing/client.js) invoking "increment" on the server:
1063
1064```javascript
1065const jayson = require('jayson');
1066const shared = require('./shared');
1067
1068const client = jayson.Client.http({
1069 port: 3000,
1070 reviver: shared.reviver,
1071 replacer: shared.replacer
1072});
1073
1074// create the object
1075const params = {
1076 counter: new shared.Counter(2)
1077};
1078
1079// invoke "increment"
1080client.request('increment', params, function(err, response) {
1081 if(err) throw err;
1082 const result = response.result;
1083 console.log(
1084 result instanceof shared.Counter, // true
1085 result.count, // 3
1086 params.counter === result // false - result is a new object
1087 );
1088});
1089```
1090
1091#### Notes
1092
1093* 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.
1094
1095### Named parameters
1096
1097It is possible to specify named parameters when doing a client request by passing an Object instead of an Array.
1098
1099Client example in [examples/named_parameters/client.js](examples/named_parameters/client.js):
1100
1101```javascript
1102const jayson = require('jayson');
1103
1104const client = jayson.Client.http({
1105 port: 3000
1106});
1107
1108client.request('add', {b: 1, a: 2}, function(err, response) {
1109 if(err) throw err;
1110 console.log(response.result); // 3!
1111});
1112```
1113
1114Server example in [examples/named_parameters/server.js](examples/named_parameters/server.js):
1115
1116```javascript
1117const jayson = require('jayson');
1118
1119const server = new jayson.Server({
1120 add: function(params, callback) {
1121 callback(null, params.a + params.b);
1122 }
1123});
1124
1125server.http().listen(3000);
1126```
1127
1128#### Notes
1129
1130* If requesting methods on a Jayson server, arguments left out will be `undefined`
1131* Too many arguments or arguments with invalid names will be ignored
1132* It is assumed that the last argument to a server method is the callback and it will not be filled with something else
1133* Parsing a function signature and filling in arguments is generally *not recommended* and should be avoided
1134
1135## Promises
1136
1137[es6-promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
1138
1139*Since version 2.0.0*
1140
1141A 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:
1142
1143* Returning a Promise when requesting a JSON-RPC method using a Client
1144* Returning a Promise inside of a Server method
1145
1146To use the separate tree, do a `require('jayson/promise')` instead of `require('jayson')`.
1147
1148Server example in [examples/promise/server.js](examples/promise/server.js) showing how to return a `Promise` in a server method:
1149
1150```javascript
1151const jayson = require('jayson/promise');
1152const _ = require('lodash');
1153
1154const server = new jayson.Server({
1155
1156 add: async function(args) {
1157 const sum = _.reduce(args, function(sum, value) { return sum + value; }, 0);
1158 return sum;
1159 },
1160
1161 // example on how to reject
1162 rejection: async function(args) {
1163 // server.error just returns {code: 501, message: 'not implemented'}
1164 throw server.error(501, 'not implemented');
1165 }
1166
1167});
1168
1169server.http().listen(3000);
1170```
1171
1172Client example in [examples/promise/client.js](examples/promise/client.js) showing how to do a request:
1173
1174```javascript
1175const jayson = require('jayson/promise');
1176
1177const client = jayson.Client.http({
1178 port: 3000
1179});
1180
1181const reqs = [
1182 client.request('add', [1, 2, 3, 4, 5]),
1183 client.request('rejection', [])
1184];
1185
1186Promise.all(reqs).then(function(responses) {
1187 console.log(responses[0].result);
1188 console.log(responses[1].error);
1189});
1190```
1191
1192#### Notes
1193
1194* 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.
1195* A `Promise` is considered to have been returned from a server method if the returned object has a property `then` that is a function.
1196
1197#### Promise Batches
1198
1199*Since version 2.0.5*
1200
1201Sometimes 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.
1202
1203To solve this, we need to set the fourth parameter to `PromiseClient.prototype.request` explicitly to `false` in order to *not* return a Promise.
1204
1205Client example in [examples/promise_batches/client.js](examples/promise_batches/client.js) showing how to properly execute a batch request:
1206
1207```javascript
1208const jayson = require('jayson/promise');
1209
1210const client = jayson.Client.http({
1211 port: 3000
1212});
1213
1214const batch = [
1215 client.request('add', [1, 2, 3, 4, 5], undefined, false),
1216 client.request('add', [5, 6, 7, 8, 9], undefined, false),
1217];
1218
1219client.request(batch).then(function(responses) {
1220 console.log(responses[0].result); // 15
1221 console.log(responses[1].result); // 35
1222});
1223```
1224
1225##### Notes
1226
1227* 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.
1228
1229#### Promise Browser Client
1230
1231A 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](#clientbrowser) works. Here is an example:
1232
1233```javascript
1234'use strict';
1235
1236const jaysonPromiseBrowserClient = require('jayson/promise/lib/client/browser');
1237const fetch = require('node-fetch');
1238
1239const callServer = function(request) {
1240 const options = {
1241 method: 'POST',
1242 body: request,
1243 headers: {
1244 'Content-Type': 'application/json',
1245 }
1246 };
1247 return fetch('http://localhost:3000', options).then(res => res.text());
1248};
1249
1250const client = new jaysonPromiseBrowserClient(callServer, {
1251 // other options go here
1252});
1253
1254client.request('multiply', [5, 5]).then(function(result) {
1255 console.log(result);
1256}, function(err) {
1257 console.error(err);
1258});
1259```
1260
1261Please refer to the [regular browser client](#clientbrowser) section of the README for more information.
1262
1263## FAQ
1264
1265### How can I pass HTTP headers/session/etc into my JSON-RPC request handler?
1266
1267*Support for method context added in version 3.0.0*
1268
1269See [Server context](#server-context) section.
1270
1271### What is the recommended way to use jayson?
1272
1273Using 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):
1274
1275- "The (non-jayson) HTTP/TCP server I'm interacting with expects every call to terminate with `\r\n` but the jayson client does not"
1276- ["How can my jayson TLS server support requests encoded such and such?"](https://github.com/tedeh/jayson/issues/86)
1277- ["How can I make the jayson HTTP middleware accept GET requests?"](https://github.com/tedeh/jayson/issues/70)
1278- ["My jayson client interacting with a (non-jayson) TLS server closes the connection after every sent request. I think this is wasteful!"](https://github.com/tedeh/jayson/issues/92)
1279
1280These 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.
1281
1282The library author [tedeh](https://github.com/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.**
1283
1284Example of a http server built with express in [examples/faq_recommended_http_server/server.js](examples/faq_recommended_http_server/server.js):
1285
1286```javascript
1287const _ = require('lodash');
1288const jayson = require('jayson');
1289const jsonParser = require('body-parser').json;
1290const express = require('express');
1291const app = express();
1292
1293// create a plain jayson server
1294const server = new jayson.Server({
1295 add: function(numbers, callback) {
1296 callback(null, _.reduce(numbers, (sum, val) => sum + val, 0));
1297 }
1298});
1299
1300app.use(jsonParser()); // <- here we can deal with maximum body sizes, etc
1301app.use(function(req, res, next) {
1302 const request = req.body;
1303 // <- here we can check headers, modify the request, do logging, etc
1304 server.call(request, function(err, response) {
1305 if(err) {
1306 // if err is an Error, err is NOT a json-rpc error
1307 if(err instanceof Error) return next(err);
1308 // <- deal with json-rpc errors here, typically caused by the user
1309 res.status(400);
1310 res.send(err);
1311 return;
1312 }
1313 // <- here we can mutate the response, set response headers, etc
1314 if(response) {
1315 res.send(response);
1316 } else {
1317 // empty response (could be a notification)
1318 res.status(204);
1319 res.send('');
1320 }
1321 });
1322});
1323
1324app.listen(3001);
1325```
1326
1327Using 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](examples/faq_recommended_http_server/client.js):
1328
1329```javascript
1330const jayson = require('jayson');
1331const request = require('superagent');
1332
1333// generate a json-rpc version 2 compatible request (non-notification)
1334const requestBody = jayson.Utils.request('add', [1,2,3,4], undefined, {
1335 version: 2, // generate a version 2 request
1336});
1337
1338request.post('http://localhost:3001')
1339 // <- here we can setup timeouts, set headers, cookies, etc
1340 .timeout({response: 5000, deadline: 60000})
1341 .send(requestBody)
1342 .end(function(err, response) {
1343 if(err) {
1344 // superagent considers 300-499 status codes to be errors
1345 // @see http://visionmedia.github.io/superagent/#error-handling
1346 if(!err.status) throw err;
1347 const body = err.response.body;
1348 // body may be a JSON-RPC error, or something completely different
1349 // it can be handled here
1350 if(body && body.error && jayson.Utils.Response.isValidError(body.error, 2)) {
1351 // the error body was a valid JSON-RPC version 2
1352 // we may wish to deal with it differently
1353 console.err(body.error);
1354 return;
1355 }
1356 throw err; // error was something completely different
1357 }
1358
1359 const body = response.body;
1360
1361 // check if we got a valid JSON-RPC 2.0 response
1362 if(!jayson.Utils.Response.isValidResponse(body, 2)) {
1363 console.err(body);
1364 }
1365
1366 if(body.error) {
1367 // we have a json-rpc error...
1368 console.err(body.error); // 10!
1369 } else {
1370 // do something useful with the result
1371 console.log(body.result); // 10!
1372 }
1373 });
1374```
1375
1376## Contributing
1377
1378Highlighting [issues](https://github.com/tedeh/jayson/issues) or submitting pull
1379requests on [Github](https://github.com/tedeh/jayson) is most welcome.
1380
1381Please make sure to follow the style of the project, and lint your code with `npm run lint` before submitting a patch.
1382
1383### Submitting issues or pull requests with the Typescript type definitions
1384
1385You 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](typescript/test.ts). Better yet, issue a pull request adding a test there yourself that shows up when running the `package.json` script `test-tsc`.