utoipa-axum について
utoipa-axumは、Axumで構築したRust REST APIをコード中心で文書化するスキルです。utoipaでOpenAPI 3.1のスキーマとパスを定義し、Scalarで操作可能なドキュメントを提供する方法を扱います。
主な機能
- 01ToSchemaでスキーマを導出
- 02ハンドラーにパス情報を注釈
- 03パスとスキーマをOpenAPIに統合
- 04クエリパラメーターと列挙型を記述
- 05Scalarで操作可能なドキュメントを提供
活用シーン
- 01Axumのリクエスト型とレスポンス型を文書化
- 02ハンドラーからOpenAPI仕様を生成
- 03操作可能なAPIドキュメントを公開
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ suryavirkapur/ skills --skill utoipa-axumインストール後、Claude Code で/utoipa-axumを入力して読み込まれたか確認します。
同梱ファイルreferences/api_reference.mdreferences/patterns.md
詳細
utoipa + Axum による OpenAPI
utoipa は、Rust の REST API 向けにコンパイル時の OpenAPI ドキュメントを提供します。コードファーストのアプローチにより、ハンドラーと型にアノテーションを付けるだけで、OpenAPI 3.1 仕様が自動的に生成されます。
主な概念
- ToSchema — リクエスト/レスポンスのボディ型に対する Derive
- #[utoipa::path] — パス、メソッド、パラメーター、レスポンスを指定してハンドラーにアノテーションを付けます
- OpenApi — すべてのパスとスキーマをまとめて仕様を作成します
- Scalar — インタラクティブな API ドキュメントを提供します
Cargo.toml の設定
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["serde", "v4"] }
chrono = { version = "0.4", features = ["serde"] }
# utoipa with axum integration
utoipa = { version = "5", features = ["axum_extras", "uuid", "chrono"] }
utoipa-scalar = { version = "0.3", features = ["axum"] }
ToSchema — リクエスト/レスポンス型
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use uuid::Uuid;
#[derive(Debug, Deserialize, ToSchema)]
pub struct CreateUserRequest {
/// User's email address
pub email: String,
/// Password (min 8 characters)
#[schema(min_length = 8)]
pub password: String,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct UserResponse {
pub id: Uuid,
pub email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ErrorResponse {
pub error: String,
}
#[utoipa::path] — ハンドラーのドキュメント化
use axum::{extract::{Path, State}, http::StatusCode, Json};
#[utoipa::path(
get,
path = "/api/users/{id}",
tag = "users",
params(("id" = Uuid, Path, description = "User ID")),
responses(
(status = 200, description = "User found", body = UserResponse),
(status = 404, description = "Not found", body = ErrorResponse)
),
security(("bearer" = []))
)]
pub async fn get_user(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<UserResponse>, (StatusCode, Json<ErrorResponse>)> {
// handler logic
}
#[utoipa::path(
post,
path = "/api/users",
tag = "users",
request_body = CreateUserRequest,
responses(
(status = 201, description = "Created", body = UserResponse),
(status = 400, description = "Bad request", body = ErrorResponse)
)
)]
pub async fn create_user(
State(state): State<AppState>,
Json(req): Json<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), (StatusCode, Json<ErrorResponse>)> {
// handler logic
}
OpenApi — すべてをまとめる
use utoipa::openapi::security::{Http, HttpAuthScheme, SecurityScheme};
use utoipa::{Modify, OpenApi};
#[derive(OpenApi)]
#[openapi(
info(
title = "My API",
version = "1.0.0",
description = "API description"
),
tags(
(name = "users", description = "User management"),
(name = "health", description = "Health checks")
),
paths(
get_user,
create_user,
health_check
),
components(
schemas(
CreateUserRequest,
UserResponse,
ErrorResponse
)
),
modifiers(&SecurityAddon)
)]
pub struct ApiDoc;
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
if let Some(components) = openapi.components.as_mut() {
components.add_security_scheme(
"bearer",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
);
}
}
}
Scalar UI の提供
use axum::{routing::get, Router};
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable};
pub fn create_router(state: AppState) -> Router {
Router::new()
.route("/health", get(health_check))
.route("/api/users", get(list_users).post(create_user))
.route("/api/users/{id}", get(get_user).put(update_user).delete(delete_user))
// Scalar UI at /api/docs
.merge(Scalar::with_url("/api/docs", ApiDoc::openapi()))
.with_state(state)
}
IntoParams — クエリパラメーター
use utoipa::IntoParams;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PaginationParams {
/// Page number (1-indexed)
#[param(minimum = 1, default = 1)]
pub page: Option<u32>,
/// Items per page
#[param(minimum = 1, maximum = 100, default = 20)]
pub per_page: Option<u32>,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct UserFilters {
/// Filter by email contains
pub email: Option<String>,
/// Filter by status
pub status: Option<UserStatus>,
}
#[utoipa::path(
get,
path = "/api/users",
tag = "users",
params(PaginationParams, UserFilters),
responses((status = 200, body = Vec<UserResponse>))
)]
pub async fn list_users(
Query(pagination): Query<PaginationParams>,
Query(filters): Query<UserFilters>,
) -> Json<Vec<UserResponse>> {
// handler logic
}
スキーマ内の列挙型
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UserStatus {
Active,
Inactive,
Pending,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Event {
UserCreated { user_id: Uuid },
UserDeleted { user_id: Uuid, reason: String },
}
よく使われるパターン
エラー処理、認証ミドルウェア、レスポンス型については、references/patterns.md を参照してください。
API リファレンス
ToSchema とパス属性のオプションについて詳しくは、references/api_reference.md を参照してください。
コメント
Markdown 対応