毎回環境を作るときに何かしらド忘れしてしまい手こずってしまうので忘れないようにメモ。
はじめに
- 下記の知識や経験があることを前提にしてます
- Go言語の基礎を理解している
- Dockerと docker-compose の基礎を理解している
- バックエンド開発やデータベースの基礎を理解している
- ORMの使い方や各種コマンドの詳しい説明はしません
- パスなどはご自身のプロジェクトの構成に合わせて適宜変更してください
作ったもののサンプルです。
開発環境
- Go1.20
- PostgreSQL 15
- Docker
- docker-compose
使用するツール
SQLBoler v3.7.1
golang-migrate v4.15.2
手順① Docker と docker-compose で Go と PostgreSQL を起動
Dockerfile作成
Go
FROM golang:1.20
WORKDIR /app
RUN go install github.com/volatiletech/sqlboiler@latest
RUN go install github.com/volatiletech/sqlboiler/drivers/sqlboiler-psql@latest
RUN go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@v4.15.2
# この辺はプロジェクトのディレクトリ構成に合わせてください
COPY ./src .
- SQLBoiler と golang-migrate もインストールしておきます
- ORMのコマンドとマイグレーションのコマンドもDocker上で叩けるようにします
PostgreSQL
FROM postgres:15
docker-compose.yml作成
version: "3.7"
services:
app:
build:
context: .
dockerfile: ./docker/golang/Dockerfile
volumes:
- ./:/app
command: ["go", "run", "src/cmd/main.go"]
ports:
- "8080:8080"
environment:
- DB_HOST=${DB_HOST:-db}
- DB_PORT=${DB_PORT:-5432}
- DB_USER=${POSTGRES_USER:-postgres}
- DB_PASSWORD=${POSTGRES_PASSWORD:-password}
- DB_NAME=${POSTGRES_DB:-postgres}
depends_on:
- db
networks:
- app_network
db:
build:
context: .
dockerfile: ./docker/postgresql/Dockerfile
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
networks:
- app_network
volumes:
postgres_data:
networks:
app_network:
起動してみる
エントリーポイントとなる main.go を作成
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
_ "github.com/lib/pq"
)
func main() {
// DB接続
dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Tokyo",
os.Getenv("DB_HOST"),
os.Getenv("DB_USER"),
os.Getenv("DB_PASSWORD"),
os.Getenv("DB_NAME"),
os.Getenv("DB_PORT"),
)
db, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatal("failed to init database: ", err)
}
err = db.Ping()
if err != nil {
log.Fatal("failed to connect database: ", err)
}
log.Default().Println("success to connect db!!")
// HTTPサーバーを起動
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
err = http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("failed to serve: ", err)
}
log.Default().Println("Server started on port: 8080")
}
envファイル作成
- ルートディレクトリに.envファイルを作成し、下記を追加
DB_HOST=db
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=password
DB_NAME=sample
- ここで設定した環境変数は docker-compose で起動する時に読み取ってくれます
# docker-compose で設定したやつ。
environment:
- DB_HOST=db
- DB_PORT=5432
- DB_USER=${POSTGRES_USER:-postgres}
- DB_PASSWORD=${POSTGRES_PASSWORD:-password}
- DB_NAME=${POSTGRES_DB:-postgres}
docker起動
docker-compose up -d --build
docker-desktop で下記を確認してください
- http server が起動できてること
- DBが起動してること
手順② マイグレーションファイルの作成
golang-migate のコマンドを叩いてマイグレーションファイルを生成
- 下記のように実行できます
docker-compose exec app migrate create -ext sql -dir ${ファイルを生成する先のディレクトリ} -seq ${テーブル名}
- 実行するとこんな感じです
docker-compose exec app migrate create -ext sql -dir ./src/database/migrations -seq tasks
- 実行に成功したらマイグレーションファイルが生成されてるはずです。
src
├── database
│ ├── migrations
│ │ ├── 000001_tasks.down.sql
│ │ └── 000001_tasks.up.sql
マイグレーションファイルに記述
upファイル
CREATE TABLE tasks (
id varchar(36) PRIMARY KEY,
title TEXT NOT NULL,
created_at TIMESTAMP NOT NULL
);
downファイル
DROP TABLE IF EXISTS tasks;
手順③ マイグレーションファイルの内容をDBに反映する
docker-compose exec app migrate -database "postgres://${ユーザー名}:${パスワード}@${ホスト}:${ポート}/${データベースの名前}?sslmode=disable" -path ${マイグレーションファイルのパス} up
こんな感じ
docker-compose exec app migrate -database "postgres://postgres:password@db:5432/postgres?sslmode=disable" -path ./src/database/migrations up
ここまでできるとテーブルが作成されてるはずです。
- PostgreSQL の中に入る
docker-compose exec db psql --host=localhost --port=5432 --username=postgres
-
\l
でデータベース一覧を確認
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
-----------+----------+----------+------------+------------+------------+-----------------+-----------------------
postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc |
sample | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc |
template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | | libc | =c/postgres +
| | | | | | | postgres=CTc/postgres
(4 rows)
- (もしデータベースを自分で作成していたら)
\c ${作成したデータベースの名前}
のコマンドを叩いてデータベースを切り替えてください
postgres=# \c sample
You are now connected to database "sample" as user "postgres".
sample=#
-
/dt
でテーブル一覧を表示する
sample=# \dt
List of relations
Schema | Name | Type | Owner
--------+-------------------+-------+----------
public | schema_migrations | table | postgres
public | tasks | table | postgres
(2 rows)
-
\d ${テーブル名}
で作成されたフィールドの一覧を確認
sample=# \d tasks
Table "public.tasks"
Column | Type | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+---------
id | character varying(36) | | not null |
title | text | | not null |
created_at | timestamp without time zone | | not null |
Indexes:
"tasks_pkey" PRIMARY KEY, btree (id)
- 上記のように作成したテーブルとフィールドが存在していたら成功です。
ちなみに down と force のコマンドは下記のとおりです。
down
docker-compose exec app migrate -database "postgres://${ユーザー名}:${パスワード}@${ホスト}:${ポート}/${データベースの名前}?sslmode=disable" -path ${マイグレーションファイルのパス} down
force
docker-compose exec app migrate -database "postgres://${ユーザー名}:${パスワード}@${ホスト}:${ポート}/${データベースの名前}?sslmode=disable" -path ${マイグレーションファイルのパス} force ${バージョン}
手順④ DBのスキーマからGoのコードを生成する(ORM)
SQLBoilerを使ってDBスキーマからコードを自動生成します。
tomlファイルの作成
- SQLBoiler設定ファイルを作成します
-
sqlboiler.toml
というファイルをルートに作成して、DBの設定に応じて下記を追加してください
[psql]
dbname = "sample"
host = "db"
port = 5432
user = "postgres"
pass = "password"
sslmode = "disable"
schema = "public"
SQLBoilerのコマンドを叩いてスキーマからコード生成
- 下記のようなフォーマットで生成できます
docker-compose exec app sqlboiler psql --output ${出力先のパス名} --pkgname models --wipe
- 今回は下記のようにします。
docker-compose exec app sqlboiler psql --output ./src/database/models --pkgname models --wipe
実行に成功すると指定したディレクトリにコードが生成されます。
└── src
├── cmd
│ └── main.go
└── database
├── migrations
│ ├── 000001_tasks.down.sql
│ └── 000001_tasks.up.sql
└── models // DBのスキーマから自動生成されたファイル
├── boil_main_test.go
├── boil_queries.go
├── boil_queries_test.go
├── boil_suites_test.go
├── boil_table_names.go
├── boil_types.go
├── psql_main_test.go
├── psql_suites_test.go
├── psql_upsert.go
├── schema_migrations.go
├── schema_migrations_test.go
├── tasks.go
└── tasks_test.go
生成後は go mod tidy
を忘れず実行しましょう。
go mod tidy
- 生成したコードを使ってDBにアクセスできます。
package repository
import (
"context"
"database/sql"
"github/Takenari-Yamamoto/golang-dev-env/src/database/models" // SQLBoilerから生成されたモデル
"github.com/volatiletech/sqlboiler/boil"
)
// 全件取得
func GetAllTasks(ctx context.Context, db *sql.DB) (tasks []*models.Task, err error) {
res, err := models.Tasks().All(ctx, db)
if err != nil {
return nil, err
}
return res, nil
}
// 1件取得
func GetTask(ctx context.Context, db *sql.DB, id string) (task *models.Task, err error) {
res, err := models.FindTask(ctx, db, id)
if err != nil {
return nil, err
}
return res, nil
}
// 作成
func CreateTask(ctx context.Context, db *sql.DB, task *models.Task) (err error) {
err = task.Insert(ctx, db, boil.Infer())
if err != nil {
return err
}
return nil
}
// 更新
func UpdateTask(ctx context.Context, db *sql.DB, task *models.Task) (err error) {
_, err = task.Update(ctx, db, boil.Infer())
if err != nil {
return err
}
return nil
}
// 1件削除
func DeleteTask(ctx context.Context, db *sql.DB, task *models.Task) (err error) {
_, err = task.Delete(ctx, db)
if err != nil {
return err
}
return nil
}
おまけ
Makefileで実行できるようにしておくと便利です。
create-migration-file:
docker-compose exec app migrate create -ext sql -dir ./src/database/migrations -seq ${TABLE_NAME}
migrate-up:
docker-compose exec app migrate -database "postgres://postgres:password@db:5432/sample?sslmode=disable" -path ./src/database/migrations up
migrate-down:
docker-compose exec app migrate -database "postgres://postgres:password@db:5432/sample?sslmode=disable" -path ./src/database/migrations down
migrate-force:
docker-compose exec app migrate -database "postgres://postgres:password@db:5432/sample?sslmode=disable" -path ./src/database/migrations force ${VERSION}
gen-code-from-db:
docker-compose exec app sqlboiler psql --output ./src/database/models --pkgname models --wipe && cd src && go mod tidy