⚙️

Rust | Web フレームワーク Axum で REST API を作る

2022/08/30に公開

はじめに

Rust で Web アプリケーションを作成し render.com で公開してみたので、記事にまとめてみたいと思います。
Web フレームワークは Axum 、DBには MongoDB を採用しています。

Axum は公式の examples が充実していますが、検索してもなかなか実装例などをの情報がヒットしません。
また、 Rust と MongoDB の参考記事も少なく、苦労しました。

この記事や拙いソースコードが、少しでも誰かのお役に立てられれば幸いです!!!

作成したWebアプリケーション

mountix API

作成したのは、山の情報を取得できる REST API です。

運営するブログに LP を作成してあるので、気になる方はご覧ください。

使い方などを紹介していますので、
Postman、Insomnia 等のHTTPクライアントツールで遊んでいただけると嬉しいです😁

https://dottrail.codemountains.org/lp/mountix-api/

APIのドキュメント も公開しています。

Github

ソースコード一式です。
将来、Rust で実務経験を積みたいエンジニアが開発を進めています笑

小出しに実装例を見るよりも伝わりやすい部分もあるかと思いますので、のっけておきます。

https://github.com/codemountains/mountix

技術スタック

Axum

tokio チーム製の Web サーバーを実装するためのクレートです。
actix-web を採用するか迷いましたが、今回は Axum を採用しました。

Axum の特徴を簡単に挙げておきます。

  • 非同期ランタイムに tokio が使われているため、tokio のバージョン管理から解放されること
  • マクロレスで実装されていること

MongoDB

データベースには MongoDB を採用しました。
ドライバーは MongoDB 公式の mongodb クレートを使用しました。

また、MongoDB を採用した理由は以下です。

  • Geospatial queries があり、地理空間検索が容易に実装できること
  • GET のエンドポイントのみを提供する REST API であり、特にデータの整合性に注意する必要もなくため、RDSであるメリットがないこと
  • tokio-runtime を選択できること
  • MongoDB Atlas を利用すれば、低コストで運用できること

アーキテクチャ

レイヤードアーキテクチャ

レイヤードアーキテクチャでの実装については、以下の記事をとても参考にさせていただきました。
@helloyuki_さんは「実践 Rustプログラミング入門」の著者です。
ぜひチェックしてみてください!

https://blog-dry.com/entry/2021/12/26/002649

実装おけるポイント

1. Axum

Router と nest

Router を定義して、.nest() でネストさせることができます。

https://github.com/codemountains/mountix/blob/main/mountix-driver/src/startup/mod.rs#L29-L36

パスパラメータとクエリパラメータ

ハンドラの引数に PathQuery を渡すことで値を受け取ることができます。
serde の Deserialize が実装された構造体であれば、axum が裏で良しなに変換してくれます。

https://github.com/codemountains/mountix/blob/main/mountix-driver/src/routes/surrounding_mountain.rs#L15-L47

2. mongodb

ドキュメントの構造体を定義しておく

ドキュメントを表す構造体を定義し Deserialise を実装して collectionで型指定しておけば、serde が良しなに変換してくれます。

https://github.com/codemountains/mountix/blob/main/mountix-adapter/src/model/mountain.rs#L9-L21

https://github.com/codemountains/mountix/blob/main/mountix-adapter/src/repository/mountain.rs#L14-L23

検索時の Filter Document を動的に生成する

この実装例が見当たらず、苦労しました。
doc! マクロを使えば、Document そのものは簡単に生成できます。

クエリパラメータで受け取った nameprefecturetag があれば、条件に追加するといった処理になっています。

https://github.com/codemountains/mountix/blob/main/mountix-adapter/src/model/mountain.rs#L58-L95

Geospatial queries を使用する

$nearSphere は中心点の座標とそこからの距離(m)を引数で渡して、その範囲に存在するドキュメントを取得できます。
$near$nearSphere がありますが、距離の計算が平面か球体の違いがあります。

https://github.com/codemountains/mountix/blob/main/mountix-adapter/src/model/surrounding_mountain.rs#L56-L68

