Norse
Norse

Reputation: 648

Problem with Errors with actix-session, sqlx, async-graphql

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

Answers (1)

gxtaillon
gxtaillon

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::Errors 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

Related Questions