1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SeaORMスターターキットを作りました

Last updated at Posted at 2025-01-01

目的

SeaORMの情報が少ないのと、どんな感じに動くのか雰囲気を知りたい人は多いのではと思って、スターターキットを作成しました。

今回は、SeaORMを動かしてみるということに特化した設計のため、クリーンアーキテクチャは無視しています。まずは「こいつ、動くぞ。。。!」を体感してもらえると幸いです。さあ、サイド7からスタートしましょう。

環境

Windows11
Ubuntu -> Ubuntuの場合を参照
Docker

リポジトリ

手順

まずは SeaORM を動かしてみよう

ローカルにクローンする。

git clone git@github.com:Ometeor-Zheero-OMZ/sea-orm-starter.git

.env.exampleから.envを作成する

cp .env.example .env

SeaORMのCLIをインストール

cargo install sea-orm-cli

コンテナを作成

docker compose up -d
make start

一応 Makefile で各コマンドを作成しているので、お好みで使用してください。


マイグレーション実行

Dockerfileにmigrationをコピーするように記述していますので、
コンテナ内でマイグレーションします。

docker exec -it backend sea-orm-cli migrate up -u postgres://postgres:postgres@db/sea-orm-starter
make migrate-up

10秒ほどビルドされた後に、
下記のようになればOK

Applying all pending migrations
Applying migration 'm20241231_132216_create_user_table'
Migration 'm20241231_132216_create_user_table' has been applied

client.http で簡単に APIテストができるようにしていますので、さっそく確認してみましょう。

before-create-user.png

まずは、ユーザー取得。
マイグレーションした直後のため、userテーブルにレコードは入っていません。

create-user.png

ユーザー登録でレコードを挿入することができます。

after-create-user.png

再度、ユーザー取得をすることで、追加したユーザー情報を取得することができます。

新しくSeaORMの環境を作るにあたっての重要事項

マイグレーションの初期化(スターターキットでは実行済み)

今回のスターターキットではすでに初期化済みですが、新規プロジェクトでは、マイグレーションの初期化をする必要があります。cargo new projectの後に実行しておくといいでしょう。

sea-orm-cli migrate init
make migrate-init

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

table_name は作成するテーブル名を記述する必要があります。

sea-orm-cli migrate generate create_<table_name>_table

例:user テーブルを作成

sea-orm-cli migrate generate create_user_table

例:make コマンドで user テーブルを作成

make gen table_name=user

以下のようになればOK

$ make gen table_name=user
sea-orm-cli migrate generate create_user_table
Generating new migration...
Creating migration file `./migration\src\m20241231_220934_create_user_table.rs`
Adding migration `m20241231_220934_create_user_table` to `./migration\src\lib.rs`

これにより、migrationディレクトリがRustの新規プロジェクトとして src と同階層に生成されます。

  • migration/src
    • main.rs
    • m20241231_132216_create_user_table.rs
    • lib.rs
  • migration/Cargo.lock
  • migration/Cargo.toml
  • migration/README.md

main.rs

use sea_orm_migration::prelude::*;

#[async_std::main]
async fn main() {
    cli::run_cli(migration::Migrator).await;
}

以降、main.rsは編集しなくとも、マイグレーションファイルの生成とリンクはされますので、構造だけ確認しておきましょう。


m20241231_132216_create_user_table.rs

use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager
            .create_table(
                Table::create()
                    .table(User::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(User::UserId)
                            .integer()
                            .not_null()
                            .auto_increment()
                            .primary_key(),
                    )
                    .col(ColumnDef::new(User::CognitoId).string().not_null())
                    .col(ColumnDef::new(User::Username).string().not_null())
                    .col(ColumnDef::new(User::ProfilePictureUrl).string())
                    .col(ColumnDef::new(User::TeamId).integer())
                    .to_owned(),
            )
            .await?;

        manager
            .create_index(
                Index::create()
                    .name("idx_user_cognito_id")
                    .table(User::Table)
                    .col(User::CognitoId)
                    .unique()
                    .to_owned(),
            )
            .await?;

        manager
            .create_index(
                Index::create()
                    .name("idx_user_username")
                    .table(User::Table)
                    .col(User::Username)
                    .unique()
                    .to_owned(),
            )
            .await?;

        Ok(())
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        manager.drop_table(Table::drop().table(User::Table).to_owned()).await
    }
}

#[derive(Iden)]
pub enum User {
    Table,
    UserId,
    CognitoId,
    Username,
    ProfilePictureUrl,
    TeamId,
}

これは、マイグレーションファイルの生成後にテーブル定義を書いたものです。

#[derive(Iden)]
pub enum User {
    Table,
    UserId,
    CognitoId,
    Username,
    ProfilePictureUrl,
    TeamId,
}

これはテーブルのフィールド決定するものです。enum で定義します。
ここで重要なのは、Iden トレイトを derive することです。

        manager
            .create_table(
                Table::create()
                    .table(User::Table)
                    .if_not_exists()
                    .col(
                        ColumnDef::new(User::UserId)
                            .integer()
                            .not_null()
                            .auto_increment()
                            .primary_key(),
                    )
                    .col(ColumnDef::new(User::CognitoId).string().not_null())
                    .col(ColumnDef::new(User::Username).string().not_null())
                    .col(ColumnDef::new(User::ProfilePictureUrl).string())
                    .col(ColumnDef::new(User::TeamId).integer())
                    .to_owned(),
            )
            .await?;

