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.md
、query_todo.md
、update_todo.md
、delete_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
というメッセージが気になります。これ本当にテストできているのかな?go
のExample
関数はfmt
で結果を表示し、期待値をコメントで定義することで結果を検証するそうです。今回はリンク先を日本語で読んでいたために、本来変換してはいけないところまで変換されてしまっていたようです。修正し再度試します。
- // 出力します。
+ // Output:
実行しているテストを見られるように-v
をつけました。
$ go test -v
=== RUN ExampleTodo
--- PASS: ExampleTodo (0.00s)
PASS
ok example 0.262s
今度こそテストが通りました!
テストコードの修正が必要だった理由は以下の通りです:
- package名をプロジェクトの定義に合わせる必要があったため(
todo
→example
) - Example関数の命名規則に従う必要があったため(
Example_Todo
→ExampleTodo
) -
Output:
コメントを正しく記述する必要があったため
まとめ
まだフィールドやエッジの定義をしていないのでなんのことかよく分かりませんが、とりあえずSQLite
がgo
で使えるようになり正常に使えることが確認できたようです。
次は実際にフィールドやエッジを定義し、テーブルを使ってみます。
Discussion