Swagger/OpenAPI Generation for Rust: Utoipa and Aide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Swagger/OpenAPI Generation for Rust: Utoipa and Aide



In modern web development with Rust, creating a documented API is not a luxury, but a necessity. Swagger (OpenAPI) allows you to automatically generate interactive documentation, client SDKs, and test consoles. In the Rust ecosystem, two tools hold leading positions: Utoipa (macro-based) and Aide (with an emphasis on typing and manual control). Let's examine them in detail.



Utoipa — Macros for OpenAPI 3.0



Utoipa is a library that uses attribute macros (#[utoipa::path]) to automatically generate OpenAPI specifications. It integrates tightly with popular frameworks: Actix-web, Axum, Rocket, Warp.



Features:

  • Full support for OpenAPI 3.0 (paths, components, security schemes).
  • Automatic type extraction from Rust structs (using #[derive(ToSchema)]).
  • Generation of JSON/YAML files for Swagger UI.
  • Built-in support for actix-web (via utoipa-swagger-ui).
  • Low entry barrier — just add macros.


Example with 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 — Control and Typing



Aide (formerly paperclip) offers a different approach: you explicitly describe the specification through structs and functions, rather than through macros. This gives more control but requires more code. The library supports Axum and Actix-web.



Features:

  • Clean typing — all descriptions are defined via enums and structs.
  • Built-in schema validation during generation.
  • Support for nested models and compositions (allOf, oneOf).
  • Flexibility: you can override any aspect of the specification.


Example with 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();}

Recommendations