2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiitanがほしい人の一人アドカレAdvent Calendar 2024

Day 24

RailsライクなRustのWebフレームワークLocoでCRUD処理を実装してみた

Posted at

読み飛ばしてください

どうも限界派遣SESのnikawamikanです。
アドカレ24日目です。

今回は、RustのWebフレームワークLocoを試してみましたが、公式チュートリアルをなぞっているだけなので気になる方は公式サイトを見るのをオススメします。

Locoとは

LocoとはRuby on RailsみたいにCLIで部品を生成して、Webアプリケーションを作成できるRustのWebフレームワークです。
名前の由来もRailsのlocomotiveから来ているようで、Railsのような使い勝手を提供してくれるようです。

しかし筆者はRailsを使ったことがないので、どれだけRailsっぽいのかはわかりませんが、CLIで部品を生成して、CRUDの実装が簡単にできるのは便利そうです。

なお、公式サイトによると、Node.jsの10倍速いらしいです。

環境構築

まずはRust環境ですが、例に漏れずDevContainerを使って環境構築しました。

開発コンテナの構成ファイルをGUIからポチポチ作っていくと、Rust & PostgreSQLの環境が用意されているので、これを利用します。

image-1.png

設定ファイルが出力されたら、以下のようなファイル構成になっていると思います。

.devcontainer
├── devcontainer.json
├── docker-compose.yml
├── Dockerfile
└── .env

devcontainer.jsonpostCreateCommandにLocoのインストールコマンドを追加します。
こうすることで、コンテナが起動したときに自動でLocoがインストールされるようになります。
dbのポートも開放しておくと便利かと思います。

devcontainer.json
{
	"name": "Rust and PostgreSQL",
	"dockerComposeFile": "docker-compose.yml",
	"service": "app",
	"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
	"forwardPorts": [
		"db:5432"
	],
	"postCreateCommand": "cargo install loco && cargo install sea-orm-cli",
}

また、Locoではデータベースを使って開発する場合、ローカルの開発環境のDB設定をpostgres://loco:loco@localhost:5432/myapp_developmentのように設定する必要があるので、.envファイルを作成しておきます。
POSTGRES_DBはアプリケーション名+_developmentになるので、適宜変更してください。

POSTGRES_USER=loco
POSTGRES_PASSWORD=loco
POSTGRES_DB=myapp_development
POSTGRES_HOSTNAME=localhost
POSTGRES_PORT=5432

あとは、コンテナを起動すれば自動で開発環境が構築されます。

チュートリアルに沿ってアプリケーションを作成

今回はチュートリアルに沿って色々ためしてみます。

プロジェクトの作成

コンテナが起動したら、アプリケーションを作成します。
コンソールから以下のコマンドを実行すると対話形式でアプリケーションの設定ができます。
4つの質問に答えるだけでアプリケーションを作成できます。

loco new

今回は以下のように設定しました。

✔ ❯ App name? · myapp
✔ ❯ What would you like to build? · Saas App with server side rendering
✔ ❯ Select a DB Provider · Postgres
✔ ❯ Select your background worker type · Async (in-process tokio async tasks)

🚂 Loco app generated successfully in:
/workspaces/loco_project/testes

- database: You've selected `postgres` as your DB provider (you should have a postgres instance to connect to)

これで、プロジェクトが生成されるので、cdコマンドで移動して、アプリケーションを起動します。

cd myapp
cargo loco start

結構ビルドに時間がかかるみたいなので、気長に待ちましょう。

ビルドが完了すると、以下のようなログが出力されます。
かわいいですね。

                      ▄     ▀                     
                                 ▀  ▄             
                  ▄       ▀     ▄  ▄ ▄▀           
                                    ▄ ▀▄▄         
                        ▄     ▀    ▀  ▀▄▀█▄       
                                          ▀█▄     
▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄▄   ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄ ▀▀█    
 ██████  █████   ███ █████   ███ █████   ███ ▀█   
 ██████  █████   ███ █████   ▀▀▀ █████   ███ ▄█▄  
 ██████  █████   ███ █████       █████   ███ ████▄
 ██████  █████   ███ █████   ▄▄▄ █████   ███ █████
 ██████  █████   ███  ████   ███ █████   ███ ████▀
   ▀▀▀██▄ ▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀  ▀▀▀▀▀▀▀▀▀▀ ██▀  
       ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀    
                https://loco.rs

environment: development
   database: automigrate
     logger: debug
compilation: debug
      modes: server

listening on http://localhost:5150

http://localhost:5150にアクセスしてみると、以下のような画面が表示されます。

image.png

これで起動まで確認できました。

コントローラーの作成

