0

I want to use Rust and Juniper to create a GraphQL server. This server has to access the database.

I've been trying to follow this example code from Juniper but it uses an empty Context to give over to the Schema; I need to send a pool for database connections.

I want to be able to connect to the GraphQL via POST, GET and websockets.

type RepositoryPool = r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;

fn graphql(
    db_pool: RepositoryPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    let state = warp::any().map(move || Context::new(db_pool.clone()));
    let root_node = Arc::new(schema());
    let graphql_filter = make_graphql_filter(schema(), state.boxed());

    let post_filter = warp::post()
        .and(warp::body::content_length_limit(1024 * 16))
        .and(graphql_filter.clone());

    let get_filter = warp::get().and(graphql_filter);

    let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
        let root_node = root_node.clone();
        let db_pool = db_pool.clone();
        ws.on_upgrade(move |websocket| async move {
            serve_graphql_ws(
                websocket,
                root_node,
                ConnectionConfig::new(Context::new(db_pool)),
            )
            .map(|r| {
                if let Err(e) = r {
                    println!("Websocket error: {}", e);
                }
            })
            .await
        })
    });

    warp::path("graphql").and(get_filter.or(post_filter).or(ws_filter))
}

However, I get an error:

error[E0382]: use of moved value: `db_pool`
  --> src\filters.rs:39:34
   |
27 |   db_pool: RepositoryPool,
   |   ------- move occurs because `db_pool` has type `r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>`, which does not implement the `Copy` trait
28 | ) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
29 |   let state = warp::any().map(move || Context::new(db_pool.clone()));
   |                               -------              ------- variable moved due to use in closure
   |                               |
   |                               value moved into closure here
...
39 |   let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
   |                                  ^^^^^^^^^^^^^^^^^^^^^^^ value used here after move
40 |     let root_node = root_node.clone();
41 |     let db_pool = db_pool.clone();
   |                   ------- use occurs due to use in closure

I don't understand enough about the way these values are moved to solve this issue. How do I solve issues like this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lanbo
  • 15,118
  • 16
  • 70
  • 147
  • 2
    Looks like you need to clone the pool outside of the closure, and then only move the clone. – Sven Marnach Oct 26 '20 at 16:40
  • 1
    @SvenMarnach: The syntax for doing that is a bit cumbersome, but _there is a crate for that!_: [enclose](https://crates.io/crates/enclose). – rodrigo Oct 26 '20 at 16:45
  • @SvenMarnach unfortunately I tried to litter this code with `db_pool.clone()` just about anywhere, and I can't figure out where to clone it. – Lanbo Oct 26 '20 at 16:46
  • 1
    `let db_pool_clone = db_pool.clone();` and then move `db_pool_clone` in the closure? – Cerberus Oct 26 '20 at 16:50
  • 1
    See also [Warp filter moving variable out of its environment](https://stackoverflow.com/q/63132362/155423) and its duplicate [Is there another option to share an Arc in multiple closures besides cloning it before each closure?](https://stackoverflow.com/q/31360003/155423) – Shepmaster Oct 26 '20 at 16:52
  • 1
    It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Oct 26 '20 at 16:54

1 Answers1

0

Thanks to some comments, I figured out a way that the Rust compiler accepts:

fn graphql(
    db_pool: RepositoryPool,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
    let db_pool_clone = db_pool.clone();
    let state = warp::any().map(move || Context::new(db_pool_clone.clone()));
    let root_node = Arc::new(schema());
    let graphql_filter = make_graphql_filter(schema(), state.boxed());

    let post_filter = warp::post()
        .and(warp::body::content_length_limit(1024 * 16))
        .and(graphql_filter.clone());

    let get_filter = warp::get().and(graphql_filter);

    let ws_filter = warp::ws().map(move |ws: warp::ws::Ws| {
        let root_node = root_node.clone();
        let db_pool = db_pool.clone();
        ws.on_upgrade(move |websocket| async move {
            serve_graphql_ws(
                websocket,
                root_node,
                ConnectionConfig::new(Context::new(db_pool)),
            )
            .map(|r| {
                if let Err(e) = r {
                    println!("Websocket error: {}", e);
                }
            })
            .await
        })
    });

    warp::path("graphql").and(get_filter.or(post_filter).or(ws_filter))
}

I don't fully understand why this is necessary, but it works for now.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lanbo
  • 15,118
  • 16
  • 70
  • 147
  • it's necessary because a `move` closure will move all the referenced *names* inside the closure, so when the closure uses ` db_pool.clone()`, Rust immediately moves `db_pool` into the closure on creation; and only clones said `db_pool` when the closure *runs*. – Masklinn Oct 27 '20 at 06:42
  • Incidentally a common pattern for this sort of situation is the "precise capture clause" pattern: http://smallcultfollowing.com/babysteps/blog/2018/04/24/rust-pattern-precise-closure-capture-clauses/ – Masklinn Oct 27 '20 at 06:46