← Back to Lessons Lesson 6 of 8
Intermediate api
Fetch API Calls
Introduction
Rust/Wasm can make HTTP requests using the browser's native Fetch API through web-sys. Combined with wasm-bindgen-futures, you get full async/await support — Rust futures are bridged to JavaScript Promises automatically.
Required dependencies
[dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
[dependencies.web-sys]
version = "0.3"
features = [
"Window",
"Request",
"RequestInit",
"RequestMode",
"Response",
"Headers",
]How async works in Wasm
Wasm is single-threaded and runs on the browser's main thread. Async in Wasm works by:
wasm-bindgen-futuresconverts RustFuture↔ JSPromise- When you
.await, control returns to the JS event loop - When the Promise resolves, your Rust code resumes
// This Rust async function...
#[wasm_bindgen]
pub async fn do_work() -> Result<JsValue, JsValue> {
let result = some_async_operation().await?;
Ok(result)
}
// ...becomes this in JavaScript:
// const result = await do_work(); // returns a PromiseGET request
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::JsFuture;
use web_sys::{Request, RequestInit, Response, window};
#[wasm_bindgen]
pub async fn fetch_json(url: &str) -> Result<JsValue, JsValue> {
let mut opts = RequestInit::new();
opts.method("GET");
let request = Request::new_with_str_and_init(url, &opts)?;
let window = window().unwrap();
let resp: Response = JsFuture::from(
window.fetch_with_request(&request)
).await?.dyn_into()?;
let json = JsFuture::from(resp.json()?).await?;
Ok(json)
}POST request with JSON body
use js_sys::JSON;
#[wasm_bindgen]
pub async fn post_json(url: &str, body: JsValue) -> Result<JsValue, JsValue> {
let mut opts = RequestInit::new();
opts.method("POST");
opts.body(Some(&JSON::stringify(&body)?));
let request = Request::new_with_str_and_init(url, &opts)?;
request.headers().set("Content-Type", "application/json")?;
let window = window().unwrap();
let resp: Response = JsFuture::from(
window.fetch_with_request(&request)
).await?.dyn_into()?;
let json = JsFuture::from(resp.json()?).await?;
Ok(json)
}Calling from JavaScript
import init, { fetch_json, post_json } from './pkg/my_wasm.js';
await init();
// GET
const data = await fetch_json("https://api.example.com/items");
console.log(data);
// POST
const result = await post_json("https://api.example.com/items", {
name: "New Item",
value: 42
});Error handling
Always check resp.ok() — the Fetch API doesn't throw on HTTP errors (4xx/5xx):
let resp: Response = /* ... */;
if !resp.ok() {
let status = resp.status();
let text = JsFuture::from(resp.text()?).await?;
return Err(JsValue::from_str(
&format!("HTTP {}: {}", status, text.as_string().unwrap_or_default())
));
}Security: CORS
All fetch requests from Wasm follow the browser's CORS (Cross-Origin Resource Sharing) policy:
- Same-origin requests always work
- Cross-origin requests require the server to send
Access-Control-Allow-Originheaders - You can set the request mode explicitly:
opts.mode(web_sys::RequestMode::Cors); // default
opts.mode(web_sys::RequestMode::NoCors); // opaque responseThe Wasm module runs with the same origin restrictions as the hosting page — no special privileges.
Try It
Modify the function to include POST support with a JSON request body.
Try It
Chapter Quiz
Pass all questions to complete this lesson