この記事ではGoのginフレームワークを用いてデータベースサーバーからデータを取得し,JSON形式で表示することを目標とする.今回はdockerで環境構築をする.DBMSはPostgreSQLを用いる.
goのバージョンは
1.22
を用いる
Dockerで環境構築
まずプロジェクトディレクトリを作成する.
mkdir yourprojectname
cd yourprojectname
goプロジェクトを始めるために新しいGoモジュールを初期化する
go mod init example.com/yourprojectname
次にginフレームワークをインストールする.
go get -u github.com/gin-gonic/gin
DBサーバー(PostgreSQL)からデータを取ってくるための依存関係を追加する.
go get github.com/lib/pq
go get golang.org/x/net
go get github.com/lib/pq
Go言語でPostgreSQLデータベースに接続するためのドライバ.GoプログラムからPostgreSQLデータベースに接続し,クエリを実行することができる.
go get golang.org/x/net
Goの標準ライブラリを補完するネットワーク関連のパッケージを提供する.以下のようなサブパッケージが含まれている
net
追加のネットワーク関連の機能を提供する.
http2
HTTP/2プロトコルのサポートを提供する.
websocket
WebSocketプロトコルのクライアントとサーバーの実装を提供する.
proxy
プロキシ関連の機能を提供する.
Dockerコンテナを作成する.
touch Dockerfile
Dockerfileの中身は以下のようにする.
FROM golang:1.22-alpine
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go mod tidy
RUN go build -o main .
EXPOSE 8080
CMD ["./main"]
ついでにdocker-compose.ymlも作成する.
touch docker-compose.yml
今回はコンテナは1つだけだし不要では...ある
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- PORT=8080
main.go(それぞれのgoファイル実行用)の作成
次にそれぞれのgoファイルを実行する専用のgoファイルmain.go
を作成する.
touch main.go
package main
import (
//一旦空白で
)
func main() {
//一旦空白で
}
getdb.go(実際のロジック書くところ)の作成
次に実際にデータを受け取るロジックを書くgoファイルgetdb.go
を格納するディレクトリを作成する.
mkdir getdb
各エンドポイントごとにディレクトリを作成すると今後規模が大きくなってきたときに探しやすくなる
getdb.go
を作成し,以下のように書く.
touch getdb/getdb.go
ソースコードの細かい説明はコメントアウトしておきました.
package getdb
import (
"database/sql"
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/lib/pq"
)
// Run関数は,データベースとの接続を確立し、APIサーバーを起動する
func Run() {
// sql.Open関数を使用して,指定されたデータベースサーバーへの接続を開く
db, err := sql.Open("postgres", "取得したいDBサーバーのリンク")
// エラーが発生した場合は,panic関数でプログラムを終了する
if err != nil {
panic(err)
}
// defer db.Close()により,関数の最後にデータベース接続を閉じるようにする
defer db.Close()
// Ginフレームワークを使用してルーターを作成する
router := gin.Default()
// router.GETメソッドを使用して,/getdbエンドポイントを定義する
router.GET("getdb", func(c *gin.Context) {
// db.Query関数を使用し,指定されたSQLクエリを実行し,結果を取得する
rows, err := db.Query("SELECT * FROM \"テーブル名\";")
// エラーが発生した場合は,ステータスコード500(Internal Server Error)とエラーメッセージをJSONレスポンスで返す
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// defer rows.Close()により,関数の最後に結果セットを閉じるようにする
defer rows.Close()
// data変数を定義して,取得したデータを格納するためのスライスを初期化する
var data []map[string]interface{}
// rows.Columns()関数を使用して,結果セットのカラム名を取得する
columns, _ := rows.Columns()
// rows.Next()メソッドを使用して,結果セットの各行をループ処理する
for rows.Next() {
// 各行の値を格納するためのvaluesスライスとvaluePtrsスライスを作成する
values := make([]interface{}, len(columns))
valuePtrs := make([]interface{}, len(columns))
for i := range columns {
valuePtrs[i] = &values[i]
}
// rows.Scan関数を使用して,現在の行の値をスキャンし,valuesスライスに格納する
err := rows.Scan(valuePtrs...)
// エラーが発生した場合は,ステータスコード500(Internal Server Error)とエラーメッセージをJSONレスポンスで返す
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
// item変数を定義して,カラム名と値のマップを作成する
item := make(map[string]interface{})
for i, col := range columns {
item[col] = values[i]
}
// dataスライスにitemを追加する
data = append(data, item)
}
// c.JSON関数を使用して,ステータスコード200(OK)と取得したデータをJSONレスポンスで返す
c.JSON(http.StatusOK, data)
})
// router.Runメソッドを使用して,指定されたポート番号でAPIサーバーを起動する
// ポート番号は8080
router.Run(":" + "8080")
}
main.goにロジックが書かれたファイルへの依存関係を追加
ルートのmain.goにgetdb/getdb.goへの依存関係を追加し,Run関数を実行するようにする.
package main
import (
"example.com/yourprojectname/getdb"
)
func main() {
getdb.Run()
}
実行
dockerコンテナを起動する
docker compose up
コンテナが立ち上がったら,以下のリンクにアクセスすればデータが返ってくるはずだ.
http://localhost:8080/getdb
デプロイ
これをherokuにデプロイするにはgo.sum
とgo.mod
がプロジェクトに存在する必要がある.go.mod
はすでに存在すると思うのでgo.sum
を初期化して生成する必要がある.go.sum
はdockerコンテナ内で実行するようになってはいるが,go.modが適用されているか確認してほしい,できていないのであれば以下のコマンドを実行すること.
go.mod
ファイルは,プロジェクトのモジュール情報と依存関係を定義し,go.sum
ファイルは,依存関係の正確なバージョンとチェックサムを記録する.
あればスキップしてね
go mod tidy
ここまで作成した全てのファイルをgitリポジトリにプッシュして,herokuでデプロイすればOK.
デプロイ方法は以下の記事から
https://qiita.com/tarakokko3233/items/0961933e2b9695cb561a
この記事内ではPythonのDjangoを使っているが,そこに登場する
Procfile``runtime.txt
は不要である.
今回は環境変数を用いなかったがherokuに環境変数を登録してソースコードには以下のように記述すれば環境変数が使用できる.
os.Getenv("KEY")
環境変数については以下の記事を参考にすること
https://qiita.com/tarakokko3233/items/bb63dbef9d91f44d3917