web-rpc 0.0.6

Bi-directional RPC for the Web
Documentation

CI Crates.io api-docs MIT licensed

web-rpc

Bidirectional RPC for browsing contexts, web workers, and message channels. Inspired by Google's tarpc: define a service as a trait, annotate, and the macro generates the client and server. JS-bearing types (anything AsRef<JsValue>) are auto-routed through postMessage; everything else through bincode. Put the trait definition in a shared crate so both ends share it.

#[web_rpc::service]
pub trait Calculator {
    fn add(&self, left: u32, right: u32) -> u32;
}

The macro generates CalculatorClient, CalculatorService, and a Calculator trait you implement on the server side:

struct CalculatorImpl;
impl Calculator for CalculatorImpl {
    fn add(&self, left: u32, right: u32) -> u32 { left + right }
}

Wire up over a MessageChannel (or a Worker / MessagePort):

let channel = web_sys::MessageChannel::new().unwrap();
let (server_iface, client_iface) = futures_util::future::join(
    web_rpc::Interface::new(channel.port1()),
    web_rpc::Interface::new(channel.port2()),
).await;

let server = web_rpc::Builder::new(server_iface)
    .with_service::<CalculatorService<_>>(CalculatorImpl)
    .build();
wasm_bindgen_futures::spawn_local(server);

let client = web_rpc::Builder::new(client_iface)
    .with_client::<CalculatorClient>()
    .build();

assert_eq!(client.add(41, 1).await, 42);

Features

  • Auto-routing of JS vs serializable types: types implementing AsRef<JsValue> (e.g. JsString, OffscreenCanvas) are posted; everything else is serialized using bincode.
  • Option/Result wrappers (and nested): each variant routes independently, so types like Result<JsT, RustError> and Result<Option<JsT>, _> just work.
  • Bidirectional RPC over a single channel, with both ends simultaneously acting as client and server.
  • Streaming RPCs via impl Stream<Item = T> returns, with abort-on-drop and close-and-drain.
  • Transfer semantics via #[transfer(...)]: bare params, derived expressions (data => data.buffer()), refutable closures (return => |Ok(buf)| buf.buffer()), or match-blocks.
  • Async or sync server methods, with per-request cancellation when the client drops the future of an RPC method that returns.
  • Notifications: methods with no return type are fire-and-forget.
  • Borrowed &str / &[u8]: zero-copy deserialization on the server side.

See the crate documentation for the full feature reference. Need help with your latest project? Get in touch via contact@allwright.io. I'm available for new assignments.