axum という Web Framework を触った

# はじめに

Rust を書きたいと言って入ってきたけど、  
入社してから事あるごとに Rustを採用せずに仕事をしている人。

半年くらい前に触ったFramework 、 axum について話します。

---

# Rust
Rust は良いぞ！

- 強力な型システム
- 素晴らしい例外処理
- 美しい言語仕様

---

# Rust で Webサーバ

弊社では Actix Web を利用している。

- １年以上前にチュートリアルを触った
- 好きではないフレームワーク

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801258/b0650d20-73a5-1886-ffd6-6d586860fb96.png)

---

# Actix Web が嫌いだった理由

理由は一つです。


---

```rust
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(hello)
            .service(echo)
            .route("/hey", web::get().to(manual_hello))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}
```

---

`actix_web::main` => `actix_rt`

---

なんだこいつ？

---

ドキュメントを追うと、 tokio をラップしたランタイムらしい。  
(https://docs.rs/actix-rt/latest/actix_rt/)

シングルスレッドによるパフォーマンスが云々...（そこまで求めていないのですが ^^; ）

しかし、 **他の tokio のライブラリを使おうとするとこける。**


---

なぜ、 Web Framework が非同期ランタイムを独自に持っているんだ？

---

素直に tokio を使いたい...

---

# そこで [axum](https://github.com/tokio-rs/axum) ！

tokio 御本家が公開。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/801258/f50a2f56-e75e-c1be-9d27-6325a58590ef.png)

---

公開時に触った（2021年の8月くらい？）。

そのときの感想は「良い！！」

マクロを使わないのは非常に取り回しが良かったし、　　
変なライブラリの依存関係問題に遭遇しなかった。

---

# 会社で見つけたコードの簡略版

Actix Web の場合

```rust
// Actix Web はマクロを使うのが基本だった...
#[get("/users/{id}")]
async fn handle(
    Data(pool): Data<PgPool>,
    Path(id): Path<Uuid>,
) -> Result<HttpResponse, ApiError> {
    Ok(HttpResponse::Ok().json(execute(pool, id).await.to_json()))
}

// マクロが邪魔なので、テスト用に別関数が切り出されている...
pub async fn execute(
    pool: &PgPool,
    id: Uuid,
) -> Result<User, ApiError> {
   Ok(UserRepository::new(&pool).find_by_id(id).await?.into())
}

#[cfg(test)]
mod tests {
    // なぜ Web Framework が非同期のランタイムを...以下略
    #[actix_rt::test]
    async fn test_admin_only() -> Result<(), ApiError> {
        let pool = my_postgres::create_pool().await.unwrap();

        // execute って何だっけ？（テスト用の関数）
        let result = execute(&pool, Uuid::new_v4()).await;
        ...
    }
}
```

---

# axum 触ったころに書いてたコード。

axum の場合

```rust
// ルータ
pub fn make_router() -> Router {
    Router::new()
        .route("/users/:id", get(get_user))
} 


// ハンドラはマクロで包まない。
pub async fn get_user(
    Extension(pool): Extension<PgPool>,
    Path(id): Path<Uuid>,
) -> Result<Json<User>, crate::Error> {
    let user: User = UserRepository::new(&pool).find_by_id(id).await?.into();

    Ok(Json(user))
}

#[cfg(test)]
mod tests {
    // tokio!! これが使いたかった...
    #[tokio::test]
    async fn test_get_user() {
        let pool = my_postgres::create_pool().await.unwrap();

        // マクロを使っていないので、まるで普通の関数のようにテストできる。
        let created_user = api::v1::post_user(
            Extension(pool.clone()),
            Json(serde_json::from_value(json!({"name": "taro".to_owned()})).unwrap()),
        )
        .await
        .unwrap();

        let user = api::v1::get_user(Extension(pool), Path(created_user.id))
            .await
            .unwrap();

        assert_eq!(*user, *created_user);
    }
}
```

---

# 言いたかったこと

まるで普通の関数のようにAPIのテストができるのは重要かな？と思っています。

```rust
let user: User = *api::v1::get_user(Extension(pool), Path(id))
    .await
    .unwrap();
```

例えば、Rust で書いたバッチのcrate のテストのときに、   
( dev-dependencies で) API サーバの crate からAPIのハンドラを持ってくれば、  
実際の処理の流れを宣言的に書いていくことで、一連のテストを記述可能になる！

---

他のいくつかの機能を組み合わせれば、 Rust は非常に強力なテスト環境/動作確認環境を構築できる！

---

Rust は最高だぜ！

---

# おまけ

---

最近の Actix Web を見に行ったら、 axum と同じことができそうだった...

時の流れは早い...