Actix + Utoipa : How to return scope implementation from function?

Recently I moved to Actix and started rewriting my old code from Rocket.
Suddenly the problem appeared: I had a routes that were logically splitted to groups. For example, user routes was laid in user.rs and product routes - product.rs.
And when I need to add documentation, before I just call a function that returns concrete group router (user_routes(), product_routes()) and in main.rs it could be like that:

mount_endpoints_and_merged_docs {
...
"/" => user_routes(),
"/" => product_routes()
...
}

And I want the same thing but with actix + utoipa.
I tried to create a function like that:

pub fn test_routes() -> utoipa_actix_web::scope::Scope<()> {
    utoipa_actix_web::scope::scope("/test").service(test_post).
}

but this doesn't work, because utoipa_actix_web::scope returns Scope where T - ScopeEndpoint, but I can't set return value as Scope because it's private.

Hope someone can give me advice. Thanks in advance!

UPD: utoipa_actix_web::scope - is an extended version of default Scope, the main problem is that we can use default Scope without any generic type, while this one cannot be used.

Complicated generics and private types are a common problem when trying to declare a actix-web app. Try using App::configure instead of your function to add the scope to your app via the callback.

1 Like

Thanks for advice, @jofas

I leave here a proper example what I'm trying to do:

//main.rs
...
HttpServer::new(||{
        App::new()
            .into_utoipa_app()
            .service(user_routes())
            .openapi_service(|api| {
                SwaggerUi::new("/swagger-ui/{_:.*}").url("/api/openapi.json", api)
            })
            .into_app()
    })
.bind(...)
...
//user.rs
pub fn user_routes() -> utoipa_actix_web::scope::Scope<()> {
    utoipa_actix_web::scope("/user")
    .service(post_user)
    .service(get_user)
    .service(update_user)
    .service(delete_user)
//^^^^^
//expected struct `utoipa_actix_web::scope::Scope<()>`
//found struct `utoipa_actix_web::scope::Scope<actix_web::scope::ScopeEndpoint>`
}

Hope this helps.

UtoipaApp and utoipa_actix_web::scope::Scope work the same as I described above. I.e. they are configurable through a configure method taking a callback as argument through which you can configure your app or scope:

//main.rs
...
HttpServer::new(||{
        App::new()
            .into_utoipa_app()
            .service(
                utoipa_actix_web::scope("/user").configure(user_routes),
            )
            .openapi_service(|api| {
                SwaggerUi::new("/swagger-ui/{_:.*}").url("/api/openapi.json", api)
            })
            .into_app()
    })
.bind(...)
...
//user.rs
use utoipa_actix_web::service_config::ServiceConfig;

pub fn user_routes(config: &mut ServiceConfig) {
    config
        .service(post_user)
        .service(get_user)
        .service(update_user)
        .service(delete_user);
}
1 Like

Got it now.
I tried to create a config variable as is, and didn't get what do u mean.
Thanks a lot!

1 Like