Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using utoipa for Lemmy #662

Open
Nutomic opened this issue Jun 30, 2023 · 4 comments
Open

Using utoipa for Lemmy #662

Nutomic opened this issue Jun 30, 2023 · 4 comments

Comments

@Nutomic
Copy link

Nutomic commented Jun 30, 2023

Im a maintainer for Lemmy, which uses actix-web and has a large API of probably around 100 endpoints. I tried to integrate utoipa in order to generate API docs, as it seems like the best option in Rust.

Unfortunately this first attempt wasnt successful for a couple of reasons. Some of these have already been reported, but I thought makes sense to report these all together in context.

  • utoipa::path needs to duplicate request and response types (Automatic type recognition for path operation handlers #591)
  • scopes are not supported (Missing actix::web::scope support #121)
  • All API endpoints and structs need to be specified manually on the ApiDoc struct. This is completely unfeasible in a project of Lemmys size (auto detection of paths/schemas for documentation #624)
  • Even nested structs need to be specified manually on the ApiDoc components. Again, unfeasible as we have hundreds of those. Its even worse because utoipa doesnt warn/error if you forget a type, and instead produces invalid output. This should be handled similar to serde::Derive, by requiring that any struct which derives utoipa::ToSchema also has its members implementing ToSchema. The derive macro could write all structs to a global vec so that they can be included in the output automatically.
  • utoipa only seems to support routes with #[get("path")] annotations. We currently configure all routes explicitly with ServiceConfig which is nice to have all paths in one place.

So the main problem is that a lot of information needs to be specified twice, once for actix-web and once for utoipa. This may work for a small project with a dozen API calls, but not when there are over hundred. It would be great if utoipa could parse the information from ServiceConfig instead (or provide a wrapper which passes route info to both actix-web and utoipa).

I hope this explanation makes sense, let me know if anything is unclear. You can also see the code with some comments here regarding these problems. I can also open separate issues for each item if desired.

@Narven
Copy link

Narven commented Jul 4, 2023

Looks like a good crate, but no response from author in any way regarding issues. :( I dont know rust well enough to help

@juhaku
Copy link
Owner

juhaku commented Jul 5, 2023

These are very good points and good collection of things that needs or at least should be solved within some timespan. My goal for this is to have more automatic configuration than it currently has.

I get get that the double declaration is not feasible but I yet haven't find the time nor the perfect design on how to implement it that it would still be intuitive. There are 2 possible designs that I can think of, one where we implement custom ServiceConfig or Router that works as a bridge between the actual Router / ServiceConfig and same time maps the stuff to the openapi spec. Another one would be that the ServiceConfig or Router should be generated based on the OpenApi types. However this is still in early stages and PRs are of course welcome 🙂 One thing is sure, that implementing a ServiceConfig shim is a lot of work especially because it would have to be implemented to all supported frameworks, or then the framework support need to be reduced.

Automatic schema and path recognition is something that I have also been investigating and yes it could be a global vec but there is a catch, I am not sure how well that would work cross crates. Becuse some might have defined some types on another crate. That is a worth testing out (I guess). Currently there is one thrid-party crate that is actually working on solutions for this purpose #635 as I have no time to do it all (at least that fast 😆).

But eventually yes, I also want this to be more thightly integrated with frameworks and all the help is welcome.

@juhaku
Copy link
Owner

juhaku commented Aug 3, 2023

To get back to this. At the moment probably quickest and easiest way to avoid duplication in OpenAPI path registeration is to manually implement the OpenApi with OpenApiBuilder and then along with that use PathBuilder to build paths. This should be used jointly with custom macro_rules which can be used to reduce boiler plate code. Some reference can be found here #230 (comment).

juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
@juhaku
Copy link
Owner

juhaku commented Oct 22, 2024

@Nutomic There is now coming support for actix-web::scopes and ServiceConfig with PR #1158 However manual routes are not supported as of yet, but the HttpServiceFactory style routes are.

juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
juhaku added a commit that referenced this issue Oct 22, 2024
Implements wrappers for `ServiceConfig`, `App`, `Scope` of actix-web.
This allows users to create `App` with collecting `paths` and `schemas`
recursively without registering them to `#[openapi(...)]` attribute.

Example of new supported syntax.
```rust
 use actix_web::{get, App};
 use utoipa_actix_web::{scope, AppExt};

 #[derive(utoipa::ToSchema)]
 struct User {
     id: i32,
 }

 #[utoipa::path(responses((status = OK, body = User)))]
 #[get("/user")]
 async fn get_user() -> Json<User> {
     Json(User { id: 1 })
 }

 let (_, mut api) = App::new()
     .into_utoipa_app()
     .service(scope::scope("/api/v1").service(get_user))
     .split_for_parts();
```

Relates #283 Relates #662
Closes #121 Closes #657
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants