Actix Web With MongoDB

I'm trying to figure out how to set up an API with actix-web and mongodb. I found this thread which shows the following code snippet as the solution:

struct AppState {
   pub db: Database,
   pub client: Client
}    

#[actix_web::main]
async fn main() -> std::io::Result<()> {
  let bind_addr = "127.0.0.1:8080";
  println!("Server Running at {} ....", bind_addr);

  // A Client is needed to connect to MongoDB:
  let client_uri = "mongodb://127.0.0.1:27017";
  let mut options = ClientOptions::parse(&client_uri).await?;
  let client = Client::with_options(options)?;
  let db = client.database("my_database");

HttpServer::new(|| App::new()
    .data(AppState {
            db,
            client
        })
    .configure(config))
    .bind(bind_addr)?
    .run()
    .await
}

However, when I try to replicate this with the following code:

use actix_web::{App, HttpServer};
use mongodb::{ Client, Database, bson::doc, options::ClientOptions };
use serde::{ Deserialize, Serialize };

struct AppState {
    pub db: Database,
    pub client: Client
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Naic {
    code: u32,
    title: String,
}

#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { 
    let client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
    let client = Client::with_options(client_options)?;
    let db = client.database("NAICS");
    // let collection = db.collection::<Naic>("Descriptions");
    // match collection.find_one(doc! { "Code": 444 }, None).await? {
    //     Some(naic) => println!("Description: {}", naic.title),
    //     None => println!("NAIC not found")
    // }
    HttpServer::new(|| App::new()
        .data(AppState {
                db,
                client
            }))
        .bind("127.0.0.1:8080")?
        .run()
        .await;
    Ok(())
}

I get an error because db has type mongodb::Database, which does not implement the Copy trait (the same goes for the client).

How do I overcome this?

What's the full error?

error[E0507]: cannot move out of `db`, a captured variable in an `Fn` closure
  --> src\main.rs:29:17
   |
21 |     let db = client.database("NAICS");
   |         -- captured outer variable
...
29 |                 db,
   |                 ^^ move occurs because `db` has type `Database`, which does not implement the `Copy` trait

error[E0507]: cannot move out of `client`, a captured variable in an `Fn` closure
  --> src\main.rs:30:17
   |
20 |     let client = Client::with_options(client_options)?;
   |         ------ captured outer variable
...
30 |                 client
   |                 ^^^^^^ move occurs because `client` has type `mongodb::Client`, which does not implement the `Copy` trait

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0507`.

Ah, right. So this is because the closure is called once per thread that the web server will spawn. You'll need to clone them, or create them inside the closure if they aren't cloneable.

1 Like

If I try to do this:

#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { 
    HttpServer::new(|| {
        let client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
        let client = Client::with_options(client_options)?;
        let db = client.database("NAICS");
        App::new().data(AppState { db, client })
            .bind("127.0.0.1:8080")?
            .run()
            .await
    });
    Ok(())
}

it breaks because await is only allowed inside async functions/blocks :frowning_face:

UPDATE: This looks like it might work:

use actix_web::{ App, HttpServer };
use mongodb::{ Client, Database, bson::doc, options::ClientOptions };
use serde::{ Deserialize, Serialize };

#[derive(Debug, Clone)]
struct AppState {
    pub db: Database,
    pub client: Client
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Naic {
    code: u32,
    title: String,
}

#[actix_web::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { 
    let client_options = ClientOptions::parse("mongodb://localhost:27017").await?;
    let client = Client::with_options(client_options)?;
    let db = client.database("NAICS");
    let state = AppState { db, client };
    HttpServer::new(move || {
        App::new().data(state.clone())
    }).bind("127.0.0.1:8080")?
    .run()
    .await?;
    Ok(())
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.