1.0.10 • Published 1 year ago

wss-rpc2 v1.0.10

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

wss-rpc2

WebSocket Server and Client implementation of the JSON RPC2 spec.\ Powered by ws.

npm NPM Coverage Status

Basic functionality, according to the spec:

  • Method call: client -> server (request/response)
  • Notifications: client -> server (notification)

Extended functionality:

  • Events: server -> client (to all / to some / to a single client)
  • Reconnect and keep alive
  • Stateful client connection
  • The client works in both node and browser

Quick start

Installation:

npm i wss-rpc2

Backend / Server:

import { RPCServer, RPCEvent } from 'wss-rpc2'

const server = new RPCServer({ port: 3000 })

server.registerMethod('multiply', (params) => {
  return params[0] * params[1]
})

Frontend / Client:

import { RPCClient } from 'wss-rpc2'

const client = new RPCClient('ws://localhost:3000')
const result = await client.call('multiply', [3, 5])

See more examples.

RPCServer API

new RPCServer(options: IRPCServerOptions)

Parameters:

  • options: IRPCServerOptions<State = any> extends ServerOptions
    • wss?: ws.WebSocketServer - existing WebSocketServer (ws) instance
    • keepAlive?: number - ping clients every N ms (default: 300000 // 5 min)
    • stateFactory?: () => State - initial state factory of the connected client
    • ...ServerOptions (see ws library)

RPCServer.registerMethod(name: string, method: IRPCMethod): void

Parameters:

  • name: string - name of the RPC method
  • method: IRPCMethod - RPC method function, see types

RPCServer.callMethod(name: string, params: IRPCParams): Promise\

Call a method explicitly

Parameters:

  • name: string - name of the RPC method
  • params: IRPCParams - params of the RPC method

RPCServer.getConnections(): RPCConnection[]

Returns active connection instances.


RPCServer.close(): Promise

Closes all active client's connections and stops the server.


RPCServer.on(eventName: string, listener: Function, once?: boolean): Function

Subscribe a listener to the event. Returns unsubscribe function.

RPCServer Events:

  • listening (params: empty) - on server start
  • connect (params: RPCConnection) - on client connect
  • disconnect (params: RPCConnection) - on client disconnect
  • request (params: RPCRequest) - on after request received
  • response (params: RPCResponse) - on before response sent
  • error (params: Error | unknown) - on server error
  • close (params: empty) - on server stop

RPCClient API

new RPCClient(address: string, options?: IRPCClientOptions)

Parameters:

  • address: string - URL of the RPCServer
  • options: IRPCClientOptions
    • autoConnect?: boolean - if false, then use client.connect() (default: true)
    • reconnectIntervals?: number[] - reconnect intervals sequence. Example: 1000, 1500, 2000 will wait between reconnect: 1000ms, 1500ms, 2000ms, ..., 2000ms, until reconnectLimit is reached (default: 1000 - every 1000 ms)
    • reconnectLimit?: number - 0 - unlimited, -1 - disabled (default: 1000)
    • requestTimeout?: number - request timeout (default: 10000)

RPCClient.state: 'init' | 'connecting' | 'connected' | 'stopped'

Client connection state:

  • init - before connect() has been called
  • connecting - connection or reconnection is in process
  • connected - active connection state
  • stopped - if disconnect() has been called or reconnectLimit is reached

RPCClient.connected: Promise\

Connection promise. However, it is not necessary to wait for a connection, because requests will be queued up until the connection is established.

const client = new RPCClient()
await client.connected
// connected!

RPCClient.connect(): Promise\

Manual connect, if autoConnect = false

const client = new RPCClient({ autoConnect: false })
await client.connect()
// connected!

RPCClient.disconnect(): Promise\

Disconnects and stops a reconnect-observer.


RPCClient.call(method: string, params?: IRPCParams): Promise\

Calls the method and waits for a response.


RPCClient.notify(method: string, params?: IRPCParams): void

Notifies the server without waiting for a response.


RPCClient.on(eventName: string, listener: Function, once?: boolean): Function

Subscribe a listener to the event. Returns unsubscribe function.

RPCClient Events:

  • connect (params: empty) - on connect
  • disconnect (params: empty) - on disconnect
  • error (params: Error | unknown) - on error
  • request (params: IRPCRequestObject) - on before request sent
  • response (params: IRPCResponseObject) - on response received
  • event (params: RPCEvent) - on server event received

RPCConnection <State = unknown> API

id: string

Connection identifier


ws: WebSocket

Browser WebSocket object (isomorphic-ws is used for the node client)


state?: State

Domain state of the connection, if defined by IRPCServerOptions.stateFactory (and State generic for TS).


lastActivity: number = 0

Last client's activity timestamp


RPCConnection.emit (event: RPCEvent, cb?: (e: Error) => void)

Emit an event to the client. Example:

const event = new RPCEvent({ event: 'hello', params: {} })
server.getConnections().forEach(connection => {
  connection.emit(event, e => console.error(e))
})

Types

interface IRPCServerOptions extends ws.ServerOptions {
  wss?: WebSocketServer
  keepAlive?: number
  stateFactory?: () => State
}
interface IRPCClientOptions {
  autoConnect?: boolean
  reconnectIntervals?: number[]
  reconnectLimit?: number
  requestTimeout?: number
}
type IRPCParams = { [key: string]: unknown } | unknown[]
type IRPCMethod<Req extends IRPCParams = any, Res = any, State = any>
        = (params: Req, connection: RPCConnection<State>) => Res
interface IRPCRequestObject {
  jsonrpc: '2.0'
  id?: string | number
  method: string
  params?: IRPCParams
}
interface IRPCResponseObject {
  jsonrpc: '2.0'
  id: string | number | null
  result?: any
  error?: IRPCError
}
interface IRPCError {
  code: number
  message: string
  data?: unknown
}

Typescript

Typescript support provides several useful features for typing request, responses and connection state.

import { RPCServer } from 'wss-rpc2'

const server = new RPCServer<IAppUserState>({
  stateFactory: () => ({ authorized: false })
})

server.registerMethod<IRpcLogin['request'], IRpcLogin['response']>('login', async (params, connection) => {
  const user = await auth(params)
  if (user) {
    connection.state.authorized = true
    connection.state.email = user.email
    connection.state.id = user.id
  }
  return user
})

interface IAppUserState {
  authorized: boolean
  email?: string
  id?: string
}

interface IRpcLogin {
  request: {
    login: string
    password: string
  }
  response: {
    user?: {
      id: string
      email: string
    }
  }
}
import { RPCClient } from 'wss-rpc2'

const client = new RPCClient(url)
const { error, result } = await client.call<{ user: IUser | null }>('login', { email, password })
console.log(result?.user?.id)

Examples

To be done\ Also see tests

1.0.10

1 year ago

1.0.9

1 year ago

1.0.8

1 year ago

1.0.7

1 year ago

1.0.6

1 year ago

1.0.5

1 year ago

1.0.4

1 year ago

1.0.3

1 year ago

1.0.2

1 year ago

1.0.1

1 year ago

0.2.3

1 year ago

0.2.2

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.1.4

1 year ago

0.1.3

1 year ago

0.1.1

1 year ago