Axumとは?
Axumは、Rustの非同期ランタイムであるTokioチームが開発したWebフレームワークです。シンプルなAPIと高いパフォーマンスを兼ね備えており、近年Rustコミュニティで急速に人気を集めています。
同じRust製WebフレームワークのActix-webと比べると、AxumはTokioエコシステムとの親和性が高く、towerミドルウェアをそのまま利用できる点が大きな特徴です。
この記事では、Axumを使った基本的なWebアプリケーションの作り方を、コードを交えながら順を追って説明します。
プロジェクトのセットアップ
まずCargoで新しいプロジェクトを作成し、必要な依存関係を追加します。
# Cargo.toml
[package]
name = "axum-sample"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
axum:Webフレームワーク本体tokio:非同期ランタイム(fullフィーチャーですべての機能を有効化)serde/serde_json:JSONのシリアライズ・デシリアライズに使用
はじめてのAxumサーバー
最小構成のHTTPサーバーを書いてみましょう。
// src/main.rs
use axum::{routing::get, Router};
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello_handler));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
println!("サーバー起動: http://localhost:3000");
axum::serve(listener, app).await.unwrap();
}
async fn hello_handler() -> &'static str {
"Hello, Axum!"
}
cargo runを実行してブラウザでhttp://localhost:3000にアクセスすると、Hello, Axum!が表示されます。
ポイントは以下の3点です。
Router::new()でルーターを作成する.route(パス, HTTPメソッド(ハンドラ))でルートを登録する- ハンドラ関数は
async fnで定義する
ルーティングの基本
複数のルートを登録する方法を見てみましょう。
use axum::{routing::{get, post}, Router};
let app = Router::new()
.route("/", get(index_handler))
.route("/users", get(get_users))
.route("/users", post(create_user));
パスパラメータを受け取る
URLの一部を変数として受け取るにはPathエクストラクタを使います。
use axum::{extract::Path, routing::get, Router};
async fn get_user(Path(user_id): Path<u32>) -> String {
format!("ユーザーID: {}", user_id)
}
let app = Router::new()
.route("/users/:id", get(get_user));
/users/42にアクセスするとユーザーID: 42が返ります。PathがURLパラメータを自動的に型変換してくれるため、文字列のパースを自分で書く必要がありません。
JSONリクエスト・レスポンスを扱う
実際のAPIでよく使うJSONの送受信を実装してみます。
use axum::{extract::Json, routing::post, Router};
use serde::{Deserialize, Serialize};
// リクエストの構造体
#[derive(Deserialize)]
struct CreateUserRequest {
name: String,
email: String,
}
// レスポンスの構造体
#[derive(Serialize)]
struct UserResponse {
id: u32,
name: String,
email: String,
}
async fn create_user(
Json(payload): Json<CreateUserRequest>,
) -> Json<UserResponse> {
// 実際はDBへの保存処理などが入る
let user = UserResponse {
id: 1,
name: payload.name,
email: payload.email,
};
Json(user)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/users", post(create_user));
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
Jsonエクストラクタを引数に使うと、リクエストボディのJSONを自動的にデシリアライズしてくれます。戻り値にJson(user)を返すと、自動的にJSONにシリアライズしてレスポンスを返します。
curlで動作確認してみましょう。
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name": "田中太郎", "email": "tanaka@example.com"}'
レスポンス例:
{
"id": 1,
"name": "田中太郎",
"email": "tanaka@example.com"
}
クエリパラメータを受け取る
/search?keyword=rust&limit=10のようなクエリパラメータはQueryエクストラクタで受け取れます。
use axum::{extract::Query, routing::get, Router};
use serde::Deserialize;
#[derive(Deserialize)]
struct SearchParams {
keyword: String,
limit: Option<u32>,
}
async fn search(Query(params): Query<SearchParams>) -> String {
let limit = params.limit.unwrap_or(20);
format!("キーワード: {}、件数上限: {}", params.keyword, limit)
}
let app = Router::new()
.route("/search", get(search));
Option<u32>にすることで、limitパラメータが省略された場合も正常に動作します。
まとめ
この記事では、Axumを使ったRust Webアプリケーションの基本を解説しました。
| 機能 | 使うもの |
|---|---|
| ルーティング | Router::new().route(...) |
| パスパラメータ | extract::Path |
| クエリパラメータ | extract::Query |
| JSONリクエスト | extract::Json |
| JSONレスポンス | Json(value)を返す |
Axumはエクストラクタという仕組みでハンドラの引数を柔軟に扱えるのが特徴です。型安全なRustの恩恵を受けながら、直感的にWebAPIを構築できます。
次のステップとして、ミドルウェア・エラーハンドリング・データベース連携なども試してみてください。