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のモデルを定義する。

出来たのがこんな感じ、コードは最後に載せます。


ER図

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/悠久山線

ちゃんと取得出来た。

取得データ

作った物は下記

次回、このデータを使うビュー部分を作る。

この記事が気に入ったらサポートをしてみませんか?