How to pass a Trait object via app_data to Actix Web?

This is my post endpoint

#[post("/entity.create")]
pub async fn create(entity: Json<Entity>, store: Data<dyn Store>) 
-> Result<impl Responder> 
{
    let response = store.create_entity(&entity).await.unwrap();

    Ok(HttpResponse::Ok().json(response))
}

store; for now, is a typical CRUD persistence API with postgres backend.

The idea is to have several Store implementation for persistence, for example a PostgresStore, a SqliteStore or TextStore, etc.


This is how I'm launching the server

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let postgres_store = PostgresTestStore::new().await.unwrap();

    HttpServer::new(move || {
        App::new()
            .app_data(Data::new(postgres_store.clone()))
            .service(web::scope("/api/v1").service(entity::create))
            .wrap(Logger::default())
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

Unfortunately, this doesn't work.

Failed to extract Data<dyn server::stores::Store> for create handler. For the Data extractor to work correctly, wrap the data with Data::new() and pass it to App::app_data(). Ensure that types align in both the set and retrieve calls.


Right now Store trait is just

#[async_trait]
pub trait Store {
    async fn create_entity(&self, entity: &Entity) -> Result<Entity>;
}

and PostgresTestStore is implemented with docker and sqlx.


There doesn't seem to be a lot of similar questions online, so I am not sure what I might be doing wrong. What I did find was this answer, which for some reason doesn't work for me. Any help is greatly appriciated! :pray:

Got it working!

let store = PostgresTestStore::new().await.unwrap();

HttpServer::new(move || {
    let store_arc: Arc<dyn Store> = Arc::new(store.clone());
    let store_data: Data<dyn Store> = Data::from(store_arc);
    App::new()
        .service(health)
        .app_data(store_data)
        .service(web::scope("/api/v1").service(entity::create))
        .wrap(Logger::default())
})
.bind(("0.0.0.0", 8080))?
.run()
.await

especially notice

let store_arc: Arc<dyn Store> = Arc::new(store.clone());
let store_data: Data<dyn Store> = Data::from(store_arc);
1 Like