Laravelなどのマイグレーションに慣れている方であれば、難しくはないはずです。
重要なのは、.table(User::Table) で記述する User は先ほど定義した enumUser ですので、他モジュールの User 構造体や enum を記述しないようにしましょう。

manager: テーブルを作成するためのインスタンス
.create_table(): 新規テーブルを作成 - テーブル情報をラップします。
Table::create(): 新規テーブルを作成 - メソッドチェーンでテーブル情報を追加していく。
.table(User::Table): enum で定義した User に関するテーブルを指定する。
.if_not_exists(): 同名テーブルがない場合にテーブルを作成する。
.col(ColumnDef::new()): フィールドを定義する。

メソッドチェーンで、全てのフィールドを定義していけば、.to_owned() 所有型に変換します。データを所有権で管理することで、SeaORMの最大の特徴である 非同期処理 が実現します。データオブジェクトが複数スレッドでアクセスされたり、バックグラウンドタスクで使用されることを考慮して、所有型である必要があります。

さらに、enumUser を定義する理由は、恐らくですがコンパイラがエラーを吐き続けることによって、フィールド定義に漏れが出ないように強制してくれるように設計されたと、私は思っています。


テーブル作成時のメソッドは公式ドキュメントを見て書いていただければと思います。
(SeaORM Docs) Create Table


lib.rs

pub use sea_orm_migration::prelude::*;

mod m20241231_132216_create_user_table;

pub struct Migrator;

#[async_trait::async_trait]
impl MigratorTrait for Migrator {
    fn migrations() -> Vec<Box<dyn MigrationTrait>> {
        vec![
            Box::new(m20241231_132216_create_user_table::Migration),
        ]
    }
}

こちらも特に手動で手を加えることは少ないと思いますが、sea-orm-cli でマイグレーションファイルを作成した際に、自動でマイグレーションファイルを mod し、vec! 内に、ボックス化したマイグレーションファイルのMigration トレイトが自動で指定されます。マイグレーションファイルを生成する毎に、それぞれ異なるスキーマの型として生成されるため、トレイトオブジェクトで扱うようになっています。


エンティティ生成 (スターターキットでは生成済み)

既存のデータベースからテーブル情報を基にsrc/entities/内でエンティティを生成します。

sea-orm-cli generate entity -u postgres://postgres:postgres@localhost:5432/sea-orm-starter -o src/entities
make gen-entity

これにより、以下のファイルが生成されます。

  • src/entities/prelude.rs
  • src/entities/mod.rs
  • src/entities/user.rs

src/entities/prelude.rs

//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.3

pub use super::user::Entity as User;

※エンティティ生成毎に上書きされます。マイグレーションとは異なり、破壊的に更新されるため注意です。といっても、モジュールを公開しているだけなので、そこまで痛くない。


src/entities/mod.rs

//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.3

pub mod prelude;

pub mod user;

※エンティティ生成毎に上書きされます。マイグレーションとは異なり、破壊的に更新されるため注意です。といっても、モジュールを公開しているだけなので、そこまで痛くない。


src/entities/user.rs

//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.3

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
#[sea_orm(table_name = "user")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub user_id: i32,
    #[sea_orm(unique)]
    pub cognito_id: String,
    #[sea_orm(unique)]
    pub username: String,
    pub profile_picture_url: Option<String>,
    pub team_id: Option<i32>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

既存テーブル情報をそのままエンティティとして生成できます。

※エンティティ生成毎に上書きされます。マイグレーションとは異なり、破壊的に更新されるため注意です。このファイルに関しては、さすがに痛いので特に注意です。

Ubuntuの場合

手順は全く同じですが、もしかすると sea-orm-cli のインストールに失敗するかもしれません。

大抵の問題は OpenSSLのライブラリ群がインストールされていなかったり、環境パスが設定されていない場合に起こります。

インストールに失敗した場合は下記の手順を試してみてください:

パッケージ・ライブラリをインストール

sudo apt update
sudo apt install -y pkg-config libssl-dev build-essential

OpenSSL をインストール

sudo apt install -y openssl

環境変数を設定

export OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu
export OPENSSL_INCLUDE_DIR=/usr/include/openssl
export OPENSSL_STATIC=false

sea-orm-cli をインストール

cargo install sea-orm-cli --locked

終わりに

今回は SeaORMをさっさと使いたい方または、新規プロジェクトでの参考にされたい方に向けて記事を書きました。

記事作成には慣れていないため、読みづらい箇所があるかと思いますが、最後までお読みいただき大変嬉しく思います。

次回は AxumとNext.jsでフルスタック開発スターターキットを公開しようかなと思います。
気になる方はいいねをいただけるとやる気が出ます。

また、Rustでゲーム開発もしてますので、ある程度いいものが作れたらみんなに見てもらいたいな🐮🤚

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?