# rpc-websockets: JSON-RPC 2.0 over WebSockets for Node.js

> rpc-websockets gives Node.js and TypeScript projects a JSON-RPC 2.0 layer on top of WebSockets, with methods, events, notifications, protected methods and a client that reconnects. It is an open source library with a paid hosted tier built on the same API.

**elpheria/rpc-websockets** — JSON-RPC 2.0 implementation over WebSockets for Node.js and JavaScript/TypeScript

- Repository: https://github.com/elpheria/rpc-websockets
- Stars: 626 · Forks: 130
- Language: JavaScript
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/elpheria-rpc-websockets

## A message envelope you would otherwise have to design

The problem this library solves is not WebSockets. The problem is what you put inside them. Once a socket is open, a Node.js service still has to decide how a request is framed, how a response is matched to its request, how a failure is signalled, how a client asks to be told when something changes, and how a method that requires authentication is distinguished from one that does not. Get any of that wrong and you have built a small broken RPC system.

rpc-websockets implements JSON-RPC 2.0 over WebSocket, and the library description calls it a JSON-RPC 2.0 implementation over WebSockets for Node.js and JavaScript/TypeScript. The README frames the goal as enabling developers to easily implement business logic that includes messaging between users, machines or any devices, by sending and receiving JSON data through the WebSocket communication protocol to support two-way notification push, running RPC methods and firing any types of event signalling.

One constraint in that description is worth holding on to, because it decides whether the library fits your design: only clients can call RPC methods, and not vice versa at the moment. The server exposes methods; the server does not call back into the client as an RPC peer. If your protocol needs the server to invoke a function on the client, this library does not give you that.

Both frontend and backend development environments are supported, and the library is built on Node.js supporting both LTS and Current versions, with the package.json requiring node 18 or newer.

## Methods, protected methods, events and notifications

Four primitives cover most of what a realtime application needs, and each maps to a distinct call in the API.

A method is registered on the server and invoked by the client. The quick start registers a sum function that adds two parameters, and a second one called account that returns a list of strings and is then marked protected. The protected marker is the authentication mechanism: the client must log in first before calling it, and the quick start shows the login call carrying a username and password, with a catch branch printing auth failed.

Events are the broadcast channel. The server creates an event with server.event, lists them with server.eventList, and emits to subscribers with server.emit. The client subscribes with ws.subscribe, receives a callback under the event name, and unsubscribes with ws.unsubscribe. A notification, by contrast, is a one-way message to the server with no response expected, sent with ws.notify.

The naming is a little confusing at first because both directions use the word event, but the mechanics are clear: subscribe and emit for many-to-many broadcast, notify for one-way messages to the server.

The API documentation in API.md covers both the WebSocket server and the client classes in JavaScript and TypeScript, which is where the full method list lives rather than the README.

## A first server and client in one script

Installation is a single npm command, and the package is published under the name rpc-websockets.

```bash
npm install rpc-websockets
```

The quick start then builds a server and a client in one file, which is the fastest way to see the whole shape of the API.

```js
var WebSocket = require('rpc-websockets').Client
var WebSocketServer = require('rpc-websockets').Server

// instantiate Server and start listening for requests
var server = new WebSocketServer({
  port: 8080,
  host: 'localhost'
})

// register an RPC method
server.register('sum', function(params) {
  return params[0] + params[1]
})
```

The server binds a port and a host and registers the method. On the other side, the client connects with ws://localhost:8080 and calls the method inside the open handler, asserting the result equals 8 after adding 5 and 3.

```js
var ws = new WebSocket('ws://localhost:8080')

ws.on('open', function() {
  // call an RPC method with parameters
  ws.call('sum', [5, 3]).then(function(result) {
    require('assert').equal(result, 8)
  })

  // send a notification to an RPC server
  ws.notify('openedNewsModule')

  // subscribe to receive an event
  ws.subscribe('feedUpdated')
```

The protected method flow follows the same shape, with a login promise that resolves before the account call is made and a catch branch for the failure case. Everything is promise based, so the natural async/await style of a modern Node.js service applies without adapters.

## Reconnection state management arrived in version 9

The release history shows where the project has put its effort. v9.2.0, released on 2025-09-24, is titled Reconnection State Management, and v10.0.1 shipped on 2026-05-15. The last push to the trunk branch was on 2026-05-15 as well.

Reconnection is the right thing for a library to invest in, because a WebSocket client in production lives or dies by it. A dropped connection in a chat client, a multiplayer game, an IoT device or a push notification service is a normal event, not an exception. A library that tracks what the client was subscribed to and what was in flight across a reconnect saves every user of it from reimplementing that state machine.

The repository topics give a sense of the intended use cases: messaging, multiplayer, chat, iot, events, push-notifications, distributed-systems, serverless and scalable. Those are all cases where a socket reconnects.

The current release is 10.0.1, and the package.json version matches. Node 18 or newer is required, and the dependency list is short: ws for the transport, eventemitter3 for the event layer, buffer and @swc/helpers for browser builds, with bufferutil and utf-8-validate as optional dependencies. The optional status of the native buffer validation packages is worth noting, since it means the library degrades to the pure JavaScript path when a native build is unavailable, which is common in serverless or prebuilt image environments.

## Dual browser and node builds, and a CommonJS package

The packaging is set up carefully, and it is one of the more useful details in the manifest.

