goaでバスの路線図APIを作る(API編2)
バックエンド処理
前回まででAPIとしてのガワは完成。
このままビルドしても動くのでgoaすごいなって思う。
とはいっても欲しいデータを取れるわけではないので、その部分をコーディングしていく。
具体的には下記赤のところ。
今回はgormを使って赤のところを書いていく。
路線名を受け取るところまでは自動生成されているので、SQLを発行するところを関数で呼び出せる形で作る。
// rosenzu.go
// データを受け取るところ(自動生成)
// Find implements find.
func (s *rosenzusrvc) Find(ctx context.Context, p *rosenzu.FindPayload) (res *rosenzu.Line, err error) {
var line = repository.FindLine(p.Name) // SQLでデータ取得←ここを作る
res = cast.CastedLine(line) // APIへ渡せる型へキャスト←ここも作る
return
}
今回はdatabaseというフォルダを作り、その中に必要な物を配置してコードを書いた。
├── database
│ ├── cast
│ │ └── line.go // APIへ渡せる型へキャストする処理
│ ├── csv // DBの初期データ
│ │ ├── elements.csv
│ │ ├── lines.csv
│ │ ├── operationalpoints.csv
│ │ └── relations.csv
│ ├── database.go // DBとの接続等の処理
│ ├── model // gormのモデル(テーブル定義)
│ │ └── line.go
│ ├── repository // SQLを発行する処理
│ │ └── line.go
│ └── seed.go // DBの初期データをファイルからDBへ登録する処理
DBとの接続処理
godotenvを使って接続情報を定義ファイル(.env)から読み込んで、その情報でDBへ接続、その後、マイグレーションして初期データ投入する様にした。
// database.go
func SetupDb() {
// .envで読み込んだ接続情報
dsn := os.Getenv("DATABASE_URL")
// DBへ接続
Db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("FAILED TO CONNECT TO DB")
} else {
log.Println("CONNECTED TO DB")
}
// オートマイグレーション
Db.AutoMigrate(&model.Line{}, &model.Element{}, &model.Relation{}, &model.Coordinate{}, &model.OperationalPoint{})
// 初期データ投入
DbSeed()
}
この処理をプログラム起動時に実行して欲しいので、main.goでこの処理を呼ぶ様にcmd内のファイルを変更する。
// cmd/rosenzu/main.go
〜略〜
var wg sync.WaitGroup
ctx, cancel := context.WithCancel(context.Background())
// ここまで自動生成コード
// load .env
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
} else {
log.Println("LOAD .env file")
}
// connect to DB
database.SetupDb()
// ここからサーバ起動処理
// Start the servers and send errors (if any) to the error channel.
switch *hostF {
〜略〜
gormのモデル定義
design編で考えていたデータに一致するようにgormのモデルを定義する。
出来たのがこんな感じ、コードは最後に載せます。
SQLでデータを取得する処理
SQLを発行する部分はgormの基本形なのでサクッと。
路線名で検索した時に、その下の要素などが取れる様にPreloadしておく。
// 路線名の検索
func FindLine(name string) model.Line {
var line model.Line
if err := database.Db.Preload(clause.Associations).Preload("Elements.Coordinates").Where("name = ?", name).First(&line).Error; err != nil {
log.Println("Not Found Line")
}
return line
}
型をキャストする処理
ここは何も考えず右から左へ型変換する様にした。
路線部分はこんな感じ。
// goaのLine型にキャスト
func CastedLine(line model.Line) *rosenzu.Line {
casted_line := rosenzu.Line{
Name: &line.Name,
Elements: CastedElements(line.Elements),
Relations: CastedRelations(line.Relations),
OperationalPoints: CastedOperationalpoints(line.OperationalPoints),
}
return &casted_line
}
動作確認
下記でビルドして、作られたファイルを起動する。
$ go build -o directory cmd/rosenzu
-oはなくてもいいけど、git管理外のディレクトリに出力するために入れている。
起動したらcurlでリクエストを投げてみる。
$ curl http://localhost:8000/line/悠久山線
ちゃんと取得出来た。
作った物は下記
次回、このデータを使うビュー部分を作る。