jsonrpc2-ws
A simple implementation of JSON-RPC 2.0 over WebSocket for Node.js
Install
npm i @c10k/jsonrpc2-ws
Usage
Server
JsonRpcWsServer
constructor params same
as WebSocketServer.
const port = 8080;
const server = new JsonRpcWsServer({port});
const sleep = async (ms) => new Promise(resolve => setTimeout(resolve, ms));
const echo = (params) => params;
const error = (params) => {
throw new Error(`${params[0]}`)
};
const time = () => Date.now();
server.setMethod('echo', echo);
server.setMethod('sleep', sleep);
server.setMethod('error', error);
server.setMethod('time', time);
Client
JsonRpcWsClient
constructor params same
as WebSocket.
const url = `ws://localhost:8080`;
const client = await JsonRpcWsClient.connect(url);
const timeFromServer = await client.request('time');
console.info(`${timeFromServer}`);
Use case
Stream
const port = 8080;
const server = new JsonRpcWsServer({port}, () => {
setInterval(async () => {
for (const ws of server.wss.clients) {
// you can check ws context here
server.notification(ws, 'ping', [Date.now]);
}
}, 1000);
});
const url = `ws://localhost:8080`;
const client = await JsonRpcWsClient.connect(url);
client.setMethod('ping', (params) => {
const [time] = params;
console.info(`client receive server time=${time}`);
});
Batch
const port = 8080;
const server = new JsonRpcWsServer({port});
let count = 0;
server.setMethod('counter', () => ++count);
const url = `ws://localhost:8080`;
const client = await JsonRpcWsClient.connect(url);
const pipeline = client.createPipeline();
await pipeline.request('counter');
await pipeline.request('counter');
await pipeline.request('counter');
const responses = await pipeline.execute();
for (const response of responses) {
const {id, result, error} = response;
// do something
}
Define ID generator
const url = `ws://localhost:8080`;
const client = await JsonRpcWsClient.connect(url);
client.idGenerator = () => uuid();
Auth
const port = 8080;
const server = new JsonRpcWsServer({port});
server.handler.getMethod = async ({id, method, params}, socket) => {
const auth = await socket.getContext('auth');
if (!auth && method.startsWith('private')) {
return () => {
throw new JsonRpcError(JSON_RPC_ERROR, 'Need auth', undefined);
}
}
return server.handler.methods.get(method);
};
const login = async (params, socket) => {
const [username, password] = params;
const auth = password === username;
socket.setContext('auth', auth);
if (auth) {
await socket.setContext('username', username);
} else {
await socket.deleteContext('username');
}
return auth;
};
const whoami = async (params, socket) => {
return await socket.getContext('username');
}
server.setMethod('public.login', login);
server.setMethod('private.whoami', whoami);
const url = `ws://localhost:8080`;
const client = await JsonRpcWsClient.connect(url);
await client.request('public.login', ['hello', 'hello']);
await client.request('private.whoami');
Authentication before connection, see https://github.com/websockets/ws/tree/8.6.0#client-authentication, example see authenticate-when-upgrade
Dependencies
- https://github.com/websockets/ws
- https://jestjs.io/zh-Hans/docs/getting-started
- https://jestjs.io/zh-Hans/docs/expect#expectclosetonumber-numdigits
- https://github.com/sindresorhus/get-port