🎉

gRPC - connect - Render でwebサービスを作ってみる:サーバサイドCRUD環境構築

に公開

はじめに

gRPCとRemixを使ってサービスを公開したいと考えています。以下で開発のテンプレートができました。今日はサーバサイドでSQLiteのDBへのCRUDができるようにしてみたいと思います。

作業を進めるに当たりサーバー側にDBが欲しくなりました。まずはSQLiteみたいな簡易なDBはないかなということで探してみるとgo-sqlite3というものがあるんですね。

更に調べるとgo用のORM(Object-Relational Mapper)であるentというものがありました。entのスキーマ定義からproto定義を生成し、フロントエンドから直接CRUDもできるようです。また、graphqlもサポートされているようですので、FEから自由にデータを取得したいときには便利ですね。ORMを使うことで型安全性を保ったデータベースの操作が簡単にできますね。

以下のリポジトリをベースに作業を進めます。

変更後の結果は以下です

参照情報

今回は以下の記事を参照してentの導入をしてみたいと思います。

インストール

作業ディレクトリに移動します
cd ./backend

entをインストールします
go get entgo.io/ent/cmd/ent

コード生成

スキーマ作成

サンプルとして Todo スキーマを作成します

go run -mod=mod entgo.io/ent/cmd/ent new Todo

以下のようにコードが追加されました。

追加されたtodo.goは以下の内容となっていてTodoのスキーマが定義されていますが、フィールドやエッジが定義されていませんので後で追加していきます。

package schema

import "entgo.io/ent"

// Todoは、Todoエンティティのスキーマ定義を保持します。
type Todo struct {
    ent.Schema
}

// Todoのフィールド
func (Todo) Fields() []ent.Field {
    return nil
}

// Todoのエッジ
func (Todo) Edges() []ent.Edge {
    return nil
}

コマンド生成

上記でスキーマを作りましたが、これだけだとデータの操作ができません。CRUD操作を行うコードを生成します。

go generate ./ent

何やらいっぱいファイルが作られました。todo.goには型定義のようなものもあります。create_todo.mdquery_todo.mdupdate_todo.mddelete_todo.mdがあるので、ここにORMの実装がありそうですね。

テストケース生成

go-sqlite3をインストールし、SQLiteを使ったテストケースを生成します。

go get github.com/mattn/go-sqlite3
touch example_test.go

以下をexample_test.goに貼り付けます。

package todo

import (
    "context"
    "log"
    "todo/ent"

    "entgo.io/ent/dialect"
    _ "github.com/mattn/go-sqlite3"
)

func Example_Todo() {
    // インメモリーのSQLiteデータベースを持つent.Clientを作成します。
    client, err := ent.Open(dialect.SQLite, "file:ent?mode=memory&cache=shared&_fk=1")
    if err != nil {
        log.Fatalf("failed opening connection to sqlite: %v", err)
    }
    defer client.Close()
    ctx := context.Background()
    // 自動マイグレーションツールを実行して、すべてのスキーマリソースを作成します。
    if err := client.Schema.Create(ctx); err != nil {
        log.Fatalf("failed creating schema resources: %v", err)
    }
    // 出力します。
}

テストを実行します。

go test

失敗します。。。むむむ。。。

今回既存のpackageexampleが定義されたプロジェクトに追加したので、テストコードもプロジェクトのgo.modで定義されているpackage名に合わせる必要がありました。また、テスト名は何やら命名規則があるそうです(後述)。

-package todo
+package example // go.modで定義しているpackageにあわせる

import (
    "context"
    "log"
-    "todo/ent"
+    "example/ent" // package名を修正

    "entgo.io/ent/dialect"
    _ "github.com/mattn/go-sqlite3"
)

-func Example_Todo() {
+func ExampleTodo() {

通りました!

$ go test

testing: warning: no tests to run
PASS
ok      example 0.264s

しかしtesting: warning: no tests to runというメッセージが気になります。これ本当にテストできているのかな?goExample関数はfmtで結果を表示し、期待値をコメントで定義することで結果を検証するそうです。今回はリンク先を日本語で読んでいたために、本来変換してはいけないところまで変換されてしまっていたようです。修正し再度試します。

-   // 出力します。
+	// Output:

実行しているテストを見られるように-vをつけました。

$ go test -v
=== RUN   ExampleTodo
--- PASS: ExampleTodo (0.00s)
PASS
ok      example 0.262s

今度こそテストが通りました!

テストコードの修正が必要だった理由は以下の通りです:

  • package名をプロジェクトの定義に合わせる必要があったため(todoexample
  • Example関数の命名規則に従う必要があったため(Example_TodoExampleTodo
  • Output:コメントを正しく記述する必要があったため

まとめ

まだフィールドやエッジの定義をしていないのでなんのことかよく分かりませんが、とりあえずSQLitegoで使えるようになり正常に使えることが確認できたようです。

次は実際にフィールドやエッジを定義し、テーブルを使ってみます。

Discussion