oscartbeaumont / rspc

A framework for building typesafe web backends in Rust
https://rspc.dev
MIT License
1.1k stars 50 forks source link

How to access headers to build request context in 0.2.0 #273

Closed banool closed 3 months ago

banool commented 3 months ago

I had this code previously:

let app = Router::new()
    .nest(
        "/api/rspc",
        rspc_router
            .endpoint(|req: rspc::integrations::httpz::Request| {
                let headers = req.headers();

                let auth_token = headers.get("Authorization").map(|header_value| {
                    let bearer = Bearer::decode(header_value).unwrap();
                    let token = bearer.token().to_string();
                    token
                });

                RspcContext {
                    admin_service,
                    auth_token,
                    jwt_verifier: jwk_auth,
                    metrics_query_service,
                }
            })
            .axum(),
    )
    .layer(
        ServiceBuilder::new()
            // Other layers
            .layer(Extension(prisma_client)),
    );

In 0.2.0 you don't have access to req in this context, the closure takes no arguments. Like this:

        rspc_axum::endpoint(rspc_router.clone(), move || RspcContext {
            admin_service: admin_service.clone(),
            auth_token: None,
            jwt_verifier: jwk_auth.clone(),
            metrics_query_service: metrics_query_service.clone(),
        })

I thought, how about I make this a middleware instead? No luck unfortunately, the request attached to the middleware context only has kind and path, no headers.

Is what I'm trying to do possible with 0.2.0?

banool commented 3 months ago

Figured it out from reading the source for rspc_axum, turns out you can do this:

        rspc_axum::endpoint(rspc_router.clone(), move |parts: Parts| {
            let headers = parts.headers;
            let auth_token = headers.get("Authorization").map(|header_value| {
                let bearer = Bearer::decode(header_value.into()).unwrap();
                let token = bearer.token().to_string();
                token
            });
            RspcContext {
                admin_service: admin_service.clone(),
                auth_token,
                jwt_verifier: jwk_auth.clone(),
                metrics_query_service: metrics_query_service.clone(),
            }
        }),

Happy days!

I know the docs are actively being updated, +1 to include this little tidbit in the docs 😃

oscartbeaumont commented 3 months ago

You beat me to it but gonna post this response I was typing anyway incase it's useful.


I am going to improve docs around this over the next week or so but with this new release rspc has full support for Axum extractors that do not access the HTTP body.

From a technical perspective this means anything that implements Axum's FromRequestParts.

I think right now you are limited to a single extractor/function argument but I will extend it to multiple in the future (only supporting one was just an oversight).

You can change your closure to |parts: axum::http::request::Parts| and it will work for what you need.