GoでCRUD処理をする簡単なAPIを作った話
※注意
Ginというフレームワークを使います。
環境
WSL2 Ubuntu20.04LTS
mongoDB
go version go1.14.7 linux/amd64
ディレクトリ構成
recipes/
controllers/controller.go
models/models.go
router/router.go
main.go
コード
main.go
package main import ( "recipes/router" ) func main() { router.Router() }
Router()という関数を呼び出すだけ。
router.go
package router import( "time" "recipes/controllers" "github.com/gin-gonic/gin" "gopkg.in/mgo.v2" ) func Router() { mongoInfo := &mgo.DialInfo{ Addrs: []string{"localhost:27017"}, Timeout: 20 * time.Second, Database: "cook-suggestioner", Username: "", Password: "", Source: "cook-suggestioner", } session, err := mgo.DialWithInfo(mongoInfo) if err != nil { panic(err) } defer session.Close() db := session.DB("cook-suggestioner").C("recipes") r := gin.Default() recipesGroup := r.Group("/recipes") recipesGroup.Use(Middleware(db)) { recipesGroup.GET("", controllers.GetAll) recipesGroup.GET("/:id", controllers.Get) recipesGroup.POST("", controllers.Create) recipesGroup.PUT("/:id", controllers.Update) recipesGroup.DELETE("/:id", controllers.Delete) } r.Run() // listen and serve on 0.0.0.0:8080 } func Middleware(db *mgo.Collection) gin.HandlerFunc { return func(c *gin.Context) { c.Set("db", db) c.Next() } }
DBのセッションを取得して各エンドポイントとハンドラを記述。
controllers.go
package controllers import( "errors" "net/http" "strings" "recipes/models" "github.com/google/uuid" "github.com/gin-gonic/gin" "gopkg.in/mgo.v2" ) func GetAll(c *gin.Context) { db := models.DbCollection{} db.DB = c.MustGet("db").(*mgo.Collection) var results []models.Recipe results, err := db.GetAll() if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, err) return } c.JSON(http.StatusOK, results) return } func Create(c *gin.Context){ db := models.DbCollection{} db.DB = c.MustGet("db").(*mgo.Collection) var request models.PostForm err := c.ShouldBindJSON(&request) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, err) return } err = db.Create(request) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, err) return } c.JSON(http.StatusOK,"") return } func Get(c *gin.Context) { db := models.DbCollection{} db.DB = c.MustGet("db").(*mgo.Collection) id, err := uuid.Parse(c.Param("id")) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, err) return } result, err := db.Get(id) if err != nil { if strings.Contains(err.Error(), "not found"){ c.AbortWithStatusJSON(http.StatusNotFound, errors.New("Not found")) return } c.AbortWithStatusJSON(http.StatusInternalServerError, err) return } c.JSON(http.StatusOK, *result) return } func Update(c *gin.Context) { db := models.DbCollection{} db.DB = c.MustGet("db").(*mgo.Collection) id, err := uuid.Parse(c.Param("id")) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, err) return } _, err = db.Get(id) if err != nil { if strings.Contains(err.Error(), "not found"){ c.AbortWithStatusJSON(http.StatusNotFound, errors.New("Not found")) return } c.AbortWithStatusJSON(http.StatusInternalServerError, err) return } var request models.PostForm err = c.ShouldBindJSON(&request) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, err) return } err = db.Update(id, request) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, err) } c.JSON(http.StatusOK, "") } func Delete(c *gin.Context) { db := models.DbCollection{} db.DB = c.MustGet("db").(*mgo.Collection) id, err := uuid.Parse(c.Param("id")) if err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, err) return } _, err = db.Get(id) if err != nil { if strings.Contains(err.Error(), "not found"){ c.AbortWithStatusJSON(http.StatusNotFound, errors.New("Not found")) return } c.AbortWithStatusJSON(http.StatusInternalServerError, err) return } err = db.Delete(id) if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, err) } c.JSON(http.StatusOK, "") }
エンドポイントにリクエストが来たら呼び出されるハンドラ達
model.go
package models import( "github.com/google/uuid" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" ) type PostForm struct { Name string `json:"name" binding:"required"` Foods []string `json:"foods" binding:"required"` } type Recipe struct { Id uuid.UUID `json:"id" bson:"id"` Name string `json:"name" bson:"name"` Foods []string `json:"foods" bson: "foods"` } type DbCollection struct{ DB *mgo.Collection } func (db DbCollection) GetAll() ([]Recipe, error) { var results []Recipe err := db.DB.Find(bson.M{}).All(&results) if err != nil { return nil, err } return results, nil } func (db DbCollection) Create(request PostForm) error { var recipe Recipe recipe.Id = uuid.New() recipe.Name = request.Name recipe.Foods = request.Foods err := db.DB.Insert(&recipe) if err != nil { return err } return nil } func (db DbCollection) Get(id uuid.UUID) (*Recipe, error) { var result Recipe err := db.DB.Find(bson.M{"id": id}).One(&result) if err != nil { return nil, err } return &result, nil } func (db DbCollection) Update(id uuid.UUID, request PostForm) error { update := bson.M{"$set": bson.M{"name": request.Name, "foods": request.Foods}} err := db.DB.Update(bson.M{"id": id}, update) if err != nil { return err } return nil } func (db DbCollection) Delete(id uuid.UUID) error { err := db.DB.Remove(bson.M{"id": id}) if err != nil { return err } return nil }
ハンドラ内でDBとやり取りをする処理をここに記述
色々足りなさ過ぎて丸一日かかった...