JSON-RPC inspired server and client with HTTP and WebSocket transports and JSON Schema IDL.
npm install mhingston/jayson
Jayson uses a custom version of JSON-RPC for messages. The specification is based on JSON-RPC 2.0 with the following changes:
-
The
request
andresponse
objects must contain the propertyjayson
with the value set to the version of the API being used. This property replaces thejsonrpc
property.e.g.
{ "jayson": "1.0" }
-
A
response
object without anerror
property and without aresult
property indicates that the remote method returnedundefined
. -
The following additional properties can be defined within a
request
object:auth
{String} A JWT 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).
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. If a method doesn't provide a full schema then any calls to the method won't have theirauth
orparams
properties validated. Similarly if thereturns
schema isn't defined then the return value won't be validated.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. -
timeout
{Number} How long to wait (in milliseconds) before timing out the request. If not provided then thetimeout
value will be used from the server config.
Note: The server is intended to be run behind a reverse proxy, unless you're using electron ofcourse.
// 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 providing the authentication context.
Define your config (default values shown below):
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. 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 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 tonull
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. Default ={}
.helmet
{Object} Helmet options, see koa-helmet. Default ={noCache: true}
.compress
{Object} Compress options, see compress. 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 ={}
.
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:
new Jayson.Server(config);
For use in a browser you can either include the bundle 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.
// Import the module
const Jayson = require('jayson');
Define your config (default values shown below):
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://
orwss://
. 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:
const client = new Jayson.Client(config);
Connect to the RPC server.
callback(error, client)
{Function} Callback function (optional).error
{Object|Null} Error object.client
{Object} The client instance.
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.
Call a method on the RPC server.
args
{Object|Array<Object>
}.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
).auth
{String} A JWT 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.
client.connect()
.then(() => client.discover())
.then(() => client.call(
{
method: 'foo'
}))
.then(response =>
{
console.log(response);
})
.catch(error =>
{
console.log(error.message);
})
See the examples folder for more.
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.
From your project directory:
node node_modules/.bin/docson --server <Server URL> --output <Output File>
e.g.
node node_modules/.bin/docson --server http://127.0.0.1:3000 --output index.html
- 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.