Rustでwebアプリを実装して勉強 その1 - sqliteを使う

Rustを勉強するためにactix-webを使ってwebアプリを書いてみた備忘録。

github.com

今日やったこと

Rust初心者で何も分からないので、actix-web と diesel のGetting Startedを読みながら、雰囲気でコードを書いてみた。

diesel_cli のインストール

DBの作成・マイグレーションなどをするため、cliをインストールする必要がある。

$ cargo install diesel_cli

で普通にインストールできた。 もし postgresql とかがローカルになければ、sqlite だけのインストールもできるらしい。

$ cargo install diesel_cli --no-default-features --features sqlite

DBの作成

DATABASE_URLを指定して diesel setup するとDBが作れる。

$ DATABASE_URL=development.sqlite3 diesel setup

毎回指定するのも面倒なので、.envrc に入れておく。

$ echo export DATABASE_URL=development.sqlite3 > .envrc

マイグレーションファイルの作成

diesel migration generate で作成できる。

$ diesel migration generate create_posts
Creating migrations/2019-10-27-152149_create_posts/up.sql
Creating migrations/2019-10-27-152149_create_posts/down.sql

ActiveRecordみたいにRubyではなくて、生SQLだった。この潔さは嫌いじゃない。

DBのマイグレーション

diesel migration runマイグレーションを実行してくれる。

$ diesel migration run
Running migration 2019-10-27-152149_create_posts

diesel migration redo で down.sql の動作確認もできる。

DBの接続

こんな感じで connection を作る。

pub fn establish_connection() -> SqliteConnection {
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    SqliteConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

コネクションの管理を diesel でやっているのか、自分でやる必要あるのかはよく分かっていない。

複数のPostを取得する

こんな感じでfilterやlimitが書けるらしい。たぶんorderとかも使える気がする。

let results = posts
    .filter(published.eq(true))
    .limit(5)
    .load::<Post>(&connection)
    .expect("Error loading posts");

1つのPostを取得する

findのあとにfirstを書くのが冗長感あるけど、たぶん posts.find(id) がWHERE句になるんだと思う。

let post = posts.find(id)
    .first::<Post>(&connection)
    .expect("Error finding posts");

Postを登録する

insert_intoを使う。

ちなみにdieselのGetting Started だとget_resultを使っているけど、sqliteは対応してないので代わりにexecuteを使っている。

let new_post = NewPost {
    title: "title",
    body: "body",
};
let connection = establish_connection();
diesel::insert_into(posts::table)
    .values(&new_post)
    .execute(&connection)
    .expect("Error saving new post");

Postを更新する

Getting Startedを参考にして書いたら、普通に動いた。

diesel::update(posts.find(id))
    .set(published.eq(true))
    .execute(&connection)
    .expect(&format!("Unable to find post {}", id));

Postを削除する

これもドキュメント通り。

diesel::delete(posts.find(id))
    .execute(&connection)
    .expect("Error deleting posts");

ルーティング周り

それっぽく書いたら、動いた。

HttpServer::new(|| {
    App::new()
        .route("/", web::get().to(index))
        .route("/posts", web::get().to(posts_index))
        .route("/posts/{id}", web::get().to(posts_show))
        .route("/posts", web::post().to(posts_create))
        .route("/posts/{id}", web::put().to(posts_update))
        .route("/posts/{id}", web::patch().to(posts_update))
        .route("/posts/{id}", web::delete().to(posts_destroy))
})

次にやりたいこと

  • SQLに関する処理をModelに移す(もしくはRepositoryを作る)
  • テストコードを書く
  • jsonを返すように直す
  • テンプレートエンジンを使ってhtmlを返す