APIを作りながら進むGo中級者への道を写経するリポジトリ

dif

内容差分

asdf

asdfを使っている。そのせいか、go.modおよびgo.sumの記述によってvscode上でエラーが発生することがある

Command 'gopls.go_get_package' failed: Error: err: exit status 1: stderr: go: finding module for package <ローカルのパッケージ>

goplsのエラーだが、どうやらasdfのgolangのpkgにインストールされたパッケージを見にいったりしている? こういう場合、go.modgo.sumのそれっぽいパッケージの部分を削除し、コードでimportしてるそれっぽいパッケージの部分も削除。vscodeのGolang拡張機能が入っていれば、そのまま保存すれば現在参照できる形で勝手に修正され、エラーは消えた。

air

特に最初はちょこちょこハンドラやリクエストの扱いなどを変更 -> プロセスを停止させたあと go run main.goで再度起動し別ターミナルでcurlで確認、という工程が多い。airでホットリロードすることにした

go install github.com/cosmtrek/air@latest
air

  __    _   ___  
 / /\  | | | |_) 
/_/--\ |_| |_| \_ v1.49.0, built with Go go1.21.4
  • これによりgo run main.go時にオプションを渡すなどができなくなる

本書だとよく「最初のAPI用のコードだけ書いて、あとは実戦演習」のように進む。そのとき、全部のAPIを書かずとも、ひとつひとつ完成したところから試せるのがよい(ただしhandler-serivice-repositoryが揃ったp241以降だが)

環境変数

本書ではgo run時にDBのパスワードなどをオプションで渡し、コードでos.Getenvで読み取るのだが、自分の環境ではうまくいかず。環境変数としてオプションを渡す方法は情報がなく不明。

これはjoho/godotenvを使うことにした

func init() {
	// 本書で書かれていた「実行時に環境変数をオプションで渡す」が動かなかったのでgodotenvで読み込むことにした
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatal(err)
	}
}

// var (
// 	dbUser     = os.Getenv("DB_USER")
// 	dbPassword = os.Getenv("DB_PASSWORD")
// 	dbDatabase = os.Getenv("DB_NAME")
// 	dbConn     = fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?parseTime=true", dbUser, dbPassword, dbDatabase)
// )

func main() {
	dbUser := os.Getenv("DB_USER")
	dbPassword := os.Getenv("DB_PASSWORD")
	dbDatabase := os.Getenv("DB_NAME")
	dbConn := fmt.Sprintf("%s:%s@tcp(127.0.0.1:3306)/%s?parseTime=true", dbUser, dbPassword, dbDatabase)

(p120~)3-1. 前準備 〜DB セットアップ

書籍内ではMySQLのDockerコンテナをローカルにあるmysqlコマンド経由で使っている。自分の環境ではローカルにMySQLがないので、コンテナのシェルに入って直接操作している。

services:

  mysql:
    platform: linux/x86_64 <- M1 mac用
    ...各種設定
    volumes:
        - db-volume:/var/lib/mysql
        - ./createTable.sql:/docker-entrypoint-initdb.d/createTable.sql <- これを追加した

本書どおりにDBからテーブルを確認する。上記のdocker-compose.yamlの追記は、コンテナボリュームが作成されるときに実行されるので、すでに作っていた場合はコンテナごと削除して再度作り直します

docker-compose down -v
docker-compose up

別のターミナルからコンテナに入って確認

docker exec -it db-for-go /bin/sh
mysql -u docker sampledb -p
  • /usr/bin/mysql: /usr/bin/mysql: cannot execute binary fileとなる場合は以下を試す
docker exec -it db-for-go  mysql -u docker -p

(p218~)4-5. repositoriesパッケージのテストを完成させよう

p224exec.Commandについて、環境の違いで改変。本書の環境だとmysqlがローカルにある前提なので、

cmd := exec.Command("mysql", "-h", "127.0.0.1", "-u", "docker", "sampledb",
↪ "--password=docker", "-e", "source ./testdata/cleanupDB.sql")

のようになっている。自分の環境ではDockerのMySQLコンテナ上でDBを動かしているが、exec.Commandみたいに実行しようとしても、シェル環境ではないので、リダイレクトができない

// これはリダイレクト演算子をつかっているのでエラーになる
cmd := exec.Command("docker", "exec", "-i", "db-for-go", "mysql", "-udocker", "-pdocker", "sampledb", "<", "./dataset/setupData.sql")

ということでChatGPTに聞いたら、cmd.Stdinを使って標準入力でファイルを読み込ませろとのこと。

// cmd := exec.Command("mysql", "-h", "127.0.0.1", "-u", "docker", "sampledb", "--password=docker", "-e", "source ./testdata/setupDB.sql") ローカルのmysql経由で実行(本書通り)

// 以下,自分の環境用に改変
// dockerのmysqlコンテナ経由で実行(リダイレクトはシェルの機能でありexec.Commandでは使えない)
setupSql, err := os.Open("./testdata/setupDB.sql")
if err != nil {
    return err
}
cmd := exec.Command("docker", "exec", "-i", "db-for-go", "mysql", "-udocker", "-pdocker", "sampledb")
// リダイレクト
cmd.Stdin = setupSql

err = cmd.Run()