# bevy-wasm-api **Repository Path**: happydpc/bevy-wasm-api ## Basic Information - **Project Name**: bevy-wasm-api - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-03-13 - **Last Updated**: 2025-08-07 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README
Image of typed wasm api returning an optional tuple asyncronously.

bevy-wasm-api

Opinionated plugin and proc macro for bevy to easily build typed APIs when running in a wasm instance.
View Demo · Report Bug · Request Feature

## Getting Started ### Installation Install the `bevy-wasm-api` crate. ```bash cargo add --git https://github.com/sanspointes/bevy-wasm-api cargo add wasm-bindgen ``` Install optional dependencies to help your development ```bash # Required if you want to return custom structs from your api cargo add serde --features derive # Helpful crate for generating typescript types for custom structs cargo add tsify --features js --no-default-features ``` ### Add Plugin to app ```rust use bevy_wasm_api::BevyWasmApiPlugin; #[wasm_bindgen] pub fn setup_app(canvas_selector: String) { let mut app = App::new(); app.add_plugins(BevyWasmApiPlugin).run(); } ``` ### Define an Api :warning: The first argument must be a `world: &mut World`. ```rust #[wasm_bindgen(skip_typescript)] // Let bevy-wasm-api generate the types struct MyApi; #[bevy_wasm_api] impl MyApi { pub fn spawn_entity(world: &mut World, x: f32, y: f32, z: f32) -> Entity { world.spawn( TransformBundle { transform: Transform { translation: Vec3::new(x, y, z), ..Default::default(), }, ..Default::default(), } ).id() } pub fn set_entity_position(world: &mut World, entity: u32, x: f32, y: f32, z: f32) -> Result<(), String> { let entity = Entity::from_raw(entity); let mut transform = world.get_mut::(entity).ok_or("Could not find entity".to_string())?; transform.translation.x = x; transform.translation.y = y; transform.translation.z = z; Ok(()) } pub fn get_entity_position(world: &mut World, entity: u32) -> Option<(f32, f32, f32)> { let transform = world.get::(Entity::from_raw(entity)); transform.map(|transform| { let pos = transform.translation; (pos.x, pos.y, pos.z) }) } } ``` ### Use your api in typescript ```typescript import { setup_app, MyApi } from 'bevy-app'; async function start() { try { setup_app('#canvas-element'); } catch (error) { // Ignore, Bevy apps for wasm error for control flow. } const api = new MyApi(); const id = await api.spawn_entity(0, 0, 0); await api.set_entity_position(id, 10, 0, 0); const pos = await api.get_entity_position(id) console.log(pos) // [10, 0, 0] const otherPos = await api.get_entity_position(1000) // (Made up entity) console.log(pos) // undefined } ```

(back to top)

## How it works The crate uses a similar approach to the [deferred promise](https://dev.to/webduvet/deferred-promise-pattern-2j59) by parking the function that we want to execute (See `Task` in [`sync.rs`](./src/sync.rs)), executing all the parked tasks, and then converts the result back to a JsValue. The real complexity is in the effort to support typed returns in typescript which is handled in the [bevy-wasm-api-macro-core`](./bevy-wasm-api-macro-core/src/analyze/) crate. Given the following input ```rust #[bevy_wasm_api] impl MyApi { pub fn my_function(world: &mut World, x:f32, y: f32) -> bool { // Do anything with your &mut World true } } ``` The output will look something like this. ```rust // Exposes `MyApiWasmApi` as `MyApi` in javascript #[wasm_bindgen(js_class = "MyApi")] impl MyApiWasmApi { // Skips wasm_bindgen typescript types so we can generate better typescript types. #[wasm_bindgen(skip_typescript)] pub fn my_function(x: f32, y: f32) -> js_sys::Promise { // Uses execute_in_world to get a `world: &mut World`, converts the future to a Js Promise wasm_bindgen_futures::future_to_promise(bevy_wasm_api::execute_in_world(bevy_wasm_api::ExecutionChannel::FrameStart, |world| { // Calls the original method let ret_val = MyApi::my_function(world, x, y); // Return the original return type as a JsValue // The real code that's generated here is actually dependent on the return type but I'll keep it simple in this example. Ok(JsValue::from(ret_val)) })) } } ```