次にコントローラーを作成してみます。

cargo loco generate controller guide --api

これでsrc/controller/guide.rsが生成されます。

#![allow(clippy::missing_errors_doc)]
#![allow(clippy::unnecessary_struct_initialization)]
#![allow(clippy::unused_async)]
use loco_rs::prelude::*;
use axum::debug_handler;

#[debug_handler]
pub async fn index(State(_ctx): State<AppContext>) -> Result<Response> {
    format::text("hello")
}

pub fn routes() -> Routes {
    Routes::new()
        .prefix("api/guides/")
        .add("/", get(index))
}

これで、localhost:5150/api/guidesにアクセスすると、helloと表示されるようになります。

$ curl http://localhost:5150/api/guides
hello

モデルの作成

ガイドに沿ってArticleモデルを作成してみます。

cargo loco generate model article title:string body:text

これで、src/model/_entities/article.rssrc/model/article.rsが生成されます。
src/model/_entities/article.rsはBDスキーマを定義しているファイルで、これは変更してはいけないようです。

ロジックはsrc/model/article.rsに書くようです。

自動で生成されたファイルは以下のようになっています。

src/model/article.rs
use sea_orm::entity::prelude::*;
use super::_entities::articles::{ActiveModel, Entity};
pub type Articles = Entity;

#[async_trait::async_trait]
impl ActiveModelBehavior for ActiveModel {
    // extend activemodel below (keep comment for generators)

    async fn before_save<C>(self, _db: &C, insert: bool) -> std::result::Result<Self, DbErr>
    where
        C: ConnectionTrait,
    {
        if !insert && self.updated_at.is_unchanged() {
            let mut this = self;
            this.updated_at = sea_orm::ActiveValue::Set(chrono::Utc::now().into());
            Ok(this)
        } else {
            Ok(self)
        }
    }
}

また同時にマイグレーションが行われているようで、DBを確認するとarticlesテーブルが作成されていました。

image-3.png

これで、モデルの作成が完了しました。

CRUDの実装

先ほどのMODELを使ってCRUDの実装をしてみます。

これもコマンド一発で生成できます。

cargo loco generate scaffold article

これで、src/controller/article.rsが生成されますが、Updateの実装は自分で行う必要があるので、以下のように実装します。

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Params {
    pub title: Option<String>,
    pub content: Option<String>,
}

impl Params {
    fn update(&self, item: &mut ActiveModel) {
        item.title = Set(self.title.clone());
        item.content = Set(self.content.clone());
    }
}

これだけで、CRUDの実装が完了します。殆どのコードが自動で生成されているので、非常に簡単に実装できます。

自動で生成されたルーティングは以下のようになっています。

pub fn routes() -> Routes {
    Routes::new()
        .prefix("api/articles/")
        .add("/", get(list))
        .add("/", post(add))
        .add(":id", get(get_one))
        .add(":id", delete(remove))
        .add(":id", put(update))
        .add(":id", patch(update))
}

動作確認

試しにデータを登録して、取得してみます。

curl -X POST -H "Content-Type: application/json" -d '{
    "title":"Locoくんすごいね",
    "content":"俺、コード全然かいてねえよ・・・。かなしい・・・。"
}' localhost:5150/api/articles

curl localhost:5150/api/articles
[{"created_at":"2024-12-24T14:52:36.440264Z","updated_at":"2024-12-24T14:52:36.440264Z","id":5,"title":"Locoくんすごいね","content":"俺、コード全然かいてねえよ・・・。かなしい・・・。"}]

ついでに、Updateも試してみます。

curl -X PUT -H "Content-Type: application/json" -d '{
    "title":"Locoくんすごいね",
    "content":"コードかかなくても動いてるよ!すごいね!"
}' localhost:5150/api/articles/5

curl localhost:5150/api/articles/5
{"created_at":"2024-12-24T14:52:36.440264Z","updated_at":"2024-12-24T14:55:18.474649Z","id":5,"title":"Locoくんすごいね","content":"コードかかなくても動いてるよ!すごいね!"}

最後に、Deleteも試してみます。

curl -X DELETE localhost:5150/api/articles/5

curl localhost:5150/api/articles/5
{"error":"not_found","description":"Resource was not found"}

簡単にCRUDの実装ができましたね。

まとめ

今回は、RustのWebフレームワークLocoを試してみました。
正直、この記事読むより公式のチュートリアルのほうがわかりやすいので、興味がある方は公式サイトを見てみてください。

本当はもうすこし具体的なアプリ開発をしたかったのですが、cargo locoコマンドを実行する度にコンパイルが走るので、思ったよりも時間がかかってしまいました。

それでは。

参考

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?