Gin(Go)でAPIをRailsやLaravelライクに作る
APIを作るときにどのようなディレクトリ構成にするのかということも悩むものの一つでしょう。特にフレームワークを使わない場合、どんな構成で作っていくのかは結構考えるのではないでしょうか。
でもフレームワークを使う場合はどうでしょう。ある程度フレームワークの流れは決まっているので、それに沿って作る場合が多いんじゃないかと思います。
今回は、GOのGinというフレームワークを使って簡単なAPIを作ってみようかなと思います。その際、ビューのないMVCモデルのような感じでやってみようかなーと思う、イメージはRailsやLaravelのような感じです。GOをわからない人が見てもあーRailsの流れと同じね、みたいな感じになればいいと思う。
作成するAPI
作るのはユーザーを登録したり削除したりすることのできるものにする。
- ユーザー一覧表示
- ユーザーの個別表示
- ユーザー登録
- ユーザー削除
上記のことを行えるようにしようかな。
ディレクトリ構成
ディレクトリ構成はこんな感じでいこうと思う。
. |-- controllers | `-- users.go |-- main.go `-- models `-- user.go
main.goがエントリーポイントなので、そこにルーティングの処理を書いてコントローラー→モデルのようなデータのやり取りをすることにする。
ルーティング
main.goにルーティングの処理を書く。
// main.go package main import ( "github.com/gin-gonic/gin" "gin_api/controllers" ) func main() { router := gin.Default() router.GET("/users", controllers.Index) router.GET("/users/:id", controllers.Show) router.POST("/users", controllers.Create) router.DELETE("/users/:id", controllers.Delete) router.Run(":8080") }
指定したパスにアクセスが来たら、コントローラーのアクションに渡す。フレームワークを使わないとハンドラ関数なんかを作ってやると思うけど、フレームワーク使えばこれだけでできるから便利だね。
- Index・・・全てのユーザーを返却
- Show・・・指定したIDのユーザーを返却
- Create・・・ユーザーを新規作成
- Delete・・・指定したIDのユーザーを削除
上記のアクションを作り、controllerに実装する。
modelの定義をする
modelsディレクトリを作り、その中にuser.goを作成する。
// models/user.go package models import ( "log" "github.com/go-xorm/xorm" _ "github.com/go-sql-driver/mysql" ) var engine *xorm.Engine func init() { var err error engine, err = xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } } type User struct { ID int `json:"id" xorm:"id pk autoincr"` NickName string `json:"nickname" xorm:"'nickname'"` }
init関数はmysqlへの接続処理ですね。モデルはtype User struct
の部分。構造体のフィールドにxormとかつけるとそこで指定したものがDBテーブルのカラムとマッピングしてくれるみたい。
ここら辺のオプションは以下のgithubを見てみるといい。
コントローラー
Indexアクション
Indexでは全ユーザーの取得と返却を行う。
func Index(c *gin.Context) { engine, err := xorm.NewEngine("mysql", "データベース情報") if err != nil { log.Fatal(err) } users := []models.User{} engine.Find(&users) c.JSON(200, users) }
サーバーを起動してブラウザから確かめてみるとこんな感じで返却される。
Showアクション
Showアクションでは、指定したIDのユーザーをDBから検索して返却する
func Show(c *gin.Context) { n := c.Param("id") id, err := strconv.Atoi(n) if err != nil { log.Fatal(err) } if id <= 0 { c.JSON(400, gin.H{"message": "idは1以上にしてね"}) return } user := models.User{ ID: id, } engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } engine.Get(&user) c.JSON(200, user) }
これはこんな感じ。idを1と想定してときの返却値。
でも、これ存在しないユーザーIDを指定しても返ってきちゃうからなんとかした方がいいですね。
Create
ここでは、ユーザーの新規作成を行う。
func Create(c *gin.Context) { newUser := models.User{} id, err := strconv.Atoi(c.PostForm("id")) if err != nil { log.Fatal(err) } newUser.ID = id newUser.NickName = c.PostForm("nickname") engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } engine.Insert(newUser) c.JSON(200, newUser) }
$ curl -F "id=4" -F "nickname=new_user" http://localhost:8080/users
curlで叩いてデータが入っているか確かめるとちゃんと入っていますね。
Delete
ここでは、指定したIDのユーザーを削除する。
func Delete(c *gin.Context) { n := c.Param("id") id, err := strconv.Atoi(n) if err != nil { log.Fatal(err) } engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } user := models.User{ID: id} _, err = engine.Id(id).Delete(&user) if err != nil { log.Fatal(err) } c.JSON(200, "") }
$ curl -X DELETE 'http://localhost:8080/users/4'
これで全てのユーザーをブラウザから見てみると、確かに削除されていますね。
全コード
// controllers/users.go package controllers import ( "log" "strconv" "github.com/gin-gonic/gin" "github.com/go-xorm/xorm" "gin_api/models" ) func Index(c *gin.Context) { engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } users := []models.User{} engine.Find(&users) c.JSON(200, users) } func Show(c *gin.Context) { n := c.Param("id") id, err := strconv.Atoi(n) if err != nil { log.Fatal(err) } if id <= 0 { c.JSON(400, gin.H{"message": "idは1以上にしてね"}) return } user := models.User{ ID: id, } engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } engine.Get(&user) c.JSON(200, user) } func Create(c *gin.Context) { newUser := models.User{} id, err := strconv.Atoi(c.PostForm("id")) if err != nil { log.Fatal(err) } newUser.ID = id newUser.NickName = c.PostForm("nickname") engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } engine.Insert(newUser) c.JSON(200, newUser) } func Delete(c *gin.Context) { n := c.Param("id") id, err := strconv.Atoi(n) if err != nil { log.Fatal(err) } engine, err := xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } user := models.User{ID: id} _, err = engine.Id(id).Delete(&user) if err != nil { log.Fatal(err) } c.JSON(200, "") }
// models/user.go package models import ( "log" "github.com/go-xorm/xorm" _ "github.com/go-sql-driver/mysql" ) var engine *xorm.Engine func init() { var err error engine, err = xorm.NewEngine("mysql", "DB情報") if err != nil { log.Fatal(err) } } type User struct { ID int `json:"id" xorm:"id pk autoincr"` NickName string `json:"nickname" xorm:"'nickname'"` }
// main.go package main import ( "github.com/gin-gonic/gin" "gin_api/controllers" ) func main() { router := gin.Default() router.GET("/users", controllers.Index) router.GET("/users/:id", controllers.Show) router.POST("/users", controllers.Create) router.DELETE("/users/:id", controllers.Delete) router.Run(":8080") }
まとめ
フレームワークを使うと、割と楽にできてしまうのが良いですね。今回はxormを使ってみたけど、以前ちらっと本で読んだ感じgormが結構良さげだったからそっちの方が使いやすいかもしれんね。