I'm now trying to build a graphql server using axum and async-graphql.
The biggest problem is that data sharing between graphql and axum is not good. The reason why I want to do this is because I want to pass the http header for authentication received by axum to the world of graphql.
Can anyone solve this problem or suggest another way?
Cargo.toml
[package] name = "app" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] axum = { version = "0.6.1", features = ["headers"] } tokio = { version = "1.19.2", features = ["rt-multi-thread", "macros"] } serde = { version = "1.0.136", features = ["derive"] } async-graphql = { version = "5.0.4", features = ["chrono"] } sqlx = { version = "0.6.2", features = [ "runtime-actix-native-tls", "postgres", "chrono" ] } dotenv = "0.15.0" tower-http = { version = "0.3.5", features = ["cors", "trace"] } tokio-stream = "0.1.11" chrono = "0.4.23" jsonwebtoken = "8.2.0" thiserror = "1.0.38" async-trait = "0.1.60"
main.rs
mod db;
mod repositories;
mod resolvers;
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
Request, Response, Schema,
};
use axum::{
extract::{Extension, State},
http::{
header::{ACCEPT, AUTHORIZATION},
HeaderValue, Method, Request as AxumRequest,
},
middleware::Next,
response::{Html, IntoResponse, Response as AxumResponse},
routing::get,
Json, Router,
};
use dotenv::dotenv;
use resolvers::{QueryRoot, Subscription};
use std::net::SocketAddr;
use tower_http::cors::CorsLayer;
use crate::{db::DB, resolvers::Mutation};
pub type MainSchema = Schema<QueryRoot, Mutation, Subscription>;
async fn graphql_handler(schema: Extension<MainSchema>, req: Json<Request>) -> Json<Response> {
schema.execute(req.0).await.into()
}
async fn graphql_playground() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/")))
}
#[derive(Clone, Debug)]
pub struct AppState {
db: DB,
token: Option<String>,
}
impl AppState {
fn set_token(mut self, token: String) {
self.token = Some(token)
}
}
async fn propagate_header<B>(
State(state): State<&AppState>,
req: AxumRequest<B>,
next: Next<B>,
) -> AxumResponse {
let token = req.headers().get("Authorization");
if token.is_some() {
// TODO: Put token in state.
};
next.run(req).await
}
#[tokio::main]
async fn main() {
dotenv().ok();
let server = async {
let db = DB::new().await;
let state = AppState { db, token: None };
let schema = Schema::build(QueryRoot, Mutation, Subscription)
// .limit_depth(5)
.data(&state)
.finish();
let cors_layer = CorsLayer::new()
.allow_origin("*".parse::<HeaderValue>().unwrap())
.allow_methods([Method::GET, Method::POST, Method::OPTIONS])
.allow_headers(vec![AUTHORIZATION, ACCEPT]);
let app = Router::new()
.route("/", get(graphql_playground).post(graphql_handler))
.layer(cors_layer)
.layer(axum::middleware::from_fn_with_state(
&state,
propagate_header,
))
.layer(Extension(schema));
let addr = SocketAddr::from(([0, 0, 0, 0], 8009));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
};
tokio::join!(server);
}
error
error[E0597]: `state` does not live long enough
--> src/main.rs:71:19
|
69 | let schema = Schema::build(QueryRoot, Mutation, Subscription)
| ______________________-
70 | | // .limit_depth(5)
71 | | .data(&state)
| |___________________^^^^^^- argument requires that `state` is borrowed for `'static`
| |
| borrowed value does not live long enough
...
93 | };
| - `state` dropped here while still borrowed