mcpField
U
utoipa-axum

utoipa-axum

github.com/suryavirkapur開発166 回インストール41 週間前に更新
リポジトリ

utoipa-axum について

utoipa-axumは、Axumで構築したRust REST APIをコード中心で文書化するスキルです。utoipaでOpenAPI 3.1のスキーマとパスを定義し、Scalarで操作可能なドキュメントを提供する方法を扱います。

主な機能

  1. 01ToSchemaでスキーマを導出
  2. 02ハンドラーにパス情報を注釈
  3. 03パスとスキーマをOpenAPIに統合
  4. 04クエリパラメーターと列挙型を記述
  5. 05Scalarで操作可能なドキュメントを提供

活用シーン

  1. 01Axumのリクエスト型とレスポンス型を文書化
  2. 02ハンドラーからOpenAPI仕様を生成
  3. 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 仕様が自動的に生成されます。

主な概念

  1. ToSchema — リクエスト/レスポンスのボディ型に対する Derive
  2. #[utoipa::path] — パス、メソッド、パラメーター、レスポンスを指定してハンドラーにアノテーションを付けます
  3. OpenApi — すべてのパスとスキーマをまとめて仕様を作成します
  4. 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 を参照してください。

コメント