The exports field publishes two conditions. The browser condition points at ./dist/index.browser.mjs for import and ./dist/index.browser.cjs for require, while the node condition points at ./dist/index.mjs and ./dist/index.cjs. A separate browser field maps the node entry points onto the browser ones as well, which covers bundlers and tooling that read that field instead of the exports map. main points at dist/index.cjs, module at dist/index.mjs and types at dist/index.d.ts.

The catch is that type is set to commonjs even though the project ships ESM builds and the source is TypeScript. The reason is that the require and import conditions each have their own file, so a CommonJS consumer resolves the .cjs file and an ESM consumer resolves the .mjs file, and the package-level type field does not need to change. TypeScript sources live in src/, the build runs through tsup with a tsup.config.ts, and there is a separate tsconfig.json plus an @microsoft/api-extractor dependency, which is the tool that produces the public .d.ts surface from the source.

The library itself ships only dist/, so consumers get the compiled output and the type definitions, not the tests or the sources.

## Where rpc-websockets stops being the right tool

Three limits deserve to be stated.

The first is the direction of calling. Only clients can call RPC methods at the moment, so a pattern where the server initiates a request to a connected client, common in bidirectional protocols, is out of reach. The workarounds are events and notifications, and if your protocol genuinely requires request/response from the server side, you will be bending the library rather than using it.

The second is scale. The library is a WebSocket RPC layer built on the ws package, running in a single Node.js process. That is fine for a chat service, a realtime dashboard or a game backend. It is not a broker, and the topics mention serverless and scalable but the README does not describe clustering, shared session state or horizontal fan-out, so a deployment that needs many connections or many instances needs its own architecture around it.

The third is the commercial shape. The project describes itself as an open source edition you use to implement and manage your own WebSocket server instances, and separately offers a Pro plan where the vendor manages the instances and provides a Web Management portal for methods, events and notifications, plus professional support and consulting. The core API is the same, but the boundary matters for procurement and for support: the hosted tier is a commercial relationship with the vendor, and the open source path is self-managed.

On the licence, the repository licence field shows a custom licence GitHub cannot classify, and the README calls it a very permissive licence. Read the LICENSE file directly before relying on that description for anything commercial.

## Comparing with plain ws and with gRPC style contracts

The realistic alternative is to use ws directly and define your own message envelope. That is often the better choice for a single application. A JSON envelope with an id, a method name and a params object is not a large amount of code, and owning it means you can shape it exactly around your protocol, add fields your domain needs, and avoid a dependency. The cost of the direct route is that every project reinvents method dispatch, error mapping, subscription bookkeeping and reconnect handling.

The other comparison point is a schema-first contract system in the style of gRPC, where the interface is defined in a .proto file and code is generated for both sides. That approach gives you typed messages and a clear versioning story, and it is the better fit when many teams in different languages consume the same API. This library takes the opposite trade: JSON-RPC 2.0 over a browser-friendly transport, with no code generation step and no schema file, so the same server code serves a browser client and a Node client.

What rpc-websockets buys over raw ws is exactly the protocol layer: request and response correlation, JSON-RPC error objects, notifications that do not expect a reply, event subscriptions with an explicit subscribe and unsubscribe, and protected methods gated behind a login. If your application needs all of that and does not need server-initiated calls or broker-grade scale, the library is a reasonable shortcut.

The README lists named users including Scratchbox, Loom Network, uniqCast, LeapDAO, Klika Tech, Kodebox, Hey-Group, Hylo and the Witnet Foundation, which is a spread across messaging, IoT, games and blockchain projects rather than a single vertical.

## Conclusion

Adopt rpc-websockets when you already run WebSockets on Node and want a typed RPC layer with events and notifications instead of inventing a message envelope, and skip it when you need bidirectional calling or a broker that handles millions of connections. Before adopting, read the LICENSE file yourself, because the repository licence field is a custom one GitHub cannot classify, and note that v10.0.1 shipped on 2026-05-15 with the last push on the same day, so check that the WebSocket server behaviour you depend on still matches the current release.

## FAQ

### How do I install rpc-websockets?

Run npm install rpc-websockets in your project. The package requires Node.js 18 or newer and is built on the ws library, with browser and node builds published under both import and require conditions.

### How do I call a protected RPC method in rpc-websockets?

Mark the method as protected on the server with the protected call after registering it, then have the client call login with its credentials and wait for the promise to resolve before invoking the method. The quick start shows the failed login being caught and handled.

### What is the difference between an event and a notification in rpc-websockets?

An event is broadcast to subscribers, with the client subscribing, receiving a callback and unsubscribing, while a notification is a one-way message sent to the server with no response expected. The server creates events, lists them and emits them to subscribers.

### Can the server call RPC methods on a connected client in rpc-websockets?

No. The README states that only clients can call RPC methods and not vice versa at the moment, so server-initiated requests to clients are not supported.

### Is there a paid version of rpc-websockets?

Yes. The README describes a Pro plan where the vendor manages the WebSocket server instances and provides a Web Management portal for methods, events and notifications, alongside professional support and consulting, while the OSS edition is free to use and self-managed.

## Sources

- [elpheria/rpc-websockets on GitHub](https://github.com/elpheria/rpc-websockets)
- [Issues](https://github.com/elpheria/rpc-websockets/issues)
- [README](https://github.com/elpheria/rpc-websockets/blob/trunk/README.md)
- [Releases](https://github.com/elpheria/rpc-websockets/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/elpheria-rpc-websockets
