Actix Web に入門して REST API サーバーを作る
はじめに
今回は Rust の Web フレームワークとしてかなり有名である、Actix-Webのチュートリアル部分をやっていこうと思います。
Rust には Web フレームワークが多く存在しますが、その中でも比較的歴史があり、第一線で活躍するフレームワークの一つが Actix-Webです。
チュートリアル的な内容ですので、これから Actix-Webを触る方の助けになればと思います。
他のWebフレームワークとの比較は以下の記事が参考になりました。
🔻Actix Web
プロジェクト作成
事前に Rust が入っていることが前提で進めていきます。
以下のコマンドで、 hello-worldプロジェクトを作成する。
cargo new hello-world
cd hello-world
以下のコマンドを実行し、actix-webのクレートを追加します。
cargo add actix-web
Cargo.tomlが以下のように更新されていることを確認します。
[package]
name = "hello-world"
version = "0.1.0"
edition = "2021"
[dependencies]
actix-web = "4.8.0"
※2024/07/15時点で最新バージョンが 4.8.0でした。一旦現時点での最新バージョンでやっていきます。
念のため、私は cargo runを実行しておきました。
cargo run
Hello, world!
REST API サーバーを構築する
メインの実装
main.rsを以下のように編集します。
use actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
async fn manual_hello() -> impl Responder {
HttpResponse::Ok().body("Hey there!")
}
#[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
}
上記のコードでは、3つのエンドポイントが実装されているはずです。
- GET
/ - POST
/echo - GET
/hey
それぞれのエンドポイントにアクセスして、どのような処理が行われているのかをざっくりとみていければと思います。
これで、 cargo runを実行します。
動作の確認
GET /
任意のブラウザで、http://127.0.0.1:8080/にアクセスすると、Hello world!が出力されていることを確認できました。

main関数の中で以下の部分が呼び出されて、Hello world!が出力される仕組みですね。
関数の前に、メソッドとパスがそのまま記述されていることがわかります。
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
GET /hey
こちらも、任意のブラウザでhttp://127.0.0.1:8080/heyにアクセスすると、Hey there!と出力されることを確認することができました。

これは、main関数の中にメソッドとパスが記述されているタイプで、呼び出し先の関数のみを定義する方法のようです。
ルーティングの方法に2つの実装方法があるようですね!
async fn manual_hello() -> impl Responder {
HttpResponse::Ok().body("Hey there!")
}
POST /echo
POST エンドポイントですので、任意のクライアント実行ツールを使い、
http://127.0.0.1:8080/echoにアクセスします。(私は Thunder Clientという VScode の拡張機能を使用します。)
Bodyには任意の JSONを入れることで、そのまま応答として返されます。

こちらも、Hello, world!出力のパターンと同様に、echo関数の前にメソッドやパスを定義していることがわかります。
#[post("/echo")]
async fn echo(req_body: String) -> impl Responder {
HttpResponse::Ok().body(req_body)
}
追加の実装をしてみる
チュートリアルのコピペでは楽しくないので、
簡単ですが、ヘルスチェックのエンドポイントを実装していきます!
GET /healthにアクセスすると以下のような JSONが帰ってくることとして実装します。
{
"error": false, // エラーかどうか
"date_time": "2024-07-15 18:57:13" // 現在の日時
}
以下のコマンドで、serdeと chronoを追加します。
cargo add serde --features derive
cargo add chrono
main.rsに以下のコードを追加してみます。
use chrono::Local;
use serde::Serialize;
#[derive(Serialize)]
struct HealthResponse {
error: bool,
date_time: String,
}
#[get("/")]
async fn health() -> impl Responder {
let response = HealthResponse {
error: false,
date_time: Local::now().format("%Y-%m-%d %H:%M:%S").to_string(),
};
web::Json(response)
}
さらに、ルーティングの部分に以下を追加。
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(hello)
.service(echo)
// 新しく追加
.service(health)
.route("/hey", web::get().to(manual_hello))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
ここまでできたら、再度 cargo runを実行し確認してみます。
ブラウザか、APIクライアントツールで http://127.0.0.1:8080/healthにアクセスします。
以下のように、応答が返ってくることが確認できました!

おわりに
いかがでしたでしょうか。
チュートリアルと、追加のエンドポイントの実装を行ってみました。
贔屓目なしでもとても簡単に REST API サーバーが構築できると感じました。
ルーティング部分が非常に簡易的だと感じました。
.route()、.service()のどちらでルーティングする方が多いのでしょうね。
ここはプロジェクト文化によって変わるところかもしれませんが、私は後者の方が良いかなと思いました。
また Actix-Webのあれこれを記事化できればと思います!
では。
Discussion