Swagger/OpenAPI-Generierung für Rust: Utoipa und Aide
In der modernen Webentwicklung mit Rust ist die Erstellung einer dokumentierten API kein Luxus, sondern eine Notwendigkeit. Swagger (OpenAPI) ermöglicht die automatische Generierung interaktiver Dokumentation, clientseitiger SDKs und Testkonsolen. Im Rust-Ökosystem nehmen zwei Werkzeuge eine führende Position ein: Utoipa (basierend auf Makros) und Aide (mit Fokus auf Typisierung und manuelle Kontrolle). Betrachten wir sie im Detail.
Utoipa – Makros für OpenAPI 3.0
Utoipa ist eine Bibliothek, die Attribut-Makros (#[utoipa::path]) verwendet, um automatisch eine OpenAPI-Spezifikation zu generieren. Sie ist eng mit den gängigen Frameworks integriert: Actix-web, Axum, Rocket, Warp.
Besonderheiten:
- Vollständige Unterstützung von OpenAPI 3.0 (paths, components, security schemes).
- Automatische Extraktion von Typen aus Rust-Strukturen (mithilfe von
#[derive(ToSchema)]). - Generierung einer JSON/YAML-Datei für die Swagger-Benutzeroberfläche.
- Unterstützung von
actix-webout-of-the-box (überutoipa-swagger-ui). - Niedrige Einstiegshürde – es reicht, Makros hinzuzufügen.
Beispiel mit Actix-web:
use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};use serde::{Deserialize, Serialize};use utoipa::{OpenApi, ToSchema};use utoipa_swagger_ui::SwaggerUi;
#[derive(Serialize, Deserialize, ToSchema)]struct User { id: u32, name: String,}
#[utoipa::path( get, path = "/users/{id}", responses( (status = 200, description = "User found", body = User), (status = 404, description = "User not found") ), params( ("id" = u32, Path, description = "User ID") ))]#[get("/users/{id}")]async fn get_user(id: web::Path<u32>) -> impl Responder { HttpResponse::Ok().json(User { id: id.into_inner(), name: "Alice".into(), })}
#[derive(OpenApi)]#[openapi(paths(get_user), components(schemas(User)))]struct ApiDoc;
#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(get_user) .service( SwaggerUi::new("/swagger-ui/{_:.*}") .url("/api-doc/openapi.json", ApiDoc::openapi()), ) }) .bind("127.0.0.1:8080")? .run() .await}Aide – Kontrolle und Typisierung
Aide (ehemals paperclip) bietet einen anderen Ansatz: Sie beschreiben die Spezifikation explizit über Strukturen und Funktionen, nicht über Makros. Dies gibt mehr Kontrolle, erfordert aber mehr Code. Die Bibliothek unterstützt Axum und Actix-web.
Besonderheiten:
- Saubere Typisierung – alle Beschreibungen werden über enum und struct definiert.
- Integrierte Schemavalidierung bei der Generierung.
- Unterstützung von verschachtelten Modellen und Kompositionen (allOf, oneOf).
- Flexibilität: Jeder Aspekt der Spezifikation kann überschrieben werden.
Beispiel mit Axum:
use aide::{axum::ApiRouter, openapi::OpenApi};use axum::{routing::get, Json, Router};use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]struct Pet { id: u32, name: String,}
async fn list_pets() -> Json<Vec<Pet>> { Json(vec![Pet { id: 1, name: "Rex".into(), }])}
async fn api_docs() -> Json<OpenApi> { let mut api = OpenApi::default(); api.info.title = "Pet Store API".into(); api.info.version = "1.0.0".into(); Json(api)}
#[tokio::main]async fn main() { let app = ApiRouter::new() .route("/pets", get(list_pets)) .route("/api-doc", get(api_docs));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap()) .serve(app.into_make_service()) .await .unwrap();}