Skip to content

Add wasm32/web browser support to the Rust SDK#4319

Open
drdozer wants to merge 20 commits intoclockworklabs:masterfrom
drdozer:rust-web-sdk-rebase
Open

Add wasm32/web browser support to the Rust SDK#4319
drdozer wants to merge 20 commits intoclockworklabs:masterfrom
drdozer:rust-web-sdk-rebase

Conversation

@drdozer
Copy link

@drdozer drdozer commented Feb 17, 2026

Overview

This PR adds web browser (wasm32) support to the SpacetimeDB Rust SDK behind an opt-in web Cargo feature flag. When enabled, the SDK can be compiled to WebAssembly and run in a browser environment, using browser-native APIs for WebSockets, storage, and async task spawning — all while keeping the existing native (tokio-based) code path completely unchanged when the feature is not enabled.

This is a rebased and updated continuation of #2704 by @thlsrms, with further work by @kistz on their rust-web-sdk-updated branch.

Motivation

The Rust SDK currently assumes a native runtime (tokio, file I/O, threads). This prevents Rust-to-WASM web applications (e.g. via wasm-bindgen / wasm-pack) from using the SDK. This PR bridges that gap, allowing the same spacetimedb-sdk crate to target both native and web platforms.

Changes

New web feature flag (sdks/rust/Cargo.toml)

  • Introduces the web feature that gates all browser-specific dependencies
  • Web deps: gloo-console, gloo-net, gloo-storage, gloo-utils, js-sys, web-sys, wasm-bindgen, wasm-bindgen-futures, tokio-tungstenite-wasm, getrandom (with wasm_js)
  • Moves home, tokio, and tokio-tungstenite under [target.'cfg(not(target_arch = "wasm32"))'.dependencies]

Credentials (sdks/rust/src/credentials.rs)

  • Native: Unchanged file-based credential storage using the home crate
  • Web: Provides a cookie API (credentials::cookies::Cookie) with a builder pattern for get/set/delete, plus re-exports of gloo_storage::{LocalStorage, SessionStorage, Storage} for web-standard storage

WebSocket connection (sdks/rust/src/websocket.rs)

  • Native: Unchanged tokio-tungstenite connection with HTTP upgrade headers
  • Web: Uses tokio-tungstenite-wasm for the browser WebSocket API
  • Adds fetch_ws_token() — an HTTP POST to /v1/identity/websocket-token to exchange a bearer token for a short-lived WebSocket token (since browsers cannot set custom headers on WebSocket handshakes)
  • Adds a wasm32 spawn_message_loop using wasm_bindgen_futures::spawn_local with futures::select! for concurrent inbound/outbound message handling

DbConnection (sdks/rust/src/db_connection.rs)

  • Conditional type alias SharedAsyncCell<T>TokioMutex on native, StdMutex on web
  • Lock helpers get_lock_sync / get_lock_async abstract over the two mutex types
  • DbConnectionBuilder::build() becomes async on web (native remains synchronous)
  • Extracts build_db_ctx_inner() and build_db_ctx() helper functions to reduce duplication between native and web build_impl
  • run_threaded / advance_one_message_blocking are #[cfg(not(feature = "web"))]
  • New run_background_task method on web: spawns a wasm_bindgen_futures::spawn_local loop calling advance_one_message_async
  • Uses futures::select! instead of tokio::select! on the web path

Codegen (crates/codegen/src/rust.rs)

  • Generated DbConnection type conditionally exposes run_threaded + advance_one_message_blocking (native) or run_background_task (wasm32)
  • Conditional doc comments so rustdoc only shows platform-relevant methods

SQL parser fix (crates/sql-parser/src/parser/mod.rs)

  • Gates size_of compile-time assertions to #[cfg(target_pointer_width = "64")], since struct sizes differ on wasm32 due to pointer width

Design Decisions

  • Feature flag, not target-only cfg: The web feature must be explicitly opted into, rather than activating automatically on target_arch = "wasm32". This keeps the default build unaffected and avoids issues in non-browser wasm contexts.
  • No tokio on web: The web path removes the tokio dependency entirely, using wasm-bindgen-futures and futures::select! instead.
  • Token verification for WebSocket auth: Browsers cannot send custom headers during WebSocket handshake, so on web the SDK first fetches a short-lived token via REST and passes it as a query parameter.

Testing

  • Native build: no changes to existing behavior or tests
  • wasm32 build: verified with cargo build --target wasm32-unknown-unknown --features web -p spacetimedb-sdk

Credits

Built on earlier exploration by @kistz and @thlsrms (see #2704). Developed with Zed Coding Agent (Claude Opus 4.6).

thlsrms and others added 20 commits February 17, 2026 13:05
- `DbConnectionBuilder::build` becomes async without tokio's
block_in_place.

Still need to add `web` feature flag.
Renamed the `run_threaded` method on `wasm32` to better reflect its
behavior of spawning a background task.

The generated `DbConnection` methods `run_threaded`, `run_background`,
and `advance_one_message_blocking` now include runtime panics with a
clear error feedback when called on unsupported targets.
Trim down repetitive `cfg` clauses by extracting common lock patterns
into `get_lock_[sync|async]`.
Moves the creation of DbContextImplInner and DbContextImpl into private
helper functions (`build_db_ctx_inner` and `build_db_ctx`) to reduce
duplication between the web and non-web implementations of `build_impl`.
- Update websocket API references: ServerMessage<BsatnFormat> -> ws::v2::ServerMessage,
  ClientMessage<Bytes> -> ws::v2::ClientMessage, BIN_PROTOCOL -> ws::v2::BIN_PROTOCOL
- Rename module_name -> database_name in wasm32 build_impl
- Remove call_reducer_flags field (removed upstream)
- Remove unused HashMap import
- Gate sql-parser size assertions to 64-bit targets (struct sizes
  differ on wasm32 due to pointer width)
- Update codegen snapshot
@CLAassistant
Copy link

CLAassistant commented Feb 17, 2026

CLA assistant check
All committers have signed the CLA.

@drdozer
Copy link
Author

drdozer commented Feb 17, 2026

In the interests of full transparency, I told the Zed coding agent that I wanted the outstanding wasm32 PR fixed. I have not yet done a proper review of exactly what it tinkered with, but will do so once we've got an idea of how far it gets through the CI.

@kistz
Copy link
Contributor

kistz commented Feb 17, 2026

@drdozer sry but what is the motivation behind this? is my branch broken? what did you add to this? if you didnt even test it what makes this better than the version i opened?

The pr is currently blocked on ci changes so this one will also not get merged till they get the ci working :)

@drdozer
Copy link
Author

drdozer commented Feb 17, 2026

@drdozer
The pr is currently blocked on ci changes so this one will also not get merged till they get the ci working :)

I had misunderstood then -- the pull didn't seem to be getting any updates and was collecting conflicts, so I had assumed it was functionally abandoned. Who is blocking it getting merged in then? And why/how?

@kistz
Copy link
Contributor

kistz commented Feb 17, 2026

They first wanna get this tested in ci and then merge the pr.

bfops already tried but likely couldnt get it done before 2.0 since they are trying to get it out the door currently.
This means that wasm support will maybe make it into 2.1 or something :>

Id suggest waiting before 2.0 breaking changes are out the door and then rersolving the merge conflicts.
#4183

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

Comments