Die Swagger UI als Rust-Modul neu verpacken

created: Dienstag, Mai 21, 2024

All our services need to have a Swagger UI for presenting its current OpenAPI file to the user. Since our backend is completely built with Rust, hosting this Swagger UI is also done through a Rust process.

So to make the Swagger UI a first degree citizen in the Rust ecosystem, we decided to re-package the Swagger-UI as Rust module. This way, dependabot can pick up update to the underlying project und pull those dependencies into our projects.

This also has the benefit that whenever this dependency gets updates, we run our CI/CD pipeline to check whether the UI is still fine for us.

Wie es gebaut wird

Die Swagger UI wird über GitHub als Paket bereitgestellt. Daher ist die Ermittlung der aktuellen Version nur ein weiterer Aufruf der GitHub-API. Auf diese Weise können wir entscheiden, ob ein Update erforderlich ist oder nicht. Wenn eine neue Version veröffentlicht wurde, laden wir die minifizierten JS- und CSS-Dateien herunter und aktualisieren unser lokales Rust-Repo mit den neuen Abhängigkeiten.

Nachdem diese Dateien aktualisiert wurden, erhöhen wir auch die Modulversion auf dieselbe Version wie das GitHub-Release. Auf diese Weise stimmt die Cargo-Version immer mit der GitHub-Version überein.

Der gesamte für dieses Verfahren benötigte Code befindet sich in der GitHub-Action-Definition.

Wie man es verwendet

Die Verwendung dieses Crates ist recht einfach. Die statischen Ressourcen werden als axum-Routen bereitgestellt und können mit einer bestehenden Routendefinition zusammengeführt werden. Um diese Routen zu implementieren, müssen Sie ein Präfix für die Routen sowie eine API-Definition angeben. Die API-Definition kann entweder eine eingebettete YAML-Datei oder ein externer Link zur API-Definition sein.

Hier ein Beispiel, wie man das Crate mit einer eingebetteten OpenAPI-Definition verwendet.

use axum::Router;
use swagger_ui_dist::{ApiDefinition, OpenApiSource};

#[tokio::main]
async fn main() {
    let api_def = ApiDefinition {
        uri_prefix: "/api",
        api_definition: OpenApiSource::Inline(include_str!("petstore.yaml")),
        title: Some("My Super Duper API"),
    };
    let app = Router::new().merge(swagger_ui_dist::generate_routes(api_def));
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    println!("listening on http://localhost:3000/api");
    axum::serve(listener, app).await.unwrap();
}

crates.io-Link: https://crates.io/crates/swagger-ui-dist

GitHub-Repo: https://github.com/apimeister/swagger-ui-dist-rs/