async-call-rpc JSON RPC Client & Server
async-call-rpc is a lightweight, zero-dependency JSON RPC server and client library written in TypeScript, designed for any ECMAScript 2018+ environment. The current stable version is 6.4.2, and the package sees active development with frequent patch and minor releases addressing bug fixes and introducing new features like the `encoder` option. Key differentiators include its full TypeScript support, custom encoder capabilities for complex data types, and experimental support for async generators. It works in both Node.js and browser environments and is published on both npm and JSR, offering flexibility in module consumption. It explicitly does not support ES5 environments or JSON RPC 1.0.
Common errors
-
TypeError: AsyncCall is not a constructor
cause Attempting to use `require` or incorrect default import syntax for a package primarily designed for ES Modules, or incorrect access in a UMD context.fixUse a named ESM import: `import { AsyncCall } from 'async-call-rpc';`. If in a browser UMD context, access via `const { AsyncCall } = globalThis.AsyncCall;`. -
Property 'serializer' is deprecated and will be removed in a future major release.
cause Using the deprecated `serializer` option in the `AsyncCall` constructor.fixRefactor your code to use the `encoder` option instead. Replace `serializer.serialization` with `encoder.encode` and `serializer.deserialization` with `encoder.decode`. -
Cannot find name 'AsyncGeneratorCall' or 'AsyncGeneratorCall' is not defined.
cause Attempting to import `AsyncGeneratorCall` from the default 'async-call-rpc' entry point, or the runtime environment lacks async generator support.fixImport `AsyncGeneratorCall` specifically from the full entry point: `import { AsyncGeneratorCall } from 'async-call-rpc/full';`. Ensure your JavaScript environment targets ES2018 or higher.
Warnings
- breaking The `serializer` option in AsyncCall constructor has been deprecated and replaced by the `encoder` option. Custom serialization logic needs to be migrated.
- gotcha Async generator support in `async-call-rpc` can lead to memory leaks on the server side if not handled carefully.
- breaking This package ships with ECMAScript 2018 syntax (`async function`) and does not support ES5 environments or JSON RPC 1.0.
- gotcha When installing via JSR (e.g., Deno, Bun), the import path for `async-call-rpc` changes from `'async-call-rpc'` to `'@works/json-rpc'`. Additionally, the `utils/` entrypoint is not published on JSR.
Install
-
npm install async-call-rpc -
yarn add async-call-rpc -
pnpm add async-call-rpc
Imports
- AsyncCall
const AsyncCall = require('async-call-rpc');import { AsyncCall } from 'async-call-rpc'; - AsyncGeneratorCall
import { AsyncGeneratorCall } from 'async-call-rpc';import { AsyncGeneratorCall } from 'async-call-rpc/full'; - Encoder
import { Encoder } from 'async-call-rpc';import type { Encoder } from 'async-call-rpc';
Quickstart
import { AsyncCall } from 'async-call-rpc';
// Define a simple in-memory channel for demonstration
class InMemoryChannel {
private clientSend?: (data: string) => void;
private serverSend?: (data: string) => void;
// Simulate client sending to server
clientChannel = {
send: (data: string) => {
if (this.serverSend) {
this.serverSend(data);
}
},
on: (cb: (data: string) => void) => {
this.clientSend = cb;
}
};
// Simulate server sending to client
serverChannel = {
send: (data: string) => {
if (this.clientSend) {
this.clientSend(data);
}
},
on: (cb: (data: string) => void) => {
this.serverSend = cb;
}
};
}
const channel = new InMemoryChannel();
// Define the server API interface
interface MyServerAPI {
add(a: number, b: number): Promise<number>;
greet(name: string): Promise<string>;
}
// Server implementation
const serverImpl: MyServerAPI = {
async add(a, b) {
console.log(`Server received: add(${a}, ${b})`);
return a + b;
},
async greet(name) {
console.log(`Server received: greet('${name}')`);
return `Hello, ${name}!`;
}
};
// Create the RPC server instance
const server = new AsyncCall(serverImpl, { channel: channel.serverChannel });
// Create the RPC client instance (null for `thisSideImplementation` means it's purely a client)
const client = new AsyncCall<MyServerAPI>(null, { channel: channel.clientChannel });
// Use the client to call server methods
async function runClient() {
console.log('Client calling add(5, 3)');
const resultAdd = await client.add(5, 3);
console.log('Result of add(5, 3):', resultAdd); // Expected: 8
console.log('Client calling greet("World")');
const resultGreet = await client.greet('World');
console.log('Result of greet("World"):', resultGreet); // Expected: "Hello, World!"
// Clean up server (optional, for explicit shutdown)
server.stop();
}
runClient().catch(console.error);