sqlcを使って簡単なウェブアプリケーションを作る
mattnさんの書かれている記事のentの部分をsqlcに置き換えてみました。 ソースコードはgithubに公開しています。 sqlc以外の部分は、ほぼmattnさんのコードの流用になります。
sqlcはSQLファイルを読み取って、goコードを生成するツールです。sqlc.devに説明がありますが、entのようにsqlc自身でマイグレーションをしてくれるわけではないようです。サポートしているツールはいくつかあるようですが、dbmateを使いました。
前準備
次のコマンドでCLIツールをインストールします。
$ go install github.com/kyleconroy/sqlc/cmd/sqlc@latest
$ go install github.com/amacneil/dbmate@latest
プロジェクトを作ります。
$ mkdir path/to/sqlc-example
$ cd path/to/sqlc-example
$ go mod init github.com/nnabeyang/sqlc-example
データベースのテーブルを作成
データベースはsample_dbという名前でmysql上に作っているとします。dbmateでこれにentries
テーブルを追加しますが、接続情報を.env
からDATABASE_URL
で参照するようなので、まず次のように.env
を作成します。
$ echo 'DATABASE_URL="mysql://user:password@127.0.0.1:3306/sample_db"' > .env
マイグレーションファイルを作成します。
$ dbmate new create_entries_table
Creating migration: db/migrations/20220515125749_create_entries_table.sql
ファイルが作成されたので、それを編集します。
-- migrate:up
CREATE TABLE `entries` (
`id` bigint NOT NULL AUTO_INCREMENT,
`content` varchar(255) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
-- migrate:down
DROP TABLE `entries`;
マイグレーションを実行します。
$ dbmate up
テーブルは作成され、db/schema.sql
に現在のデータベースのスキーマが書き出されます。
DBスキーマに対応するgoコードを生成
sqlc init
でsqlc.yamlを生成して、編集します。
version: "1"
project:
id: ""
packages:
- path: "database"
name: "database"
engine: "mysql"
schema: "db/schema.sql"
queries: "db/query.sql"
まだquery.sql
がないので、ファイルを作成します。このquery.sql
を元にGoコードを生成します。
-- name: GetEntries :many
SELECT *
FROM entries
ORDER BY created_at DESC
LIMIT ?;
-- name: CreateEntry :execresult
INSERT INTO entries (content)
VALUES (?);
このようにコメントに関数名や、返り値の型などを書きます。では生成しましょう。
$ sqlc generate
database
ディレクトリが作成されます。ここまでで、省略した部分もありますが、以下のような感じになります。database
とdb/schema
はツールが生成したものです。
.
├── LICENSE
├── README.md
├── database
│ ├── db.go
│ ├── models.go
│ └── query.sql.go
├── db
│ ├── migrations
│ │ └── 20220515125749_create_entries_table.sql
│ ├── query.sql
│ └── schema.sql
├── go.mod
├── go.sum
├── main.go
├── .env
├── sqlc.yaml
└── template
└── index.slim
アプリケーションを実装
mattnさんのコードをコピペした後、ent
のコードをsqlc
に置き換えました。
package main
import (
"context"
"database/sql"
"log"
"net/http"
"os"
_ "github.com/go-sql-driver/mysql"
"github.com/labstack/echo/v4"
"github.com/mattn/go-slim"
"github.com/nnabeyang/sqlc-example/database"
)
func main() {
db, err := sql.Open("mysql", os.Getenv("DSN")+"?tls=false&parseTime=true")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
t, err := slim.ParseFile("template/index.slim")
if err != nil {
log.Fatal(err)
}
client := database.New(db)
e := echo.New()
e.GET("/", func(c echo.Context) error {
entries, err := client.GetEntries(context.Background(), 10)
if err != nil {
return err
}
c.Request().Header.Set("content-type", "text/html")
return t.Execute(c.Response(), map[string]interface{}{
"entries": entries,
})
})
e.POST("/add", func(c echo.Context) error {
if _, err := client.CreateEntry(context.Background(), c.FormValue("content")); err != nil {
log.Println(err.Error())
return c.String(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}
return c.Redirect(http.StatusFound, "/")
})
e.Logger.Fatal(e.Start(":8989"))
}
おわりに
いかがだったでしょうか。sqlcはentのように細かくgoコードでSQLを組み立てたりすることはできませんが、スキーマ変えたり、追加するたびに決まりきって書くようなコードの自動生成は十分にやってくれるようです。
Discussion