Skip to content

Add wasm32 support and browser-based client example#19

Merged
iainmcgin merged 4 commits intoanthropics:mainfrom
tannaurus:main
Apr 2, 2026
Merged

Add wasm32 support and browser-based client example#19
iainmcgin merged 4 commits intoanthropics:mainfrom
tannaurus:main

Conversation

@tannaurus
Copy link
Copy Markdown
Contributor

Background

I realized connectrpc effectively already supported WASM but it couldn't compile because of the unnecessary tokio features specified at the workspace level. All other usages of tokio in this workspace set their features to full anyway so it seemed like it could be an oversight. From there I added some tests to ensure the crate will retain WASM support in future iterations.

Summary

  • Slim workspace tokio/tokio-util to version-only so connectrpc can declare minimal features and compile for wasm32-unknown-unknown
  • Add wasm-client example demonstrating a ClientTransport backed by web-sys::fetch, usable in browsers and web workers
  • Add CI job that verifies wasm32 compilation and runs the example's integration tests in headless Firefox

Details

The workspace tokio dependency previously declared all features (net, rt-multi-thread, signal, etc.), which pulled platform-specific code into every member crate. Now the workspace specifies version-only and connectrpc declares just the features it needs (rt, io-util, sync, time).

The wasm-client example reuses the existing eliza server and proto definitions. The eliza server gains a permissive CORS layer so browser clients can reach it. The test.sh script starts the eliza server, runs wasm-pack test --headless --firefox, and verifies the RPC round-trips successfully.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@tannaurus
Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

Copy link
Copy Markdown
Collaborator

@iainmcgin iainmcgin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — really appreciate the careful work, and the analysis of the workspace tokio feature usage. Tightening the published crate's tokio feature footprint is a nice benefit for everyone.

I'm happy to merge this as-is. Before I do, a couple of notes and one ask for the wasm README, plus some thoughts on directions I'd love to see this example grow in (no obligation — pointers for whoever picks it up next).

Small README ask. Could you note in examples/wasm-client/README.md (or in lib.rs's module doc) that the example currently demonstrates unary calls without deadlines, and that timeouts and streaming would each require additional setup? Just so anyone copy-pasting the transport doesn't get a surprise panic the first time they pass CallOptions::with_timeout(...). A two-line note is plenty.

Future work I'd love to see (not blocking).

  • Request timeouts. CallOptions::with_timeout(...) currently goes through tokio::time::timeout_at, which needs a tokio runtime context. On wasm32-unknown-unknown, that means building a current-thread runtime with enable_time() and entering its context before each call (tokio's wasm timer driver uses setTimeout underneath, so this works without a block_on). Demonstrating that pattern in the example would save the next person from rediscovering it. Longer term, threading deadlines down to the transport so it can use AbortController.signal instead would make wasm transports timer-runtime-free, but that's a connectrpc-side change and out of scope here.

  • Server-streaming. Eliza's Introduce RPC is server-streaming, and fetch's response body is a ReadableStream in every browser. The current transport buffers via array_buffer().await into Full<Bytes>; swapping that for a custom http_body::Body impl that pulls chunks from the JS reader (wrapped in SendWrapper) would get progressive delivery, and connectrpc's stream reader will produce one message per envelope as bytes arrive. This feels like the most valuable next step — it's universally browser-supported and matches what gRPC-Web and Connect-Web expose.

  • Client-streaming and bidi — known limitations. Worth being explicit about why these aren't here:

    • Client-streaming via fetch's ReadableStream request body with duplex: 'half' is Chromium-only as of early 2026 (Firefox bug 1561841 still open, no Safari support). The Firefox-based CI test you've added wouldn't exercise it.
      • True bidirectional streaming isn't achievable via fetch; I'm considering submitting an RFC to standardize WebTransport as an option for ConnectRPC, which would allow for bidi in connect-es and a wasm build of connect-rust. That's likely going to be a lot of work to nail down the details, though, so don't hold your breath 😆

I'll approve and merge this now, anything said above can be considered optional feedback for future PRs. Thanks again!

@iainmcgin iainmcgin enabled auto-merge (squash) April 2, 2026 22:08
@iainmcgin iainmcgin merged commit 2971c18 into anthropics:main Apr 2, 2026
11 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Apr 2, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants