Skip to content

A Javascript JSON-RPC-2.0 transport protocol agnostic client and server engine.

License

Notifications You must be signed in to change notification settings

leonardoraele/json-rpc-dual-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

91 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

json-rpc-dual-engine

GitHub Workflow Status (branch) NPM node-current npm GitHub last commit npm

A zero-dependencies protocol agnostic implementation of the JSON-RPC-2.0 spec.

This package includes a client and a server, but you can import either separately without increasing the size of your final build, if you don't need both.

Install

npm install json-rpc-dual-engine

Usage

Server

Create the server with an object that implements the methods to be exposed to the client:

import { JsonRpcServer } from 'json-rpc-dual-engine/server';

const api = {
	multiply(a, b) {
		return a * b;
	},
	ping() {
		return 'pong';
	},
};

const server = new JsonRpcServer({ api });

Register a onresponse callback to receive the json-rpc responses generated by the server:

// `onresponse` will be called whenever the server generates a response.
// It's your job is to send it to the client somehow.
server.onresponse = response => console.log(response);

Then use the accept method to deliver incoming remote method calls to the server. The server validates the json-rpc message call, calls the appropriate registered method, and generate the appropriate json-rpc response based on the return of the registered handler.

// It's your job to receive the request from the client somehow.
const request = '{"jsonrpc":"2.0","method":"multiply","params":[7,11],"id":"1"}';

server.accept(request);
// Output: '{"jsonrpc":"2.0","result":77,"id":"1"}'

Client

Create the client:

import { JsonRpcClient } from 'json-rpc-dual-engine/client';

const client = new JsonRpcClient();

Register a onrequest callack to send requests made by the client to the server.

// It's your job to send the requests to the server somehow.
client.onrequest = request => console.log(request);

client.sendRequest('multiply', [7, 11]);
// Output: '{"jsonrpc":"2.0","method":"multiply","params":[7,11],"id":"1"}'

client.sendNotification('ping'); // no return
// Output: '{"jsonrpc":"2.0","method":"ping"}'

// Or use the proxy object:
client.remote.multiply(7, 11);
client.remote.ping();

Use the accept method to deliver incoming server responses to the client to resolve request promises.

// Using setTimeout to simulate a future incoming message from the server
setTimeout(100, () => {
	const response = '{"jsonrpc":"2.0","result":77,"id":"2"}'; // Gets a response from the server somehow
	client.accept(response);
});

const result = await client.request('multiply', [7, 11]);

console.log(result); // Output: 77

Or using the proxy object:

const remote = client.remote;

const result = await remote.multiply(7, 11);

console.log(result); // Output: 77

Server and Client

The JsonRpcDualEngine class includes both a client and a server in it. It's useful in cases where you need bidirectional communication. The accept() method of the dual engine accepts both requests and responses.

import { JsonRpcDualEngine } from 'json-rpc-dual-engine';

const engine1 = new JsonRpcDualEngine({
	api: {
		getId() { return '1'; }
	}
});

const engine2 = new JsonRpcDualEngine({
	api: {
		getId() { return '2'; }
	}
});

engine1.onmessage = message => engine2.accept(message);
engine2.onmessage = message => engine1.accept(message);

console.log(await engine1.sendRequest('getId')); // Output: '2'
console.log(await engine2.sendRequest('getId')); // Output: '1'

WebSocket Example

On the server side:

import { JsonRpcServer } from 'json-rpc-dual-engine/server';
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });
const engine = JsonRpcServer({
	api: {
		multiply(a, b) {
			return a * b;
		}
	}
});

wss.on('connection', ws => {
	ws.on('message', request => engine.accept(request)); // Messages from the client are handled by the engine
	engine.onresponse = response => ws.send(response); // Responses are sent to the client via websocket
});

On the client side:

import { JsonRpcClient } from 'json-rpc-dual-engine/client';
import WebSocket from 'ws';

const websocket = new WebSocket('ws://remote.example');
const engine = new JsonRpcClient();

engine.onrequest = request => websocket.send(request); // Requests are sent to the server via websocket
websocket.on('message', response => engine.accept(response)); // Responses from the server are handled by the engine

// Waits for connection before sending a message
await new Promise((resolve, reject) => {
	websocket.onopen = resolve;
	websocket.onerror = reject;
});

// Sends a json-rpc request through webscoket to the remote server and waits for the response
const result = await engine.remote.multiply(7, 11);

console.log(result); // Output: 77

Development Environment

Install Deps

npm install

Test

npm test

Coverage

npm run coverage

LICENSE

MIT