Isomorphic async RPCs on top of message based communication protocols.
Out of the box support for:
- Browser WebSockets
- Browser WebWorker
- NodeJS IPC (inter-process-communication)
- NodeJS UDP sockets (bidirectional)
- NodeJS duplex streams
- request-response mapping
- concurrent requests
- timout control
- remote stack traces on / off
- customisable message encoding
- NodeJS and Browser support
- TypeScript support
The package contains well formated JS files and TypeScript declarations. Once bundled and minified (e.g. using esbuild) it boils down to tiny ~4kb.
Both peers can expose and invoke RPC APIs. For brevity, only the server side is implemented in this example.
import cluster from "node:cluster";
import { rpcFromIpc } from "rpc-async";
/* optional interface */
interface Server {
add: (a: number, b: number) => number;
disconnect: () => void;
}
if (cluster.isPrimary) {
/* --- MAIN PROCESS ---- */
const worker = cluster.fork();
const rpc = rpcFromIpc(worker);
/* Implement and expose local methods.
✅ Use generics for type safety */
rpc.expose<Server>({
add: (a: number, b: number) => a + b,
disconnect: () => cluster.disconnect(),
});
} else {
/* --- CHILD PROCESS ---- */
/* ✅ Use generics for code completion */
const rpc = rpcFromIpc<Server>(process);
/* rpc.request[method]() invokes promisified non void remote methods */
const sum = await rpc.request.add(3, 4);
console.log("sum =", sum); // => 7
/* rpc.notify[method]() invokes void remote methods */
rpc.notify.disconnect();
}
Name | Platform | Source | Test |
---|---|---|---|
rpcFromStream | NodeJS | source | test |
rpcFromIpc | NodeJS | source | test |
rpcFromUdp | NodeJS | source | test |
rpcFromWebSocket | Browser | source | TODO |
rpcFromWebWorker | Browser | source | TODO |
If you have a communication channel that allows you to
- send messages
- listen to incoming messages
- remove your listener (optional, but recommended)
You can build your own RPC wrapper.
/* -- pseudocode for an imaginary "com"unication channel -- */
import { createRpc, type Handler } from 'rpc-async'
export function myCustomRpc<T extends Handler>(com: any) {
return createRpc<T>({
/* required: send messages */
send: (msg) => com.send(msg),
/* required: listen to and route incoming messages. Should return a detach function */
attach: (route) => {
com.on("message", route)
return () => com.removeListener("message", route)
},
/* optional message codec, depending on the needs of your communication channel */
encode: (obj: any) => JSON.stringify(obj),
decode: (text: string) => JSON.parse(text)
});
}
This pseudocode is quite similar to the templates listed above. Usually the communication channels have quite similar APIs and only slight adjustments are required.
Read about rpc-async protocol specs.
Publish via
npm run deploy [major|minor|patch]
# build > test > tag > publish #
Credits
Other similar solutions
trpc provides a feature rich "End-to-end typesafe API".