LoginSignup
0
0

More than 1 year has passed since last update.

はじめに

Goを勉強し始めたということで、O/Rマッパーも体験しておきます。

今回は、Go で API 作るの続きとして、Gormを入れて試してみたいと思います。

早速作ってみる

今回もこれをベースに組み込んでいきます。

Go で API 作るの続きなので、API部分はginを使います。
ginの使い方は、上記Qiita記事で紹介しているので割愛します。

Gorm を取得する

Dockerで開発しているので、docker composeコマンドとなります。
DBPostgreSQLを使ってみます。

$ docker compose run --rm back go get -u gorm.io/gorm
$ docker compose run --rm back go get -u gorm.io/driver/postgres

DB と接続

取得したgormpostgresimport

cmd/main.go
 import (
+   "gorm.io/driver/postgres"
+   "gorm.io/gorm"
 )

DBと接続するメソッドを作成します。

cmd/main.go
func gormConnect() *gorm.DB {
    dsn := os.Getenv("DATABASE_URL")
    db, _ := gorm.Open(postgres.Open(dsn), &gorm.Config{})

    db.AutoMigrate(&Todo{})

    return db
}

Herokuにデプロイも試したかったので、dsnは、DATABASE_URLの環境変数から取るようにしました。

DATABASE_URL=host=postgres user=postgres password=password dbname=go-todo-api-db port=5432 sslmode=disable TimeZone=Asia/Tokyo

todosテーブルは、AutoMigrateを使って作られるようにしました。

db.AutoMigrate(&Todo{})

todosテーブルはこんな感じで定義しています。

cmd/main.go
type Todo struct {
    gorm.Model
    Title string
}

リスト取得(GET: /api/todos)を作ってみる

上記のようにルートを用意し、getTodosメソッドが呼ばれるようにします。

Railsで言う、Todo.allのように、DBから取得します。

cmd/main.go
func getTodos(c *gin.Context) {
    var todos [] Todo

    db := gormConnect()

    db.Find(&todos)

    c.JSON(http.StatusOK, gin.H { "todos": todos })
}

Findメソッドを使います。

データが空なので、何も帰ってこないと分かりづらいです。
試しに2つデータを登録して、GET/api/todosを叩いてみました。

スクリーンショット 2022-02-23 14.42.27.png

todosテーブルを作成する際に使っているTodoの型には、Titleカラムしか指定していないのに、IDCreatedAtUpdatedAtDeletedAtカラムが追加されています。

これは、gorm.Modelに定義されているそうです。

デフォルトでIDカラムは主キーで、自動でインクリメントもしてくれました。

ちなみにIDカラム以外の独自に定義したカラムを主キー且つ、自動インクリメントしたい場合は、以下のようにすると出来ました。

Id    uint   `gorm:"primaryKey;autoIncrement`
Title string

リスト追加(POST: /api/todos)を作ってみる

上記のようにルートを用意し、addTodoメソッドが呼ばれるようにします。

cmd/main.go
func addTodo(c *gin.Context) {
    type InputTodo struct {
        Title string
    }

    var inputTodo InputTodo
    c.BindJSON(&inputTodo)

    newTodo := Todo { Title: inputTodo.Title }

    db := gormConnect()

    db.Create(&newTodo)

    c.JSON(http.StatusOK, gin.H { "todo": newTodo })
}

Createメソッドを使います。

リクエストを投げるだけでは分かりづらいので、追加したTodoをレスポンスとして返すようにしてみました。

POST/api/todosを叩いてみます。

スクリーンショット 2022-02-23 15.04.35.png

IDも自動インクリメントされているし、渡したTitleが入ったデータが作られていそうです。

リスト更新(PATCH: /api/todos/:ID)を作ってみる

上記のようにルートを用意し、updateTodoメソッドが呼ばれるようにします。
※パラメタの受け取り方をIdからIDに変更しました。(r.PATCH("/api/todos/:ID", updateTodo)

cmd/main.go
func updateTodo(c *gin.Context) {
    type InputTodo struct {
        Title string
    }

    Id := c.Param("ID")
    id, _ := strconv.Atoi(Id)

    db := gormConnect()

    var todo Todo

    db.First(&todo, id)

    var inputTodo InputTodo
    c.BindJSON(&inputTodo)

    db.Model(&todo).Update("Title", inputTodo.Title)

    c.JSON(http.StatusOK, gin.H { "todo": todo })
}

Updateメソッドを使います。
FirstFindで取得した後、カラム毎に値を代入して、Saveもできるそうです。

PATCH/api/todos/1を叩いてみます。

スクリーンショット 2022-02-23 15.09.39.png

渡したTitleID1のレコードが更新されていそうです。

リスト削除(DELETE: /api/todos/:ID)を作ってみる

上記のようにルートを用意し、deleteTodoメソッドが呼ばれるようにします。
※パラメタの受け取り方をIdからIDに変更しました。(r.DELETE("/api/todos/:ID", deleteTodo

cmd/main.go
func deleteTodo(c *gin.Context) {
    Id := c.Param("ID")
    id, _ := strconv.Atoi(Id)

    db := gormConnect()

    var todo Todo

    db.Delete(&todo, id)
}

Deleteメソッドを使います。

DELETE/api/todos/1を叩いてみます。
削除されたことが分かりづらいので、GET/api/todosも叩いてみます。

スクリーンショット 2022-02-23 15.25.18.png

ID1のレコードが削除されていそうです。

おまけ(Cross-Domain問題)

今回紹介した方法で、リクエストを送っているツールは、Insomniaを使っています。
仮に、フロントエンドアプリからリクエストを送るとしたら、Cross-Domain問題が発生すると思います。

その場合は、corsの設定を行います。

$ docker compose run --rm back go get -u github.com/gin-contrib/cors
 import (
+   "github.com/gin-contrib/cors"
 )


 r := gin.Default()

+r.Use(cors.New(cors.Config{
+   AllowOrigins: []string{os.Getenv("CORS_ORIGIN")},
+   AllowMethods: []string{"PUT", "PATCH", "DELETE"},
+}))

GETPOSTまでは問題なかったですが更新のPATCHあたりからこの問題に直面しました。
適宜、使用するHTTPリクエストメソッドを追加してください。

おまけ(HerokuGoのバージョン認識)

今回使用しているGoのバージョンは、1.17.1です。

筆者は、DockerをそのままHerokuにデプロイしていますが、ライブラリインストールでエラーが発生しました。

../codon/tmp/cache/go-path/pkg/mod/gorm.io/gorm@v1.23.1/logger/sql.go:105:45: rv.IsZero undefined (type reflect.Value has no field or method IsZero)
note: module requires Go 1.14

Goのバージョンを1.14以降にしたら良さそうですが、1.17.1の想定です。

go.modにも1.17と記載されています。

HerokuBuild Logを遡ってみると、1.12.17を使っていることが判明

-----> 
 !!    The go.mod file for this project does not specify a Go version
 !!    
 !!    Defaulting to go1.12.17
 !!    
 !!    For more details see: https://devcenter.heroku.com/articles/go-apps-with-modules#build-configuration
 !!    
-----> Using go1.12.17

Herokuの環境変数に、GOVERSION=1.17.1を追加すると解決しました。

おわりに

今回のソースはこちらのGitHubに上げてます。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0