1.1.13 • Published 2 years ago

passage-rpc v1.1.13

Weekly downloads
2
License
ISC
Repository
github
Last release
2 years ago

Passage RPC

Client and server side JSON-RPC 2.0 websockets

This is a websocket subprotocol implementation designed for remote procedure calls and server responses.

http://www.jsonrpc.org/specification

Installation from NPM

Install the package.

npm i passage-rpc --save

Import it into your server side script.

const Passage = require('passage-rpc');

Import it into your client side script.

import Passage from 'passage-rpc';

Installation using IIFE

Download and include the library onto your page, a minified version can be found in the /dist directory.

<script src="/javascripts/passage-rpc.min.js"></script>

Setup

Create a new instance of Passage providing a uri and set of options.

const options = {
    reconnect: true,
};

const passage = new Passage('wss://example.com', options);

passage.on('rpc.open', () => {
    console.log('connected!');
});

Options

requestTimeout <default: 6000>

The amount of time the server can take responding to requests before a timeout.

reconnect <default: false>

Whether the client should attempt to reconnect when disconnected.

reconnectTimeout <default: 2000>

The amount of time to wait between reconnection attempts.

reconnectTries <default: 60>

Maximum number of reconnection attempts.

persistent <default: false>

Whether the library should keep trying to reconnect forever. The library will first use the reconnect feature, and if it runs out of tries will use this feature.

persistentTimeout <default: 10 60 1000>

The amount of time to wait between each persistent reconnection attempt.

Events

When the server sends a notification to your application, it triggers an event. This library uses node's EventEmitter therefore events can be listened for in a common way.

methoddescriptionparams
rpc.messageMessage was received.data
rpc.openConnection established.
rpc.closeConnection closed.reconnecting
rpc.errorError has occurred.Error
passage.on('myapp.welcome', (params) => {
    console.log(params);
});

On rpc.close a parameter is passed representing whether the module intends to reconnect. This will only be true if the reconnect option has been set to true, and the maximum number of reconnectTries has not been met. The persistent option will not affect this.

Instance

close (code?: number, reason?: string) => void

Closes the connection using the optional code and reason given.

connect () => void

This will close the connection, then reconnect.

readyState

The ready state of the connection, useful to compare against named ready states available on the constructor.

namevaluelocation
CONNECTING0Passage.CONNECTING
OPEN1Passage.OPEN
CLOSING2Passage.CLOSING
CLOSED3Passage.CLOSED
if (passage.readyState !== Passage.OPEN) {
    console.log('Not connected');
}

send (method: string, params: any, callback?: (error: Error, result?: any) => void, timeout?: number) => void

If a callback is provided, then the server will respond once it has finished processing. It may return an error or a result once completed but not both. Params will be available for consumption on the server. If a timeout is provided it will override the default requestTimeout from options.

passage.send('myapp.hello');

passage.send('myapp.hello', (error, response) => {
    if (error) throw error;
    console.log(response);
});

passage.send('myapp.hello', { my: 'params' });

passage.send('myapp.hello', { my: 'params' }, (error, response) => {
    if (error) throw error;
    console.log(response);
});

Note: If a callback is not provided and the connection is not available this method will throw an error.

Sending more than one request at the same time

JSON-RPC supports sending an array of messages. To do this the library exposes helper methods for you to use. A full example sending multiple messages can be seen below.

const callback = (error, response) => {
    console.log(response);
};

const messages = [
    passage.buildMessage('myapp.request', callback),
    passage.buildMessage('myapp.request', { code: 'the stork swims at midnight' }),
    passage.buildMessage('myapp.alert', 'important message')
];

const payload = JSON.stringify(messages);
passage.connection.send(payload);

buildMessage (method: string, params: any, callback?: (error: Error, result?: any) => void, timeout?: number) => Object

This creates a simple object for consumption by the server. It takes the same values as the send method, however does not stringify or send the message. If a callback is provided it will timeout, if you use this function you should send your payload soon.

expectResponse (callback: (error: Error, result?: any) => void, timeout?: number) => number

Returns a number representing a message id. The callback will timeout if a response containing the message id is not received in time.

Server setup

The server implementation is built on the npm ws library and shares several similarities with it. There are a few additional options and events utilised for JSON-RPC.

const Passage = require('passage-rpc');

const options = {
    port: 8000,
    heartrate: 30000,
    methods: {
        'myapp.hello': () => 'hi';
    }
};

