Pitasi / rscx

Rust Server Components. JSX-like syntax and async out of the box.
https://rscx.dev/
MIT License
63 stars 6 forks source link

Example of context with web server like Axum #9

Open pbouzakis opened 10 months ago

pbouzakis commented 10 months ago

I'm struggling to implement context in my application and was wondering if we could get an example of using context in more typical application running a web server like axum.

Pitasi commented 10 months ago

Hey there! Sorry to not have a proper Axum example, you're right, I'll make one as soon as I can :)

The idea would be something like this:

pub async fn your_handler() -> impl IntoResponse {
    rscx::axum::render(async move {
        html! {
            <h1>hello world</h1>
        }.await
    })
    .await
}

instead of <h1> use your custom components, and inside their body you can use rscx::provide_context() and rscx::use_context() to respectively register a new context or access one (like I did here https://github.com/Pitasi/rscx/blob/main/rscx/examples/context.rs#L41).

Does this help?

pbouzakis commented 10 months ago

I ended up with a solution by building my own context rather than using rsxc context.

Something like this

use axum::{http::Request, middleware::Next, response::Response};
use std::future::Future;

#[derive(Clone)]
pub struct Context {
    pub page_url: String,
}

tokio::task_local! {
    pub(crate) static CONTEXT: Context;
}

pub async fn context_provider_layer<B>(request: Request<B>, next: Next<B>) -> Response {
    let context = Context {
        page_url: request.uri().path().to_string(),
    };

    // Set the context for this request.
    provide_context(context, next.run(request)).await
}

pub async fn provide_context<F: Future<Output = O>, O>(context: Context, f: F) -> O {
    CONTEXT.scope(context, f).await
}

pub fn context() -> Option<Context> {
    CONTEXT.try_with(|c| c.clone()).ok()
}

I then add the context_provider_layer as middleware to axum, and then inside any component, I can call context.

Looking forward to your update!

npetrangelo commented 7 months ago

I was able to throw a component into an Axum handler like this

let router = Router::new()
    .route("/", get(|| async { Html(kinbox().await) }))
    .route("/styles.css", get(|| async { Css(getCss().await) }));