Reputation: 648
I am trying to create a graphql resolver (with async-graphql) in an actix web app. I am doing a me query, where I am getting the user id from the session, and fetching the user from the database. This is the most relevant parts of the code
use async_graphql::*;
use anyhow::anyhow;
use actix_session::Session;
use async_graphql_actix_web::{Request, Response};
#[derive(Default, Debug)]
pub struct UserQuery;
struct User {
id: i32,
username: String,
...
}
#[Object] // <- ERROR (Squiggly lines) IS HERE
impl UserQuery {
...
// Other resolvers are fine
// But this one does not work
async fn me(&self, ctx: &Context<'_>) -> actix_http::error::Result<User> {
let actix_session = ctx.data_unchecked::<Shared<actix_session::Session>>();
let user_id = actix_session.get::<i32>("user_id");
if let Ok(Some(id)) = user_id {
let pool = ctx.data_unchecked::<DbPool>();
let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", id)
.fetch_one(pool)
.await
.map_err(|e| actix_http::error::ErrorBadRequest(e))?;
return Ok(user);
} else {
Err(anyhow!("lsdhf")).map_err(|e| actix_http::error::ErrorBadRequest(e))?
}
}
}
/// A wrapper around e.g. sessions, to inject into the request
#[derive(Clone, Debug)]
pub struct Shared<T>(pub Option<SendWrapper<T>>);
impl<T> Shared<T> {
pub fn new(v: T) -> Self {
Self(Some(SendWrapper::new(v)))
}
}
impl<T> Deref for Shared<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&*self.0.as_deref().clone().unwrap()
}
}
pub async fn graphql(schema: web::Data<SchemaType>, req: Request, session: Session) -> Response {
let sesh = Shared::new(session);
schema.execute(req.into_inner().data(sesh)).await.into()
}
But I get the error
`(dyn actix_web::ResponseError + 'static)` cannot be sent between threads safely
the trait `std::marker::Send` is not implemented for `(dyn actix_web::ResponseError + 'static)`
required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn actix_web::ResponseError + 'static)>`
required because it appears within the type `std::boxed::Box<(dyn actix_web::ResponseError + 'static)>`
required because it appears within the type `actix_web::Error`
required because it appears within the type `std::result::Result<std::option::Option<i32>, actix_web::Error>`
required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9> {std::future::ResumeTy, &'r routes::graphql::Shared<actix_session::Session>, std::result::Result<std::option::Option<i32>, actix_web::Error>, i32, &'s sqlx::Pool<sqlx::Postgres>, &'t0 str, sqlx::postgres::PgArguments, sqlx::query::Query<'t1, sqlx::Postgres, sqlx::postgres::PgArguments>, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::query::Map<'t2, sqlx::Postgres, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::postgres::PgArguments>, impl std::future::Future, ()}`
required because it appears within the type `[static generator@src/graphql/user.rs:102:67: 115:6 for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9> {std::future::ResumeTy, &'r routes::graphql::Shared<actix_session::Session>, std::result::Result<std::option::Option<i32>, actix_web::Error>, i32, &'s sqlx::Pool<sqlx::Postgres>, &'t0 str, sqlx::postgres::PgArguments, sqlx::query::Query<'t1, sqlx::Postgres, sqlx::postgres::PgArguments>, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::query::Map<'t2, sqlx::Postgres, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::postgres::PgArguments>, impl std::future::Future, ()}]`
required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, tracing::Span, [static generator@src/graphql/user.rs:102:67: 115:6 for<'t11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22> {std::future::ResumeTy, &'t11 routes::graphql::Shared<actix_session::Session>, std::result::Result<std::option::Option<i32>, actix_web::Error>, i32, &'t12 sqlx::Pool<sqlx::Postgres>, &'t13 str, sqlx::postgres::PgArguments, sqlx::query::Query<'t14, sqlx::Postgres, sqlx::postgres::PgArguments>, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::query::Map<'t15, sqlx::Postgres, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::postgres::PgArguments>, impl std::future::Future, ()}], impl std::future::Future, tracing::instrument::Instrumented<impl std::future::Future>, ()}`
required because it appears within the type `[static generator@src/graphql/user.rs:101:5: 101:60 for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, tracing::Span, [static generator@src/graphql/user.rs:102:67: 115:6 for<'t11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22> {std::future::ResumeTy, &'t11 routes::graphql::Shared<actix_session::Session>, std::result::Result<std::option::Option<i32>, actix_web::Error>, i32, &'t12 sqlx::Pool<sqlx::Postgres>, &'t13 str, sqlx::postgres::PgArguments, sqlx::query::Query<'t14, sqlx::Postgres, sqlx::postgres::PgArguments>, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::query::Map<'t15, sqlx::Postgres, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::postgres::PgArguments>, impl std::future::Future, ()}], impl std::future::Future, tracing::instrument::Instrumented<impl std::future::Future>, ()}]`
required because it appears within the type `std::future::from_generator::GenFuture<[static generator@src/graphql/user.rs:101:5: 101:60 for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, tracing::Span, [static generator@src/graphql/user.rs:102:67: 115:6 for<'t11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22> {std::future::ResumeTy, &'t11 routes::graphql::Shared<actix_session::Session>, std::result::Result<std::option::Option<i32>, actix_web::Error>, i32, &'t12 sqlx::Pool<sqlx::Postgres>, &'t13 str, sqlx::postgres::PgArguments, sqlx::query::Query<'t14, sqlx::Postgres, sqlx::postgres::PgArguments>, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::query::Map<'t15, sqlx::Postgres, [closure@/Users/sorenhansen/.cargo/registry/src/github.com-1ecc6299db9ec823/sqlx-0.4.2/src/macros.rs:519:9: 519:102], sqlx::postgres::PgArguments>, impl std::future::Future, ()}], impl std::future::Future, tracing::instrument::Instrumented<impl std::future::Future>, ()}]>`
required because it appears within the type `impl std::future::Future`
required because it appears within the type `impl std::future::Future`
required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3, 't4> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, ()}`
required because it appears within the type `[static generator@src/graphql/user.rs:13:1: 13:10 for<'r, 's, 't0, 't1, 't2, 't3, 't4> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, ()}]`
required because it appears within the type `std::future::from_generator::GenFuture<[static generator@src/graphql/user.rs:13:1: 13:10 for<'r, 's, 't0, 't1, 't2, 't3, 't4> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, ()}]>`
required because it appears within the type `impl std::future::Future`
required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22, 't23, 't24, 't25, 't26, 't27, 't28, 't29, 't30, 't31, 't32, 't33, 't34, 't35, 't36> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, (), graphql::response_types::user_form_response::UserFormResponse, async_graphql::ContextBase<'t10, &'t11 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, &'t12 graphql::response_types::user_form_response::UserFormResponse, &'t13 async_graphql::ContextBase<'t14, &'t15 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, async_graphql::ContextBase<'t16, &'t17 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, &'t18 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>, std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = std::result::Result<async_graphql::Value, async_graphql::ServerError>> + std::marker::Send + 't19)>>, impl std::future::Future, models::user::User, &'t28 models::user::User, impl std::future::Future}`
required because it appears within the type `[static generator@src/graphql/user.rs:13:1: 13:10 for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22, 't23, 't24, 't25, 't26, 't27, 't28, 't29, 't30, 't31, 't32, 't33, 't34, 't35, 't36> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, (), graphql::response_types::user_form_response::UserFormResponse, async_graphql::ContextBase<'t10, &'t11 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, &'t12 graphql::response_types::user_form_response::UserFormResponse, &'t13 async_graphql::ContextBase<'t14, &'t15 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, async_graphql::ContextBase<'t16, &'t17 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, &'t18 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>, std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = std::result::Result<async_graphql::Value, async_graphql::ServerError>> + std::marker::Send + 't19)>>, impl std::future::Future, models::user::User, &'t28 models::user::User, impl std::future::Future}]`
required because it appears within the type `std::future::from_generator::GenFuture<[static generator@src/graphql/user.rs:13:1: 13:10 for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7, 't8, 't9, 't10, 't11, 't12, 't13, 't14, 't15, 't16, 't17, 't18, 't19, 't20, 't21, 't22, 't23, 't24, 't25, 't26, 't27, 't28, 't29, 't30, 't31, 't32, 't33, 't34, 't35, 't36> {std::future::ResumeTy, &'r graphql::user::UserQuery, &'s async_graphql::ContextBase<'t0, &'t1 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, impl std::future::Future, (), graphql::response_types::user_form_response::UserFormResponse, async_graphql::ContextBase<'t10, &'t11 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, &'t12 graphql::response_types::user_form_response::UserFormResponse, &'t13 async_graphql::ContextBase<'t14, &'t15 async_graphql::Positioned<async_graphql::async_graphql_parser::types::SelectionSet>>, async_graphql::ContextBase<'t16, &'t17 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>>, &'t18 async_graphql::Positioned<async_graphql::async_graphql_parser::types::Field>, std::pin::Pin<std::boxed::Box<(dyn std::future::Future<Output = std::result::Result<async_graphql::Value, async_graphql::ServerError>> + std::marker::Send + 't19)>>, impl std::future::Future, models::user::User, &'t28 models::user::User, impl std::future::Future}]>`
required because it appears within the type `impl std::future::Future`
required for the cast to the object type `dyn std::future::Future<Output = std::result::Result<std::option::Option<async_graphql::Value>, async_graphql::ServerError>> + std::marker::Send`rustcE0277
What should I do? I have tried with anyhow for errors as well, with no luck. I think the error is with the sqlx stuff, because when i remove the query_as
part the error goes away.
The relevant dependencies are
[dependencies]
actix-web = "3"
actix-session = "0.4.1"
actix-redis = "0.9.1"
actix-http = "2.2.1"
async-graphql = { version = "2.9.12", features = ["chrono", "log"] }
async-graphql-actix-web = "2.9.12"
send_wrapper = "0.5.0"
anyhow = "1.0.43"
If it is any help, the whole repo can be found here https://github.com/SorenHolstHansen/full-stack-starter
Upvotes: 2
Views: 1237
Reputation: 1076
Try using an actix_http::error::Result
as return type for your me
function.
The results have different error types.
Your function's return anyhow::Result
is using an anyhow::Error
as E
.
Session get however will use a ResponseError
which is not threadsafe.
Additionally there's no automatic conversion between the types, you'll need to use something like below to convert the anyhow::Error
s to actix compatible Errors.
returnsAnyhowErrors()
.map_err(|e| error::ErrorBadRequest(e))?
It's difficult to tell if that's the problem but in general with actix I have gotten the habit of switching to actix results only as late as possible to keep using anyhow errors almost everywhere. However there are cases, like yours where that not possible, or at least I haven't found how to do it.
Upvotes: 1