const server = new Passage.Server(options);

server.on('rpc.listening', () => {
    console.log('Listening on port: ' + port);
});

heartrate <default: 30000>

Periodically each of the connected clients will be pinged terminating ones for which there is no response. Checking every 30 seconds is a good default but you might wish to adjust this.

methods <default: {}>

The methods parameter is a dictionary of procedures your server listens to from the client. In this case if a client sends myapp.hello the server will run the associated function and respond with "hi". You may return an Error instead, or even nothing at all.

Whether the server responds is dependent on the client. If the client is not waiting for a response the server will not send one. Every method is expected to be in the following format.

(params: any, client: ConnectedClient) => any

Must return a Promise if you are doing something which is asyncronous.

For example:

const methods = {
    'myapp.findUser': async (userId) => {
        const user = await findUser(userId);
        return { user };
    }
};

Errors

When being returned from the server, it should be a Error instance. If your method is a Promise it is acceptable to reject. The following attributes are transmitted across the network, message name code and data. The data attribute contains any additional information you would like to include but must be stringifiable into JSON.

There are some errors the library may return itself in callbacks.

namecodemessage
Timeout408Timeout
ServiceUnavailable503Service unavailable
ParseError-32700Parse error
InvalidRequest-32600Invalid request
MethodNotFound-32601Method not found

Server events

Events on the server are handled differently than on the client in most cases, but there are important ones.

methoddescriptionparams
rpc.listeningServer is listening.
rpc.connectionConnection established.ConnectedClient, req object
rpc.errorError has occurred.Error

Server instance

close (callback?: () => void) => void

Closes the server then runs the callback.

clients

Set of connected clients.

ConnectedClient instance

The rpc.connection event offers a connected client instance, and a req object. The connected client has its own events separate from the server.

methoddescriptionparams
rpc.messageMessage was received.data
rpc.closeConnection closed.
rpc.errorError has occurred.Error

close (code?: number, reason?: string) => void

Closes the connection using the optional code and reason given.

readyState

The ready state of the connection.

send (method: string, params: any, callback?: (error: Error) => void) => void

Send a notification to the connected client. The server cannot expect a response from the client with the current implementation. The callback is only to let you know that the message was sent successfully.

client.send('myapp.welcome');

client.send('myapp.welcome', { my: 'params' });

client.send('myapp.welcome', (error) => {
    if (!error) console.log('Notification sent');
});

client.send('myapp.welcome', { my: 'params' }, (error) => {
    if (!error) console.log('Notification sent');
});

Note: If a callback is not provided and the connection is not available this method will throw an error.

Sending more than one notification at the same time

A full example from the server can be seen below.

const messages = [
    client.buildMessage('myapp.notify'),
    client.buildMessage('myapp.notify', { friends: 'forevah' }),
    client.buildMessage('myapp.alert'),
];

const payload = JSON.stringify(messages);
client.connection.send(payload, (error) => {
    if (!error) console.log('Notifications sent');
});

buildMessage (method: string, params?: any) => Object

This creates a simple object for consumption by the client. It takes nearly the same values as the send method, however does not stringify or send the message and does not accept a callback.

Example

Server

const Passage = require('passage-rpc');

const port = 8080;
const methods = {
    'myapp.cats.list': async () => {
        const cats = await getCats();
        return { cats };
    }
};

const server = new Passage.Server({ port, methods });

server.on('rpc.listening', () => {
    console.log('Server listening on port: ' + port);
});

server.on('rpc.connection', (client) => {
    setTimeout(() => {
        client.send('myapp.hi', { message: 'Connected 10 seconds ago.' });
    }, 10000);
});

Client

const Passage = require('passage-rpc');

const passage = new Passage('ws://localhost:8080');

function processResponse (error, response) {
    if (error) throw error;
    console.log(`Returned ${response.cats.length} cat(s).`);
}

passage.on('myapp.hi', (params) => {
    console.log(params.message);
    passage.send('myapp.cats.list', processResponse);
});
1.1.13

2 years ago

1.1.12

6 years ago

1.1.11

6 years ago

1.1.10

6 years ago

1.1.9

6 years ago

1.1.8

6 years ago

1.1.7

6 years ago

1.1.6

6 years ago

1.1.5

6 years ago

1.1.4

6 years ago

1.1.3

6 years ago

1.1.2

6 years ago

1.1.1

6 years ago

1.1.0

6 years ago

1.0.0

6 years ago