3

Integrating the Rust Axum Framework with Cloudflare Workers

 1 year ago
source link: https://logankeenan.com/posts/rust-axum-and-cloudflare-workers/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Integrating the Rust Axum Framework with Cloudflare WorkersHome Public Talks

Integrating the Rust Axum Framework with Cloudflare Workers

April 20, 2023

In this post, I'll share my experience integrating the Rust Axum framework into a Cloudflare worker. The journey started with contributing to Axum, which allowed it to compile to WebAssembly (Wasm). The next step was getting Axum to work in a Cloudflare worker, leading me to create a crate called axum-cloudflare-adapter. Let's dive into the challenges I faced and how I tackled them.

Mapping Requests and Responses

Axum uses http::Request which is not the same as Cloudflare's worker::Request, so I needed to map them accordingly. I created the to_axum_request function to solve this issue. The same problem exists with responses, so I created the to_worker_response function.

use axum_cloudflare_adapter::{to_axum_request, to_worker_response};
use tower_service::Service;

#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: worker::Context) -> Result<Response> {
    let mut router: AxumRouter = AxumRouter::new();
    let axum_request = to_axum_request(req).await.unwrap();
    let axum_response = router.call(axum_request).await.unwrap();
    let response = to_worker_response(axum_response).await.unwrap();

    response
}

Accessing Cloudflare's worker::Env within Axum Routes

To use Cloudflare's worker::Env within my Axum routes, I initially tried to put it in the state, but worker::Env doesn't implement Sync or Send. To work around this, I created EnvWrapper, which does implement Sync and Send with no-ops. This is fine because workers are always executed in a single context.

use axum_cloudflare_adapter::{EnvWrapper};

#[derive(Clone)]
pub struct AxumState {
    pub env_wrapper: EnvWrapper,
}

#[event(fetch)]
pub async fn main(_: Request, env: Env, _: worker::Context) -> Result<Response> {
    let axum_state = AxumState {
        env_wrapper: EnvWrapper::new(env),
    };
}

Handling Non-Send Futures in Axum Routes

Axum expects routes to return a Send future, but JS types don't implement Send. This is a problem if you want to use worker::Fetch to make an HTTP request. To work around this issue, I created a macro that wraps the entire Axum route in a wasm_bindgen_futures::spawn_local and passes the result of the route back to the main thread using a oneshot::channel. Credit goes to SebastiaanYN for the solution. I simply created a macro for it. To overcome this issue, you can add the #[worker_route_compat] macro, and it'll make your Axum routes compatible.

#[worker_route_compat]
pub async fn index(State(state): State<AxumState>) -> impl IntoResponse {
    // your code
}

// The macro converts it to the following
pub async fn index(State(state): State<AxumState>) -> impl IntoResponse {
    wasm_bindgen_futures::spawn_local(async move {
        let result = {
            // your code
        };
        tx.send(result).unwrap();
    });
    rx.await.unwrap
}

I created a demo application using the axum-cloudflare-adapter, source. It proxies this blog through a worker.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK