### WebSocket Server Setup Source: https://github.com/antfu-collective/birpc/blob/main/README.md Set up a birpc server to handle incoming WebSocket connections. Each connection gets its own RPC instance. ```typescript import type { ClientFunctions } from './types' import { WebSocketServer } from 'ws' const serverFunctions: ServerFunctions = { hi(name: string) { return `Hi ${name} from server` } } const wss = new WebSocketServer() wss.on('connection', (ws) => { const rpc = createBirpc( serverFunctions, { post: data => ws.send(data), on: fn => ws.on('message', fn), serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) await rpc.hey('Server') // Hey Server from client }) ``` -------------------------------- ### MessageChannel Port Setup (Bob) Source: https://github.com/antfu-collective/birpc/blob/main/README.md Initialize a birpc client using MessageChannel's port1. MessageChannel handles serialization automatically. ```typescript export const channel = new MessageChannel() import type { AliceFunctions } from './types' import { channel } from './channel' const Bob: BobFunctions = { hey(name: string) { return `Hey ${name}, I am Bob` } } const rpc = createBirpc( Bob, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), }, ) await rpc.hi('Bob') // Hi Bob, I am Alice ``` -------------------------------- ### WebSocket Client Setup Source: https://github.com/antfu-collective/birpc/blob/main/README.md Configure and initialize a birpc client for WebSocket communication. Requires custom serializers/deserializers for JSON. ```typescript import type { ServerFunctions } from './types' const ws = new WebSocket('ws://url') const clientFunctions: ClientFunctions = { hey(name: string) { return `Hey ${name} from client` } } const rpc = createBirpc( clientFunctions, { post: data => ws.send(data), on: fn => ws.on('message', fn), // these are required when using WebSocket serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) await rpc.hi('Client') // Hi Client from server ``` -------------------------------- ### MessageChannel Port Setup (Alice) Source: https://github.com/antfu-collective/birpc/blob/main/README.md Initialize a birpc client using MessageChannel's port2. MessageChannel handles serialization automatically. ```typescript import type { BobFunctions } from './types' import { channel } from './channel' const Alice: AliceFunctions = { hi(name: string) { return `Hi ${name}, I am Alice` } } const rpc = createBirpc( Alice, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) await rpc.hey('Alice') // Hey Alice, I am Bob ``` -------------------------------- ### `createBirpc` with `MessageChannel` — Zero-config local transport Source: https://context7.com/antfu-collective/birpc/llms.txt Demonstrates using `createBirpc` with `MessageChannel` for local communication, eliminating the need for custom serialization as `MessageChannel` handles structured data natively. ```APIDOC ## `createBirpc` with `MessageChannel` — Zero-config local transport `MessageChannel` auto-serializes structured data and handles circular references natively, so no custom `serialize`/`deserialize` is needed. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type AliceFunctions = { hello(name: string): string } type BobFunctions = { hi(name: string): string; bump(): void; getCount(): number } const channel = new MessageChannel() const alice = createBirpc( { hello: name => `Hello ${name}, my name is Alice` }, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) const bob = createBirpc( { hi: name => `Hi ${name}, I am Bob`, bump: () => {}, getCount: () => 0, }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), }, ) console.log(await bob.hello('Bob')) // "Hello Bob, my name is Alice" console.log(await alice.hi('Alice')) // "Hi Alice, I am Bob" ``` ``` -------------------------------- ### Customizing `createBirpcGroup` with `resolver` per channel Source: https://context7.com/antfu-collective/birpc/llms.txt Use the `resolver` option in `createBirpcGroup` to customize function behavior based on individual RPC instance metadata (`$meta`). This allows for per-client logic. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc, createBirpcGroup } from 'birpc' interface ClientFunctions { greet(name: string): string } interface ServerFunctions { hello(name: string): string } const ch1 = new MessageChannel() const ch2 = new MessageChannel() const server = createBirpcGroup( { hello: name => `Hello ${name}!` }, [ { post: data => ch1.port2.postMessage(data), on: fn => ch1.port2.on('message', fn), meta: { role: 'admin' } }, { post: data => ch2.port2.postMessage(data), on: fn => ch2.port2.on('message', fn), meta: { role: 'guest' } }, ], { resolver(name, fn) { if (name === 'hello' && this.$meta?.role === 'admin') return (n: string) => `[ADMIN] Hello ${n}!` return fn }, }, ) const admin = createBirpc( { greet: n => n }, { post: data => ch1.port1.postMessage(data), on: fn => ch1.port1.on('message', fn) }, ) const guest = createBirpc( { greet: n => n }, { post: data => ch2.port1.postMessage(data), on: fn => ch2.port1.on('message', fn) }, ) console.log(await admin.hello('Alice')) // "[ADMIN] Hello Alice!" console.log(await guest.hello('Bob')) // "Hello Bob!" ``` -------------------------------- ### `meta` — Attaching metadata to RPC instances Source: https://context7.com/antfu-collective/birpc/llms.txt The `meta` option allows you to attach arbitrary data to an RPC instance, which can be accessed via `rpc.$meta`. This is particularly useful in group or broadcast scenarios for identifying the source channel or client of a call. ```APIDOC ## `meta` — Attaching metadata to RPC instances The `meta` option lets you attach arbitrary data to an RPC instance, accessible via `rpc.$meta`. This is especially useful in group/broadcast scenarios to identify which channel or client a call is coming from. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' const channel = new MessageChannel() const rpc = createBirpc, { info(): string }> ({ info() { // `this` is the rpc instance when bind = 'rpc' (default) return `I am ${(this as any).$meta?.name ?? 'unknown'}` }, }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), meta: { name: 'server-channel-1' }, }, ) console.log(rpc.$meta) // { name: 'server-channel-1' } ``` ``` -------------------------------- ### `createBirpcGroup` with `resolver` per channel Source: https://context7.com/antfu-collective/birpc/llms.txt The `resolver` option within `createBirpcGroup` allows for customized function behavior based on the metadata of individual RPC instances. It receives `this` bound to the specific RPC instance, enabling inspection of `this.$meta` to tailor responses per connected client. ```APIDOC ## `createBirpcGroup` with `resolver` per channel The `resolver` in a group receives `this` bound to the individual RPC instance, so you can inspect `this.$meta` to customize function behavior per connected client. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc, createBirpcGroup } from 'birpc' interface ClientFunctions { greet(name: string): string } interface ServerFunctions { hello(name: string): string } const ch1 = new MessageChannel() const ch2 = new MessageChannel() const server = createBirpcGroup( { hello: name => `Hello ${name}!` }, [ { post: data => ch1.port2.postMessage(data), on: fn => ch1.port2.on('message', fn), meta: { role: 'admin' } }, { post: data => ch2.port2.postMessage(data), on: fn => ch2.port2.on('message', fn), meta: { role: 'guest' } }, ], { resolver(name, fn) { if (name === 'hello' && this.$meta?.role === 'admin') return (n: string) => `[ADMIN] Hello ${n}!` return fn }, }, ) const admin = createBirpc( { greet: n => n }, { post: data => ch1.port1.postMessage(data), on: fn => ch1.port1.on('message', fn) }, ) const guest = createBirpc( { greet: n => n }, { post: data => ch2.port1.postMessage(data), on: fn => ch2.port1.on('message', fn) }, ) console.log(await admin.hello('Alice')) // "[ADMIN] Hello Alice!" console.log(await guest.hello('Bob')) // "Hello Bob!" ``` ``` -------------------------------- ### `createBirpc` — Create a two-way RPC channel Source: https://context7.com/antfu-collective/birpc/llms.txt Creates a typed RPC instance for point-to-point communication. It wraps local functions and a channel transport, allowing remote functions to be called as async methods. The generics define the types for remote and local functions, ensuring static type checking. ```APIDOC ## `createBirpc` — Create a two-way RPC channel Creates a typed RPC instance that wraps a local functions object and a channel transport. The first generic parameter is the **remote** functions type (what you can call), the second is the **local** functions type (what you expose). Returns a proxified object where remote functions are directly callable as async methods. ```ts import { createBirpc } from 'birpc' // --- Shared types --- interface ServerFunctions { greet(name: string): string add(a: number, b: number): number } interface ClientFunctions { notify(message: string): void } // --- Server side (using WebSocket) --- import { WebSocketServer } from 'ws' const wss = new WebSocketServer({ port: 3000 }) wss.on('connection', (ws) => { const serverFunctions: ServerFunctions = { greet: name => `Hello, ${name}!`, add: (a, b) => a + b, } const rpc = createBirpc( serverFunctions, { post: data => ws.send(data), on: fn => ws.on('message', fn), serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) // Call client-side function rpc.notify('You are connected!') }) // --- Client side --- const ws = new WebSocket('ws://localhost:3000') const clientFunctions: ClientFunctions = { notify(message) { console.log('Server says:', message) }, } const rpc = createBirpc( clientFunctions, { post: data => ws.send(data), on: fn => ws.addEventListener('message', e => fn(e.data)), serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) // Call server-side functions like local async functions console.log(await rpc.greet('Alice')) // "Hello, Alice!" console.log(await rpc.add(3, 4)) // 7 ``` ``` -------------------------------- ### $close Method Source: https://context7.com/antfu-collective/birpc/llms.txt Marks the RPC as closed, rejects all pending promises, and removes the message listener. Accepts an optional custom `Error` that becomes the rejection reason. ```APIDOC ## $close ### Description Marks the RPC as closed, rejects all pending promises, and removes the message listener. Accepts an optional custom `Error` that becomes the rejection reason (with the original birpc error as its `.cause`). ### Parameters - `error` (Error, optional) - A custom error to reject pending calls with. ### Example ```ts // Close with a custom error rpc.$close(new Error('Connection dropped')) ``` ### Properties - `$closed` (boolean) - Indicates if the RPC channel is closed. ``` -------------------------------- ### Attach metadata to RPC instances with `meta` Source: https://context7.com/antfu-collective/birpc/llms.txt Use the `meta` option to attach arbitrary data to an RPC instance, accessible via `$meta`. This is useful for identifying call origins in group or broadcast scenarios. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' const channel = new MessageChannel() const rpc = createBirpc, { info(): string }> ({ info() { // `this` is the rpc instance when bind = 'rpc' (default) return `I am ${(this as any).$meta?.name ?? 'unknown'}` }, }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), meta: { name: 'server-channel-1' }, }, ) console.log(rpc.$meta) // { name: 'server-channel-1' } ``` -------------------------------- ### One-to-many broadcast RPC with `createBirpcGroup` Source: https://context7.com/antfu-collective/birpc/llms.txt Manage multiple RPC channels and broadcast calls to all connected clients using `createBirpcGroup`. Responses are collected into an array. Channels can be added dynamically. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc, createBirpcGroup } from 'birpc' interface ServerFunctions { hello(name: string): string } interface ClientFunctions { hi(name: string): string } const ch1 = new MessageChannel() const ch2 = new MessageChannel() const ch3 = new MessageChannel() // Three independent clients const client1 = createBirpc( { hi: name => `Hi ${name}, I am Client1` }, { post: data => ch1.port1.postMessage(data), on: fn => ch1.port1.on('message', fn) }, ) const client2 = createBirpc( { hi: name => `Hi ${name}, I am Client2` }, { post: data => ch2.port1.postMessage(data), on: fn => ch2.port1.on('message', fn) }, ) // Server group managing two channels const server = createBirpcGroup( { hello: name => `Hello ${name}!` }, [ { post: data => ch1.port2.postMessage(data), on: fn => ch1.port2.on('message', fn) }, { post: data => ch2.port2.postMessage(data), on: fn => ch2.port2.on('message', fn) }, ], { eventNames: ['notify'] }, ) // Broadcast to all clients — returns array of responses const results = await server.broadcast.hi('Server') console.log(results) // ["Hi Server, I am Client1", "Hi Server, I am Client2"] // Add a new channel dynamically server.updateChannels((channels) => { channels.push({ post: data => ch3.port2.postMessage(data), on: fn => ch3.port2.on('message', fn), }) }) // Iterate over individual client RPC instances for (const client of server.clients) { console.log(client.$meta?.name) } ``` -------------------------------- ### Create Two-way RPC Channel with WebSocket Source: https://context7.com/antfu-collective/birpc/llms.txt Use `createBirpc` to set up a typed RPC channel over WebSockets. Define shared types for remote and local functions. Ensure proper serialization and deserialization for message transport. ```typescript import { createBirpc } from 'birpc' // --- Shared types --- interface ServerFunctions { greet(name: string): string add(a: number, b: number): number } interface ClientFunctions { notify(message: string): void } // --- Server side (using WebSocket) --- import { WebSocketServer } from 'ws' const wss = new WebSocketServer({ port: 3000 }) wss.on('connection', (ws) => { const serverFunctions: ServerFunctions = { greet: name => `Hello, ${name}!`, add: (a, b) => a + b, } const rpc = createBirpc( serverFunctions, { post: data => ws.send(data), on: fn => ws.on('message', fn), serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) // Call client-side function rpc.notify('You are connected!') }) // --- Client side --- const ws = new WebSocket('ws://localhost:3000') const clientFunctions: ClientFunctions = { notify(message) { console.log('Server says:', message) }, } const rpc = createBirpc( clientFunctions, { post: data => ws.send(data), on: fn => ws.addEventListener('message', e => fn(e.data)), serialize: v => JSON.stringify(v), deserialize: v => JSON.parse(v), }, ) // Call server-side functions like local async functions console.log(await rpc.greet('Alice')) // "Hello, Alice!" console.log(await rpc.add(3, 4)) // 7 ``` -------------------------------- ### Create Two-way RPC Channel with MessageChannel Source: https://context7.com/antfu-collective/birpc/llms.txt Utilize `createBirpc` with `MessageChannel` for zero-configuration local transport. This method automatically handles structured data serialization and circular references. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type AliceFunctions = { hello(name: string): string } type BobFunctions = { hi(name: string): string; bump(): void; getCount(): number } const channel = new MessageChannel() const alice = createBirpc( { hello: name => `Hello ${name}, my name is Alice` }, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) const bob = createBirpc( { hi: name => `Hi ${name}, I am Bob`, bump: () => {}, getCount: () => 0, }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), }, ) console.log(await bob.hello('Bob')) // "Hello Bob, my name is Alice" console.log(await alice.hi('Alice')) // "Hi Alice, I am Bob" ``` -------------------------------- ### $functions Property Source: https://context7.com/antfu-collective/birpc/llms.txt Provides a live reference to the local functions object, enabling functions to be added, replaced, or removed at runtime without re-creating the RPC instance. ```APIDOC ## $functions ### Description The `$functions` property is a live reference to the local functions object, allowing functions to be added, replaced, or removed at runtime without re-creating the RPC instance. ### Example ```ts // Swap out the implementation at runtime rpc.$functions.greet = name => `Hello ${name} (v2)` ``` ``` -------------------------------- ### `createBirpcGroup` — One-to-many broadcast RPC Source: https://context7.com/antfu-collective/birpc/llms.txt `createBirpcGroup` facilitates the management of dynamic RPC channels and provides a `broadcast` proxy. This proxy distributes calls to all connected clients and aggregates their responses into an array. ```APIDOC ## `createBirpcGroup` — One-to-many broadcast RPC `createBirpcGroup` manages a dynamic set of RPC channels and exposes a `broadcast` proxy that fans calls out to all connected clients and collects their responses into an array. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc, createBirpcGroup } from 'birpc' interface ServerFunctions { hello(name: string): string } interface ClientFunctions { hi(name: string): string } const ch1 = new MessageChannel() const ch2 = new MessageChannel() const ch3 = new MessageChannel() // Three independent clients const client1 = createBirpc( { hi: name => `Hi ${name}, I am Client1` }, { post: data => ch1.port1.postMessage(data), on: fn => ch1.port1.on('message', fn) }, ) const client2 = createBirpc( { hi: name => `Hi ${name}, I am Client2` }, { post: data => ch2.port1.postMessage(data), on: fn => ch2.port1.on('message', fn) }, ) // Server group managing two channels const server = createBirpcGroup( { hello: name => `Hello ${name}!` }, [ { post: data => ch1.port2.postMessage(data), on: fn => ch1.port2.on('message', fn) }, { post: data => ch2.port2.postMessage(data), on: fn => ch2.port2.on('message', fn) }, ], { eventNames: ['notify'] }, ) // Broadcast to all clients — returns array of responses const results = await server.broadcast.hi('Server') console.log(results) // ["Hi Server, I am Client1", "Hi Server, I am Client2"] // Add a new channel dynamically server.updateChannels((channels) => { channels.push({ post: data => ch3.port2.postMessage(data), on: fn => ch3.port2.on('message', fn), }) }) // Iterate over individual client RPC instances for (const client of server.clients) { console.log(client.$meta?.name) } ``` ``` -------------------------------- ### $call / $callEvent / $callOptional - Explicit Method Invocation Source: https://context7.com/antfu-collective/birpc/llms.txt Built-in dollar-prefixed methods allow calling remote functions by string name, bypassing the proxy. `$callOptional` silently returns `undefined` if the remote side does not expose the function (instead of throwing). ```APIDOC ## `$call` / `$callEvent` / `$callOptional` — Explicit method invocation Built-in dollar-prefixed methods allow calling remote functions by string name, bypassing the proxy. `$callOptional` silently returns `undefined` if the remote side does not expose the function (instead of throwing). ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { greet(name: string): string; optFeature?(): string } const channel = new MessageChannel() createBirpc, Remote>( { greet: (name: string) => `Hi ${name}` }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn) }, ) const rpc = createBirpc( {}, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn) }, ) // Equivalent to rpc.greet('World') console.log(await rpc.$call('greet', 'World')) // "Hi World" // Fire-and-forget await rpc.$callEvent('greet', 'nobody') // returns undefined, no wait // Safe call — returns undefined if remote doesn't have the function const result = await rpc.$callOptional('optFeature') // undefined (no error) // rpc.$callRaw for advanced use with full options object const raw = await rpc.$callRaw({ method: 'greet', args: ['Raw'], event: false }) console.log(raw) // "Hi Raw" ``` ``` -------------------------------- ### Optional Broadcast Calls Source: https://context7.com/antfu-collective/birpc/llms.txt Use `$callOptional` for broadcasting to a group where some clients might not implement a function. It returns `undefined` for missing implementations instead of throwing an error. ```APIDOC ## `broadcast.$callOptional` — Optional broadcast calls When broadcasting to a group where some clients may not implement a function, use `$callOptional` to receive `undefined` for missing implementations instead of throwing. ```ts import { createBirpcGroup } from 'birpc' interface ClientFunctions { feature?(): string } interface ServerFunctions { ping(): string } // ... channel setup omitted for brevity const server = createBirpcGroup( { ping: () => 'pong' }, [ /* channel1, channel2, channel3 */ ] ) // client3 does not implement `feature` — would throw without $callOptional const results = await server.broadcast.$callOptional('feature') console.log(results) // ["result from client1", "result from client2", undefined] ``` ``` -------------------------------- ### $rejectPendingCalls Method Source: https://context7.com/antfu-collective/birpc/llms.txt Rejects all currently pending calls without closing the channel, allowing new calls to be made. Optionally accepts a handler function to customize the rejection per call. ```APIDOC ## $rejectPendingCalls ### Description Rejects all currently pending calls without closing the channel, so new calls can still be made. Optionally accepts a handler function to customize the rejection per call. ### Parameters - `handler` (function, optional) - A function that receives `method` and `reject` to customize rejection. - `method` (string) - The name of the pending method. - `reject` (function) - Function to reject the specific call. ### Example ```ts rpc.$rejectPendingCalls(({ method, reject }) => { reject(new Error(`Manually cancelled: ${method}`)) }) ``` ``` -------------------------------- ### RPC with Circular References Source: https://github.com/antfu-collective/birpc/blob/main/README.md Utilize `structured-clone-es` for serialization when your data may contain circular references, as standard JSON does not support them. ```typescript import { parse, stringify } from 'structured-clone-es' const rpc = createBirpc( functions, { post: data => ws.send(data), on: fn => ws.on('message', fn), // use structured-clone-es as serializer serialize: v => stringify(v), deserialize: v => parse(v), }, ) ``` -------------------------------- ### `proxify: false` — Disable the Proxy wrapper Source: https://context7.com/antfu-collective/birpc/llms.txt When `proxify` is `false`, the RPC instance exposes only the built-in `$call*` methods and no proxy is created. This is useful in environments where `Proxy` is unavailable or explicit method names are preferred. ```APIDOC ## `proxify: false` — Disable the Proxy wrapper When `proxify` is `false`, the RPC instance exposes only the built-in `$call*` methods and no proxy is created. This is useful in environments where `Proxy` is unavailable or explicit method names are preferred. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { add(a: number, b: number): number } type Local = Record const channel = new MessageChannel() createBirpc( { add: (a: number, b: number) => a + b } as any, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn) }, ) // Proxify disabled — type parameter Proxify = false const rpc = createBirpc( {}, { proxify: false, post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) // Must use $call — rpc.add() would throw "not a function" console.log(await rpc.$call('add', 10, 32)) // 42 ``` ``` -------------------------------- ### Fire-and-Forget Events with `eventNames` Source: https://context7.com/antfu-collective/birpc/llms.txt Use the `eventNames` option in `createBirpc` to designate functions as one-way events. These calls return `undefined` immediately without waiting for a response, suitable for notifications. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { log(msg: string): void; ping(): string } type Local = Record const channel = new MessageChannel() // Receiver createBirpc( {}, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), }, ) // Sender — `log` is fire-and-forget const sender = createBirpc( {}, { eventNames: ['log'], // <-- treated as one-way event post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) await sender.log('hello') // returns undefined immediately, no round-trip // Any function can also be called as an event ad-hoc via `.asEvent` await sender.ping.asEvent() // fire-and-forget even though `ping` is normally awaited ``` -------------------------------- ### onRequest Middleware Source: https://context7.com/antfu-collective/birpc/llms.txt Intercepts incoming calls to implement middleware logic like caching. It can either proceed with the call using `next()` or short-circuit with a response using `resolve(value)`. ```APIDOC ## onRequest ### Description Intercepts every incoming call before it is dispatched. Use `next()` to proceed normally or call `resolve(value)` to short-circuit with a cached or synthetic response. ### Parameters - `req` (object) - The incoming request object, containing method and arguments. - `next` (function) - Call this function to proceed with the normal RPC dispatch. - `resolve` (function) - Call this function with a value to provide a response without dispatching. ### Example ```ts async onRequest(req, next, resolve) { const key = `${req.m}:${JSON.stringify(req.a)}` if (cache.has(key)) { console.log('Cache hit for', key) resolve(cache.get(key)) } else { const result = await next() cache.set(key, result) } } ``` ``` -------------------------------- ### `timeout` / `onTimeoutError` — Controlling call timeouts Source: https://context7.com/antfu-collective/birpc/llms.txt The default timeout is 60 seconds. Override it per instance with `timeout` (milliseconds). Set to `-1` to disable. Use `onTimeoutError` to customize the thrown error or suppress it. ```APIDOC ## `timeout` / `onTimeoutError` — Controlling call timeouts The default timeout is 60 seconds. Override it per instance with `timeout` (milliseconds). Set to `-1` to disable. Use `onTimeoutError` to customize the thrown error or suppress it. ```ts import { createBirpc } from 'birpc' const rpc = createBirpc<{ slowOp(): string }>( {}, { timeout: 5000, // 5-second timeout post: () => {}, // transport that never delivers (for demo) on: () => {}, onTimeoutError(functionName, args) { console.warn(`Timed out: ${functionName}(${args.join(', ')})`) throw new Error(`Request to "${functionName}" timed out after 5s`) }, }, ) try { await rpc.slowOp() } catch (err) { console.error(err.message) // "Request to "slowOp" timed out after 5s" } ``` ``` -------------------------------- ### Optional Broadcast Calls with $callOptional Source: https://context7.com/antfu-collective/birpc/llms.txt Use `$callOptional` when broadcasting to a group where some clients may not implement a function. This prevents errors and returns `undefined` for missing implementations instead of throwing. ```typescript import { createBirpcGroup } from 'birpc' interface ClientFunctions { feature?(): string } interface ServerFunctions { ping(): string } // ... channel setup omitted for brevity const server = createBirpcGroup( { ping: () => 'pong' }, [ /* channel1, channel2, channel3 */ ], ) // client3 does not implement `feature` — would throw without $callOptional const results = await server.broadcast.$callOptional('feature') console.log(results) // ["result from client1", "result from client2", undefined] // Broadcast fire-and-forget to all clients await server.broadcast.$callEvent('feature') ``` -------------------------------- ### Implement Request Caching with onRequest Middleware Source: https://context7.com/antfu-collective/birpc/llms.txt Use the `onRequest` middleware to intercept incoming calls, cache responses, and short-circuit future requests with cached data. This avoids unnecessary remote calls for repeated requests. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { fetchData(id: string): string } const channel = new MessageChannel() const cache = new Map() const rpc = createBirpc>( {}, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), async onRequest(req, next, resolve) { const key = `${req.m}:${JSON.stringify(req.a)}` if (cache.has(key)) { console.log('Cache hit for', key) resolve(cache.get(key)) } else { const result = await next() cache.set(key, result) } }, }, ) await rpc.fetchData('user-42') // fetches remotely, caches result await rpc.fetchData('user-42') // Cache hit — no remote round-trip ``` -------------------------------- ### Selectively Reject Pending Calls with $rejectPendingCalls Source: https://context7.com/antfu-collective/birpc/llms.txt Employ `$rejectPendingCalls` to reject all active calls without closing the RPC channel, allowing new calls to be initiated. A handler can be provided to customize rejection logic per call. ```typescript import { createBirpc } from 'birpc' const rpc = createBirpc<{ op(): void }> ({}, { on: () => {}, post: () => {} }, ) rpc.op().catch(err => console.error(err.message)) // Reject all pending calls with a custom message per method rpc.$rejectPendingCalls(({ method, reject }) => { reject(new Error(`Manually cancelled: ${method}`)) }) // "Manually cancelled: op" // rpc is still open — new calls can be made console.log(rpc.$closed) // false ``` -------------------------------- ### `eventNames` — Fire-and-forget events (no response) Source: https://context7.com/antfu-collective/birpc/llms.txt Allows specifying functions that should be treated as one-way events. Calls to these functions return `undefined` immediately without waiting for a response, suitable for notifications. ```APIDOC ## `eventNames` — Fire-and-forget events (no response) Functions listed in `eventNames` are sent as one-way events. The caller receives `undefined` immediately without waiting for a reply, making them ideal for notifications and state updates. ```ts import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { log(msg: string): void; ping(): string } type Local = Record const channel = new MessageChannel() // Receiver createBirpc( {}, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), }, ) // Sender — `log` is fire-and-forget const sender = createBirpc( {}, { eventNames: ['log'], // <-- treated as one-way event post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) await sender.log('hello') // returns undefined immediately, no round-trip // Any function can also be called as an event ad-hoc via `.asEvent` await sender.ping.asEvent() // fire-and-forget even though `ping` is normally awaited ``` ``` -------------------------------- ### Close RPC Channel with $close Source: https://context7.com/antfu-collective/birpc/llms.txt Use `$close()` to terminate the RPC channel, reject all pending promises, and remove listeners. It accepts an optional custom error to specify the rejection reason. ```typescript import { createBirpc } from 'birpc' const rpc = createBirpc<{ ping(): string }> ({}, { on: () => {}, post: () => {} }, ) const pending = rpc.ping().catch(err => console.error('Rejected:', err.message)) // Close with a custom error rpc.$close(new Error('Connection dropped')) await pending // Rejected: Connection dropped console.log(rpc.$closed) // true // Any further calls throw immediately rpc.ping().catch(err => console.error(err.message)) // '[birpc] rpc is closed, cannot call "ping"' ``` -------------------------------- ### resolver Option Source: https://context7.com/antfu-collective/birpc/llms.txt Allows intercepting and overriding which function is invoked for an incoming RPC call. Useful for middleware, permission checks, or per-connection function overrides. ```APIDOC ## resolver ### Description Allows intercepting and overriding which function is invoked for an incoming RPC call. This is useful for middleware, permission checks, or per-connection function overrides. ### Parameters - `name` (string) - The name of the RPC method being called. - `originalFn` (function) - The original function that would have been invoked. ### Returns - (function) - The function to be invoked for the RPC call. ### Example ```ts resolver(name, originalFn) { if (name === 'foo') { // Wrap the original function with additional logic return async (n: string) => `[wrapped] ${await originalFn(n)}` } return originalFn } ``` ``` -------------------------------- ### Explicitly Call Remote Functions with $call, $callEvent, $callOptional Source: https://context7.com/antfu-collective/birpc/llms.txt Use $call, $callEvent, and $callOptional for direct remote function invocation by name. $callOptional safely returns undefined if the function is not found on the remote side. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { greet(name: string): string; optFeature?(): string } const channel = new MessageChannel() createBirpc, Remote>( { greet: (name: string) => `Hi ${name}` }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn) }, ) const rpc = createBirpc( {}, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn) }, ) // Equivalent to rpc.greet('World') console.log(await rpc.$call('greet', 'World')) // "Hi World" // Fire-and-forget await rpc.$callEvent('greet', 'nobody') // returns undefined, no wait // Safe call — returns undefined if remote doesn't have the function const result = await rpc.$callOptional('optFeature') // undefined (no error) // rpc.$callRaw for advanced use with full options object const raw = await rpc.$callRaw({ method: 'greet', args: ['Raw'], event: false }) console.log(raw) // "Hi Raw" ``` -------------------------------- ### Disable Proxy Wrapper with proxify: false Source: https://context7.com/antfu-collective/birpc/llms.txt Set `proxify: false` to disable the proxy, exposing only built-in $call* methods. This is useful in environments lacking Proxy support or when explicit method names are preferred. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { add(a: number, b: number): number } type Local = Record const channel = new MessageChannel() createBirpc( { add: (a: number, b: number) => a + b } as any, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn) }, ) // Proxify disabled — type parameter Proxify = false const rpc = createBirpc( {}, { proxify: false, post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) // Must use $call — rpc.add() would throw "not a function" console.log(await rpc.$call('add', 10, 32)) // 42 ``` -------------------------------- ### Fire-and-Forget Broadcast Event Source: https://context7.com/antfu-collective/birpc/llms.txt Broadcast a fire-and-forget event to all clients without expecting a return value. ```APIDOC ## `broadcast.$callEvent` — Fire-and-forget broadcast Broadcast fire-and-forget to all clients. ```ts // Broadcast fire-and-forget to all clients await server.broadcast.$callEvent('feature') ``` ``` -------------------------------- ### Dynamically Resolve Functions with resolver Middleware Source: https://context7.com/antfu-collective/birpc/llms.txt Utilize the `resolver` option to intercept and override function invocations. This is useful for implementing middleware, permission checks, or per-connection function overrides by wrapping or replacing the original function. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' const channel = new MessageChannel() const rpc = createBirpc, { foo(name: string): string }> ({ foo: name => `Default foo for ${name}` }, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), resolver(name, originalFn) { if (name === 'foo') { // Wrap the original function with additional logic return async (n: string) => `[wrapped] ${await originalFn(n)}` } return originalFn }, }, ) ``` -------------------------------- ### Control Call Timeouts with timeout and onTimeoutError Source: https://context7.com/antfu-collective/birpc/llms.txt Configure call timeouts using the `timeout` option (milliseconds, -1 to disable). Customize timeout errors or suppress them using the `onTimeoutError` hook. ```typescript import { createBirpc } from 'birpc' const rpc = createBirpc<{ slowOp(): string }> {}, { timeout: 5000, // 5-second timeout post: () => {}, // transport that never delivers (for demo) on: () => {}, onTimeoutError(functionName, args) { console.warn(`Timed out: ${functionName}(${args.join(', ')})`) throw new Error(`Request to "${functionName}" timed out after 5s`) }, }, ) try { await rpc.slowOp() } catch (err) { console.error(err.message) // "Request to "slowOp" timed out after 5s" } ``` -------------------------------- ### Replace Functions Dynamically with $functions Source: https://context7.com/antfu-collective/birpc/llms.txt Modify the `$functions` property to add, replace, or remove local functions at runtime without reinitializing the RPC instance. This allows for live updates to the available remote methods. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' type Remote = { greet(name: string): string } const channel = new MessageChannel() const rpc = createBirpc ({ greet: name => `Hello ${name} (v1)` }, { post: data => channel.port2.postMessage(data), on: fn => channel.port2.on('message', fn), }, ) // Swap out the implementation at runtime rpc.$functions.greet = name => `Hello ${name} (v2)` // New calls on the remote side will now use the updated function ``` -------------------------------- ### Handle Errors with onFunctionError and onGeneralError Source: https://context7.com/antfu-collective/birpc/llms.txt Intercept local handler errors with `onFunctionError` and general RPC errors (serialization, transport) with `onGeneralError`. Returning `true` from these hooks can suppress re-throwing. ```typescript import { MessageChannel } from 'node:worker_threads' import { createBirpc } from 'birpc' const channel = new MessageChannel() const server = createBirpc, { riskyOp(): string }> { riskyOp() { throw new Error('Something went wrong internally') }, }, { post: data => channel.port1.postMessage(data), on: fn => channel.port1.on('message', fn), onFunctionError(error, functionName, args) { console.error(`[${functionName}] failed with args`, args, ':', error.message) // return true to silently suppress sending the error to the caller }, onGeneralError(error, functionName) { console.error(`General RPC error in "${functionName}":`, error.message) return true // suppress re-throw }, } ) ```