Swagger/OpenAPI генерация для Rust: Utoipa и Aide
В современной веб-разработке на Rust создание документированного API — не роскошь, а необходимость. Swagger (OpenAPI) позволяет автоматически генерировать интерактивную документацию, клиентские SDK и тестовые консоли. В экосистеме Rust лидирующие позиции занимают два инструмента: Utoipa (на основе макросов) и Aide (с акцентом на типизацию и ручной контроль). Рассмотрим их подробно.
Utoipa — макросы для OpenAPI 3.0
Utoipa — это библиотека, которая использует атрибуты-макросы (#[utoipa::path]) для автоматической генерации OpenAPI спецификации. Она плотно интегрируется с популярными фреймворками: Actix-web, Axum, Rocket, Warp.
Особенности:
- Полная поддержка OpenAPI 3.0 (paths, components, security schemes).
- Автоматическое извлечение типов из структур Rust (с помощью
#[derive(ToSchema)]). - Генерация JSON/YAML файла для Swagger UI.
- Поддержка
actix-webиз коробки (черезutoipa-swagger-ui). - Низкий порог входа — достаточно добавить макросы.
Пример с 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 — контроль и типизация
Aide (ранее paperclip) предлагает другой подход: вы явно описываете спецификацию через структуры и функции, а не через макросы. Это даёт больше контроля, но требует большего объёма кода. Библиотека поддерживает Axum и Actix-web.
Особенности:
- Чистая типизация — все описания задаются через enum и struct.
- Встроенная валидация схем при генерации.
- Поддержка вложенных моделей и композиций (allOf, oneOf).
- Гибкость: можно переопределить любой аспект спецификации.
Пример с 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();
}
Сравнение Utoipa vs Aide
- Простота: Utoipa выигрывает за счёт макросов — достаточно аннотировать хендлеры. Aide требует ручного описания.
- Контроль: Aide даёт полный контроль над спецификацией, что полезно для сложных API с кастомными валидациями.
- Эк