https://github.com/mhingston/jayson
JSON-RPC inspired server and client with HTTP and WebSocket transports and JSON Schema IDL
https://github.com/mhingston/jayson
http json-rpc json-schema nodejs websocket
Last synced: 2 months ago
JSON representation
JSON-RPC inspired server and client with HTTP and WebSocket transports and JSON Schema IDL
- Host: GitHub
- URL: https://github.com/mhingston/jayson
- Owner: mhingston
- License: mit
- Created: 2018-02-08T21:08:49.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2018-12-31T10:59:51.000Z (over 7 years ago)
- Last Synced: 2026-01-08T13:49:10.544Z (3 months ago)
- Topics: http, json-rpc, json-schema, nodejs, websocket
- Language: JavaScript
- Size: 247 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Jayson
[JSON-RPC](http://www.jsonrpc.org/specification) inspired server and client with HTTP and WebSocket transports and JSON Schema IDL.
## Installation
```
npm install mhingston/jayson
```
## Introduction
### Format
Jayson uses a custom version of JSON-RPC for messages. The specification is based on [JSON-RPC 2.0](http://www.jsonrpc.org/specification) with the following changes:
* The `request` and `response` objects **must** contain the property `jayson` with the value set to the version of the API being used. This property **replaces** the `jsonrpc` property.
e.g.
```json
{
"jayson": "1.0"
}
```
* A `response` object without an `error` property and without a `result` property indicates that the remote method returned `undefined`.
* The following additional properties can be defined within a `request` object:
* `auth` {String} A [JWT](https://github.com/auth0/node-jsonwebtoken) providing the authentication context.
The reason for baking auth into JSON-RPC was that I felt it should be part of the protocol and not reliant on the different authentication mechanisms available within the transport layers (i.e. HTTP, WebSocket).
### Method Properties
Remote methods may have attached to them the following properties:
* `schema` {Object} A JSON schema which is used as the IDL. The schema must verify against the [method schema](https://github.com/mhingston/jayson/blob/master/schemas/1.0/method.json). If a method doesn't provide a full schema then any calls to the method won't have their `auth` or `params` properties validated. Similarly if the `returns` schema isn't defined then the return value won't be validated.
#### Example
```javascript
function hello(name)
{
return 'Hello ' + name;
}
hello.schema =
{
params:
{
type: 'string'
},
returns:
{
type: 'string'
}
}
```
If you need to reference schema definitions you should pass in your `definitions` schema to the server config. [Example](https://github.com/mhingston/jayson/blob/master/examples/schema-definitions).
* `timeout` {Number} How long to wait (in milliseconds) before timing out the request. If not provided then the `timeout` value will be used from the server config.
## Server Usage
**Note: The server is intended to be run behind a reverse proxy, unless you're using [electron](https://github.com/electron/electron) ofcourse.**
```javascript
// Import the module
const Jayson = require('jayson');
// Declare an object literal with all the methods you want to expose...
const methods =
{
foo: () => 'hello',
bar: (a, b) => a + b,
baz: ({name}) => 'hello ' + name
};
// ...or pass in an instance of a class with the methods you want to expose.
class Method
{
foo()
{
return 'hello';
}
bar(a, b)
{
return a + b
}
baz({name})
{
return 'hello ' + name;
}
}
const methods = new Method();
```
**Note: The Jayson server passes a `context` object argument to every method that's called. If the `params` property of a `request` is an object then the property `context` will be added to that object. If the `params` property is any other type then the first argument passed to the method will be the `context` object.**
`context` {Object} Method context.
* `headers` {Object} Request headers.
* `auth` {String} A [JWT](https://github.com/auth0/node-jsonwebtoken) providing the authentication context.
Define your config (default values shown below):
```javascript
const config =
{
title: 'Jayson Server API',
methods,
logger: false,
jsonLimit: '1mb',
timeout: 60000,
http:
{
port: 3000,
cors: {},
helmet: {noCache: true},
compress: {}
},
ws:
{
port: 3001,
heartbeat: 30000
},
jwt:
{
secret: 'sauce'
}
}
```
* `title` {String} Name of the API instance. Default = `'Jayson Server API'`.
* `description` {String} Description of the API instance. Default = `undefined`.
* `$id` {String} [JSON Schema ID](https://spacetelescope.github.io/understanding-json-schema/structuring.html#the-id-property). Default = `undefined`.
* `methods` {Object} **(Required)** Object containing the methods exposed to the RPC server. Default = `undefined`.
* `definitions` {Object} Schema definitions. Use this when you need to reference shared definitions from method schemas. See the [schema-definitions](https://github.com/mhingston/jayson/blob/master/examples/schema-definitions) example. Default = `undefined`.
* `logger` {Boolean|Function} Set to true to have debug log written to the console or pass in a function to receive the log messages. Default = `undefined`.
* `jsonLimit` {String} Maximum size of the message payload. Default = `'1mb'`.
* `timeout` {Number|Null} Default timeout for all RPC calls (in milliseconds). Set to `null` to disable default timeout. Default = `60000`.
* `http` {Object}. Default = `undefined`.
* `port` {Number} Port to listen to HTTP connections on. Default = `3000`.
* `cors` {Object} CORS options, see [koa2-cors](https://github.com/zadzbw/koa2-cors#options). Default = `{}`.
* `helmet` {Object} Helmet options, see [koa-helmet](https://github.com/venables/koa-helmet). Default = `{noCache: true}`.
* `compress` {Object} Compress options, see [compress](https://github.com/koajs/compress#options). Default = `{}`.
* `ws` {Object}. Default = `undefined`.
* `port` {Number} Port to listen to WebSocket connections on. Default = `3001`.
* `heartbeat` {Number} How often to send pings to clients (in milliseconds). Default = `30000`.
* `electron` {Boolean} Whether the server is running in electron. Default = `undefined`.
* `jwt` {Object}. Default = `{}`.
* `secret` {String|Buffer|Object} See [jwt.sign](https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback). Default = `'sauce'`.
* `options` {Object} See [jwt.sign](https://github.com/auth0/node-jsonwebtoken#jwtsignpayload-secretorprivatekey-options-callback).
**Note: The config must include a `http` and/or a `ws` property unless you're using electron in which case just set `electron: true`.**
Instantiate a new RPC server:
```javascript
new Jayson.Server(config);
```
## Client Usage
For use in a browser you can either include the bundle [`dist/jayson.min.js`](https://github.com/mhingston/jayson/blob/master/dist/jayson.min.js) or you can import the module using a module loader.
**Note: When used in a browser the global variable `window.Jayson` is set.**
```javascript
// Import the module
const Jayson = require('jayson');
```
Define your config (default values shown below):
```javascript
const config =
{
retryDelay: 3000,
timeout: 60000,
logger: false,
url: 'http://127.0.0.1:3000'
}
```
* `retryDelay` {Number} If the connection to the WebSocket server is lost how often should the client attempt to reconnect (in milliseconds). Default = `3000`.
* `timeout` {Number} How long to wait for a response for every RPC call (in milliseconds). Default = `60000`.
* `logger` {Boolean|Function} Set to true to have debug log written to the console or pass in a function to receive the log messages. Default = `undefined`.
* `url` {String} The URL of the Jayson server. To connect to a WebSocket server use a WebSocket protocol i.e. `ws://` or `wss://`. If you're using electron this isn't required. Default = `undefined`.
* `electron` {Boolean} Whether the client is running in electron. Default = `undefined`.
Instantiate a new RPC client:
```javascript
const client = new Jayson.Client(config);
```
### Class: Client
#### client.connect(callback) [async]
Connect to the RPC server.
* `callback(error, client)` {Function} Callback function (optional).
* `error` {Object|Null} Error object.
* `client` {Object} The client instance.
#### client.discover() [async]
Retrieve the RPC methods schema from the RPC server. This is necessary to validate future RPC calls. If you don't call this method then schema validation will be disabled.
* `callback(error, result)` {Function} Callback function.
* `error` {Object|Null} Error object.
* `result` {Object} Method schema.
#### client.call(args) [async]
Call a method on the RPC server.
* `args` {Object|Array``}.
* `method` {String} **(Required)** Name of the RPC method to call.
* `params` {Array|Object} Arguments to pass to the RPC method. Be aware that your params will be serialized as JSON (i.e. [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)).
* `auth` {String} A [JWT](https://github.com/auth0/node-jsonwebtoken) providing the authentication context.
* `timeout` {Number} How long to wait for a response for the RPC call (in milliseconds).
* `notification` {Boolean} Whether the call is a notification or not (i.e. expects a response).
* `callback` {Function} Callback function.
* `error` {Object} Error object.
* `result` {String|Number|Boolean|Null|Undefined|Array|Object} Result from the RPC call.
### Example
```javascript
client.connect()
.then(() => client.discover())
.then(() => client.call(
{
method: 'foo'
}))
.then(response =>
{
console.log(response);
})
.catch(error =>
{
console.log(error.message);
})
```
See the [examples](https://github.com/mhingston/jayson/blob/master/examples) folder for more.
## Generating Documentation
Once you have an API server up and running and have provided a schema for some/all of your methods you can generate a HTML file using [docson](https://github.com/TexKiller/node-docson).
From your project directory:
`node node_modules/.bin/docson --server --output `
e.g.
`node node_modules/.bin/docson --server http://127.0.0.1:3000 --output index.html`
## Notes
* Jayson supports calling async methods, i.e. functions returning a promise.
* Rate limiting requests is not supported and is beyond the scope of this project. It's better handled by a reverse proxy and/or firewall.