$box は長方形の地理空間上に存在するドキュメントを取得できます。
[[左下隅の経度, 左下隅の緯度], [右上隅の経度, 右上隅の緯度]]の形式で指定します。

https://github.com/codemountains/mountix/blob/main/mountix-adapter/src/model/mountain.rs#L108

mongodbクレートの使い方はブログでも紹介しているので、良ければチェックしてください!

3. render.com

Heroku の無料プランが廃止されることとなりました。
無料枠狙いnの方々は、render.com に流れていくのではないでしょうか。
Rust で実装した Web アプリケーションを手軽に公開できる、良い選択肢だと思います。

ただ、やってみた系の記事もまだまだ少ないのが現状です。
Rust のデプロイ方法の参考記事を探すのにも苦労しました。

HOST と PORT を環境変数で管理できるように

render.comにデプロイする際、以下の制約があります。

  • HOST0.0.0.0
  • PORT は環境変数で受け取れるようにする

そのため、HOSTPORTどちらも環境変数で管理できるようにしておくと良いかと思います。

ローカルでのデバック時には 127.0.0.1:8080 で起動していますが、
render.comの .env では HOST=0.0.0.0を定義し、PORT は未定義にしてあります。

https://github.com/codemountains/mountix/blob/main/sample.env#L2-L3

https://github.com/codemountains/mountix/blob/main/mountix-driver/src/startup/mod.rs#L52-L69

Health check 用のエンドポイントを実装しておく

今回、一番ハマったポイントかもしれません。
よく読めば、すんなり解決できたはずなのですが...笑

render.com で Rust の Web アプリケーションをデプロイする際、特に注意する点はありません。
以下のようにコマンド設定するだけで基本的にはOKなはずです。

  • Build Command: $ cargo build --release
  • Start Command: $ cargo run --release

ただし、私の場合は Health Check Pathを変更する必要がありました。(デフォルトは /
理由は、Health Check Path をネストさせており、ルート URL にアクセスすると404が返るためです。

Start Command 実行後にこの Health Check Path にリクエストが投げられ、200 OK のレスポンスが返るとデプロイ成功となるようです。
デプロイ時のログを見ていると、成功応答があるまで数分間、404エラーが出続けていました笑

ちなみに、200 OKを返せと書いてありますが、204 No Contentでも問題ありませんでした。
おそらくですが、200番台の成功応答が返ればよいと思われます。(試してはないです)

https://github.com/codemountains/mountix/blob/main/mountix-driver/src/startup/mod.rs#L19-L21

https://github.com/codemountains/mountix/blob/main/mountix-driver/src/routes/health.rs#L13-L28

その他・所感

デプロイ時間

無料プランで稼働させているので、デプロイに10分弱かかります。
初回のデプロイはキャッシュが効かないので、もう少し時間がかかった記憶があります。

短くしたい場合は、有料プランを検討した方が良いでしょう!

.env の管理が簡単

管理画面に Secret Files という設定項目があり、.env という名前でファイルを作成可能です。

もちろん、環境変数を個別に設定することも可能です。
.env で管理する環境変数が多い場合は非常に便利だなと感じました。

ファイル名は自由に設定できるので、.env 以外の設定ファイルをバージョン管理下に置かずに管理する場合も、活用できる機能だと思います。

カスタムドメイン

カスタムドメインについては、設定はとても簡単でした。

render.com で Web Service を公開すると、https://{your-app-name}.onrender.comというURLが発行されます。
お名前.com などの DNS レコード設定で、CNAME{your-app-name}.onrender.comで登録すればOKです。

まとめ

エラーハンドリングやマイグレーション、テストコードなど課題がまだまだあるので、今後も開発を進めていきたいと思います。

また、render.com は便利ですが、
実務で扱うとなるとAWSにはなるかなと思うので ECS で動かせるくらいにはしたいです。
AWSでの常時稼働はお財布と相談ですね...

ひと段落したら、次は MongoDB/mongodb を PostgreSQL/sqlx あたりに置き換えて、別の Web アプリなんかも開発したいです。

Discussion