Rust+ActixWeb+Diesel で Realworld プロジェクトを登録した話

ども、Nash です。

この記事は、学習用に作成した Rust による Realworld プロジェクトが完成して公式に登録されたので、そこまでに学んだことなどをまとめた記事です。

はじまり

仕事ではフロントエンド寄りのフルスタックエンジニアとして働いているのですが、前々から Rust が好きで個人的に学習していました。

その一環として Realworld プロジェクトに ActixWeb のプロジェクトが登録されていないことに気付いたので、いい機会なので自分で作成して登録してもらうところまでを目標にプロジェクトを作ってみました。

Realworld プロジェクトとは

GitHub - gothinkster/realworld: “The mother of all demo apps” — Exemplary fullstack Medium.com clone powered by React, Angular, Node, Django, and many more 🏅

Realworld プロジェクトとは、 任意の言語・バックエンド・フロントエンドの技術を使ったアプリケーションを作るときに、実際に使えるレベルの複雑性・大きさ・機能性を含んだ example なアプリケーションを OSS として作るプロジェクトです。

このプロジェクトで作成するアプリケーションは Medium のクローンアプリとなり、それらのフロントエンド・バックエンドの仕様がすでに決められています。

このプロジェクトの仕様に沿って、Medium クローンのアプリを作ることによってこのようなこと特徴があります。

  • この仕様を満たすフロント・バックエンドを作成すれば誰でこのプロジェクトへ登録できる
  • どのフロントエンド x バックエンドの組み合わせでもアプリケーションを動かせられる
  • 同一仕様のアプリケーションに対して、フロントエンド同士またはバックエンド同士で比較することができる

詳細は公式のブログポストをどうぞ

Introducing RealWorld 🙌. 🏅 Exemplary fullstack blog apps… | by Eric Simons | Medium

選んだ技術スタック

今回、自分が選んだ技術スタックとしてはこれらのスタックになります。

  • 言語:Rust
  • FW:actix-web
  • ORM:Diesel

また他にもこれらのスタックも使ってます。

  • CI: GitHub Actions
  • E2E: Postman(公式が作った Postman の script を利用)
  • Container: Docker
  • DB: PostgreSQL

所要期間

おおよその骨組みを作るのに 1 ヶ月くらいで、そこから細かくすべての API が正しく動くようにしたり E2E をするのに 1~2 ヶ月くらいかかりました。

  • 2021/11 ごろ:プロジェクトの開始
  • 2021/12 ごろ:だいたいの骨組みが終わった
  • 2022/01~04:細かい API のリファクタや E2E を通すために Fix を地道に勧めた
  • 2022/05 ごろ:最終的にプロジェクトを登録

稼働時間が一日 1h もない日もあれば、それなりに稼働した時期もありますし、そもそも登録申請自体は実際はもっと早くしても良かったかと思います。

なにはともあれ、E2E がすべて通った状態で登録申請を行ったところ無事に登録されました。

学んだこと

このプロジェクトを作るにあたって学んだことなどをざっくりと書いていきます。

FW よりも ORM のほう技術選定のインパクトが大きい

今回作成したプロジェクトはバックエンド側の機能かつ軽量 FW である ActixWeb を利用しました。そのため、ORM は別で選定していて Diesel を利用しています。

今まで、バックエンドにおける技術選定においては「どの FW を使っているか?」を注視することが多かったですが、今回のこのプロジェクトで気付いたのは特に軽量 FW の場合は、FW よりも ORM のほうが遥かにバックエンド内のロジックにおいて大きなウエイトを占めている、という点です。

フルスタック FW においては、例えば Rails における Active Record など、ORM が FW に内包しているのであまり別々に意識することがないのかもしれないですが、FW よりも ORM のほうが実は重要なのではないかな、と気付かされました。

Diesel がしんどい

Active Recordl に慣れていると Diesel がめちゃくちゃ大変に感じます。

具体例のドキュメントがかなり少ないこともあり、実現方法がわからなかったり自分のケースだとちょっとでも複雑なクエリになった途端にどう書けばいいのかすぐに詰まってかなり時間を溶かしました。

結局、1つのクエリでうまいこと fetch する方法がわからないので複数回、または N+1 でクエリを発行してる箇所がありイケてない状態ですがひとまず動くことを優先しているので仕方がない、と割り切って作りきりました。

一度作ったあとは静的型付けがゴリゴリ効く

Rust の特徴として、作り始めは型・ライフタイム・参照周りが理由でコンパイルができない苦しみがすごいです。コンパイルできずに苦しむ Rust あるあるですね。

ただ、一度どうにか作れればそこからちょいっと修正するときは、実機テストをそこまでしないでも型システムのおかげでかなりの動作を保証してくれるのでめちゃくちゃ楽になります。

メモリ効率を捨ててコンパイルできるものをまずは作り切る

ひとまずコンパイルできるものを優先して、メモリ効率は度外視して作りきるのが最初は大事でした。

具体的には、最初は Clone しまくって OK でとりあえずコンパイルを通して動くものを作り切ります。そのあとで Clone をしてるところを FIX していったりイケてない箇所を修正していくほうが遥かに楽に進められます。

{}で読みやすいコードに分割しておけて便利

Rust の書き方として{}で囲むことで文にすることができるのがものすごく便利でした。

  • 「関数に切り出すまでもない」
  • 「作り途中なので見通しが曖昧なのであとで関数に切り出そうと考えている」

こういったときに、スコープを切って値を return したり、スコープを制限して use することができるのも素敵でした。

let (user, article) = {
	use diesel::*; // use のスコープ範囲が狭まる
	let user = fetch_user(conn, user_id);
	let article = diesel::filter(
			articles::id.eq(article_id)
		)
		.get_result::<Article>()?;
  (user, article)
}

POSTMAN による E2E があるので TDD で進めると良い

Realworld プロジェクトを作る上でのナレッジですが、Realworld プロジェクトでは POSTMAN による E2E のスクリプトが提供されているので、このテストを早い段階で CI に噛ましてあげて TDD で進めるべきでした。

この CI が動くようになってからは、リグレッションテストがものすごく楽になりますし Rust の強力な型機能のおかげで予想外の不具合が起きるケースが劇的に減った印象です。

まとめ

雑なまとめとしてはこんなところです。

  • Rust において、なによりまずはコンパイルすることを優先して書ききる
  • バックエンド技術選定において、FW よりも ORM の選定に気をつける

なにはともあれ、ひとまずプロジェクトの登録まで完了したのでこのプロジェクトは必要に応じて細々とメンテをしていきます。

それとは別に、Rust で仕事をしたり次のプロジェクトをなにか作ったり OSS を触ったりしたいなーと考えてる次第です。

現場からは以上です。

GitHub - snamiki1212/realworld-v1-rust-actix-web-diesel: RealWorld written in Rust and using ActixWeb+Diesel