nirpc
A small TypeScript-first RPC library with acknowledgements, middleware, configurable timeouts and a fluent call builder.
Features
- Type-safe client and server interfaces
- Acknowledgements before work starts
- Configurable timeouts for acknowledgement and result
- Optional middleware hook for logging, auth, etc.
- Per-call retry support
- Simple builder API on each remote method
Installation
bun install
Quick start
Basic setup
import { createNirpc } from "nirpc";
interface RemoteFunctions {
hello: (name: string) => string;
calculate: (a: number, b: number) => number;
}
const rpc = createNirpc<RemoteFunctions>({}, {
post: async (data) => {
},
on: (fn) => {
},
});
Calling methods
Direct call:
const result = await rpc.hello("World");
With per-call options using the builder hanging off the method:
const sum = await rpc.calculate
.options({
timeout: 2000,
ackTimeout: 500,
retry: 3,
})
.run(10, 20);
Middleware
Middleware runs before each request and can inspect or modify the outgoing message:
const rpc = createNirpc<RemoteFunctions>({}, {
post: async (data) => { },
on: (fn) => { },
middleware: (req) => {
return {
...req,
};
},
});
Acknowledgements and timeouts
createNirpc can send an acknowledgement as soon as a request is received, before the handler runs. Timeouts are configurable both for the acknowledgement and for the handler result:
const rpc = createNirpc<RemoteFunctions>({}, {
post: async (data) => { },
on: (fn) => { },
ackTimeout: 5000,
timeout: 30000,
onAckTimeoutError: (functionName, args) => {
console.error(`No acknowledgement for ${functionName}`, args);
},
});
API surface
createNirpc
Creates an RPC instance:
const rpc = createNirpc<RemoteFunctions, LocalFunctions>(localFunctions, {
post,
on,
off,
serialize,
deserialize,
bind,
eventNames,
timeout,
ackTimeout,
resolver,
middleware,
onRequest,
onError,
onFunctionError,
onGeneralError,
onTimeoutError,
onAckTimeoutError,
});
Each remote function is exposed as:
rpc.method(...args) â regular call returning a promise
rpc.method.asEvent(...args) â fire-and-forget
rpc.method.options({ timeout?, ackTimeout?, retry? }).run(...args) â call with per-invocation options
Additional helpers:
rpc.$call(name, ...args)
rpc.$callOptional(name, ...args)
rpc.$callEvent(name, ...args)
rpc.$builder(name) â lower-level builder (params().timeout().ackTimeout().retry().execute())
rpc.$close(error?)
rpc.$rejectPendingCalls(handler?)
createNirpcGroup
For broadcasting the same call to multiple channels:
import { createNirpcGroup } from "nirpc";
const group = createNirpcGroup<RemoteFunctions>(localFunctions, () => channels, {
timeout: 10_000,
});
await group.broadcast.processEvent("payload");
await group.broadcast.processEvent.asEvent("payload");
Examples
Run the examples in this repo with:
bun run examples.ts
License
MIT