workers-rs Documentation

repository·main·Indexed 25 days ago

https://github.com/cloudflare/workers-rs

Ergonomic Rust bindings for the Cloudflare Workers environment, providing a high-level API to write Workers in Rust. Includes support for the worker::Router, Cloudflare Queues, Email Workers (inbound and outbound), and integration with the axum web framework via axum-on-workers. The library supports standard types from the http crate via a feature flag and provides utilities for handling AbortSignal, DigestStream, and CPU limit signals.

Tokens
18.5K
Snippets
39
Records
126
Agent score
85%

What's inside workers-rs

  1. Understand workers-rs Benchmark Output

    main

    The benchmark suite measures streaming and parallel sub-request performance using internal requests (no network I/O). The output includes:

    • Timing: Per-iteration timing for Node.js end-to-end and Worker internal execution.
    • Statistics: Summary of average, min, and max times.
    • Data Transfer: Statistics for data transferred (e.g., 10MB per iteration for 10 parallel 1MB streams).
    • Throughput: Average throughput measured in Mbps.
  2. Quickstart: Create a new workers-rs project

    main

    Use cargo generate to scaffold a new project from the official workers-rs template. This will set up the necessary project structure, including src/lib.rs. During generation, you will be prompted to enable panic=unwind and abort recovery.

    To run your worker locally, use wrangler. To deploy to Cloudflare, use wrangler deploy after configuring your routes and zones in wrangler.toml.

  3. Run the workers-rs Benchmark Suite

    main

    To run the performance benchmarks for workers-rs, follow these steps to build the local environment and execute the suite. Ensure you have cloned the repository including all submodules.

    1. From the root of the workers-rs repository, build the local worker-build:
      npm run build
    2. Navigate to the benchmark directory, install dependencies, and run the benchmark:
      cd benchmark
      npm install
      npm run bench
    npm run build
    cd benchmark
    npm install
    npm run bench
  4. Test AbortSignal functionality locally

    main

    To test AbortSignal behavior, you can use a local slow Node.js server to simulate delayed responses and then invoke the Worker via wrangler dev.

    1. Start a local slow server

    Run the provided Node server to simulate delays. You can specify a port and a delay in milliseconds:

    node slow-server.mjs 3000

    2. Start the Worker

    Navigate to the abort-signal example directory and start the development server:

    npx wrangler dev

    3. Verify behavior with curl

    Test immediate abort:

    curl "http://localhost:8787/abort?url=http://localhost:3000"
    # Expected output: "Aborted: ..."

    Test request timeout (e.g., 500ms timeout against a 5s delay):

    curl "http://localhost:8787/timeout?url=http://localhost:3000&timeout=500"
    # Expected output: "Request timed out after 500ms"

    Test successful response within timeout (e.g., 2000ms timeout against a 100ms delay):

    curl "http://localhost:8787/timeout?url=http://localhost:3000/delay/100&timeout=2000"
    # Expected output: "Got response: {\"delayed_ms\":100,\"message\":\"slow response\"}"
  5. Enable Panic Recovery with `--panic-unwind`

    main

    By default, Rust panics terminate the WebAssembly instance. Using the --panic-unwind flag with worker-build converts panics into JavaScript PanicError exceptions, allowing the Worker to continue serving subsequent requests.

    Requirements & Behavior:

    • Requires the nightly Rust toolchain.
    • Rebuilds std with -Zbuild-std=std,panic_unwind and -Cpanic=unwind.
    • Enables wasm-bindgen panic catching.
    • Automatically reinitializes the instance after a hard abort (e.g., OOM or stack overflow).

    Unwind Safety: When using panic=unwind, exported function arguments and closure captures must satisfy the UnwindSafe trait. If you pass closures to JavaScript via Closure::new, you may need to wrap non-unwind-safe captures (like RefCell<T>) in std::panic::AssertUnwindSafe.

    # Running worker-build directly
    worker-build --panic-unwind
    # In wrangler.toml
    [build]
    command = "cargo install worker-build && worker-build --release --panic-unwind"
    use std::cell::Cell;
    use std::panic::AssertUnwindSafe;
    use wasm_bindgen::prelude::*;
    
    let counter = Cell::new(0u32);
    let counter_ref = AssertUnwindSafe(&counter);
    let closure = Closure::new(move || {
        counter_ref.set(counter_ref.get() + 1);
    });
  6. Send email using structured messages with `worker::SendEmail`

    main
    You can send emails using a structured approach by setting fields like from, to, subject, and text/html on a Message::builder(). The Cloudflare Workers runtime will automatically assemble the MIME body for you. This is suitable for standard email requirements where you do not need manual control over the MIME structure.
  7. Use SQLite Storage in Durable Objects

    main

    Durable Objects can use SQLite for persistent relational storage.

    1. Enable SQLite: In your wrangler.toml migration, use new_sqlite_classes instead of new_classes.
    2. Access Storage: Use state.storage().sql() to get a SqlStorage instance.
    3. Execute SQL: Use .exec(query, params) to run SQL commands.
    #[durable_object]
    pub struct SqlCounter {
        sql: SqlStorage,
    }
    
    impl DurableObject for SqlCounter {
        fn new(state: State, _env: Env) -> Self {
            let sql = state.storage().sql();
            sql.exec("CREATE TABLE IF NOT EXISTS counter(value INTEGER);", None)
                .expect("create table");
            Self { sql }
        }
    
        async fn fetch(&self, _req: Request) -> Result<Response> {
            // ... use self.sql.exec() to query and update
            Response::ok(format!("SQL counter is now {}", next))
        }
    }

    wrangler.toml configuration:

    [durable_objects]
    bindings = [
      { name = "SQL_COUNTER", class_name = "SqlCounter" }
    ]
    
    [[migrations]]
    tag = "v1"
    new_sqlite_classes = ["SqlCounter"]
  8. Use D1 Databases

    main

    D1 databases are in alpha and require the d1 feature flag in your Cargo.toml.

    To use D1, access the database via env.d1("binding_name")?. You can then prepare statements, bind parameters, and execute queries (e.g., .first::<T>(None).await?) to retrieve data mapped to Rust structs.

    # Cargo.toml
    worker = { version = "x.y.z", features = ["d1"] }
    use worker::*;
    
    #[derive(Deserialize)]
    struct Thing {
    	thing_id: String,
    	desc: String,
    	num: u32,
    }
    
    #[event(fetch, respond_with_errors)]
    pub async fn main(request: Request, env: Env, _ctx: Context) -> Result<Response> {
    	Router::new()
    		.get_async("/:id", |_, ctx| async move {
    			let id = ctx.param("id").unwrap()?;
    			d1 = ctx.env.d1("things-db")?;
    			let statement = d1.prepare("SELECT * FROM things WHERE thing_id = ?1");
    			let query = statement.bind(&[id])?;
    			let result = query.first::<Thing>(None).await?;
    			match result {
    				Some(thing) => Response::from_json(&thing),
    				None => Response::error("Not found", 404),
    			}
    		})
    		.run(request, env)
    		.await
    }
  9. Implement an Email Worker to receive and reply to emails

    main

    To create an Email Worker, use the #[event(email)] attribute to define your handler. The handler receives a worker::InboundEmail object. You can extract headers like Message-ID and Subject from the inbound email and use the InboundEmail::reply method to send a response.

    Note: Unlike outbound email, inbound delivery is not configured in wrangler.toml. You must attach an email address to the worker via the Email → Email Routing section of the Cloudflare dashboard after deployment.

  10. Understand runtime and target limitations

    main

    When building with workers-rs, keep the following constraints in mind:

    • Target Architecture: All code and third-party libraries must target wasm32-unknown-unknown. If a library does not support this target, it cannot be used on Cloudflare Workers.
    • Async Runtimes: Threaded async runtimes like tokio or async_std are not supported. You cannot deploy a Worker that relies on these runtimes.
    • Async/Await: While full runtimes are unsupported, async/await syntax is fully supported out of the box via the worker crate.
    • Runtime-Agnostic Primitives: You can still use runtime-agnostic primitives from crates like tokio (e.g., those found in tokio::sync).