(back to top)

## Examples ### `vite-app` This is your "kitchen sink" example showcasing a lot of the features of the crate. This is how I am personally using the package to develop my app (a CAD/design program). ### `wasm-app` This shows how to use the crate purely from the bevy side. Showcasing the changes you'd make / dependencies you'd need in bevy.

(back to top)

## Features Here's an outline of the currently supported feature set + features that I'd like to implement. - [ ] Type inference / handling of return types - [x] Infers any number (`i32`, ...) as typescript `number` type - [x] Infers `bool` as typescript `bool` type - [x] Correctly handles custom struct returns (must implement From/IntoWasmAbi) (use [tsify](https://github.com/madonoharu/tsify) to generate typescript types). - [x] Infers `&str`/`String` as typescript `string` - [x] Infers `Result` as typescript `Promise` - [ ] Use a Result polyfill so the final return type is `Result>` - [x] Infers `Vec` as typescript typescript `Array` type - [ ] Infers an `Iter` as typescript `Array`? - [x] Infers `Option` as typescript `T | undefined` type - [x] Infers tuples (i.e. `(f32, String)`) as typescript `[number, String]` type - [ ] Infers `&[i32]`, and other number arrays as typescript `Int32Array` - [ ] Infers `i32[]`, and other number arrays as typescript `Int32Array` - [ ] Handle `Future` as typescript `Promise`? - [ ] Type inference / handling of argument types - [x] Input parameters handled entirely by `wasm_bindgen`. [tsify](https://github.com/madonoharu/tsify) is good for making this more ergonomic. - [ ] Implement custom handling supporting the same typed parameters as return types (above) - [ ] Targets: - [x] Exposes an api in JS that communicates directly with the bevy wasm app. (For use in browser contexts) - [ ] Exposes an api in JS that communicates with a desktop app over a HTTP endpoint + RPC layer. (For use in desktop contexts with ui in [bevy_wry_webview](https://github.com/hytopiagg/bevy_wry_webview)) - [ ] Support systems as the Api handler. Make use of [`In`](https://docs.rs/bevy/latest/bevy/ecs/system/struct.In.html) and [`Out`](https://docs.rs/bevy/latest/bevy/ecs/prelude/trait.System.html#associatedtype.Out) for args / return value. - [ ] Support multiple bevy apps - [ ] Less restrictive dependency versions - [ ] Adding proc macro attributes to declare when in the frame lifecycle we want to execute the api method.

(back to top)

## Contributing This crate is an ends to a means for developing an app so I am not sure what level of support I will be able to provide and I might not be able to support a lot of additional features. That being said, if you run into bugs or have ideas for improvements/features feel free to create an issue or, even better, submit a PR. > :warning: If the PR is fairly large and complex it could be worth submitting an issue introducing the desired > changes + the usecase so I can verify if it's something that belongs in this crate.

(back to top)

## Help me out? This is also my first proc_macro and I am not that experience with the "bevy" way of doing things so if you know have some technical ideas on how this crate can be improved (improve modularity/adaptability, performance, simplify code) I would be very grateful to hear it in an issue. Some things I'd love feedback on is: - Making the dependency versions lest restrictive. - Adding proc macro attributes on each function to declare when the ApiMethod should run. - Making better use of bevy paradigms - Making better use of wasm_bindgen type inference (currently duplicating logic converting `str` (rust) -> `string` (typescript)) - All of this is only tested with my depenencies, anything that makes it more versatile (I might be a bit too dumb to make it fully generic) - Generalising the type inference improvements into its own crate (could be useful outside of the bevy ecosystem) ## Compatibility | bevy-wasm-api version | Bevy version | |-----------------------|--------------| | 0.2 | 0.14 | | 0.1 | 0.13 |