0
Help us understand the problem. What are the problem?

posted at

updated at

【gin】goでAPIサーバー

golangでAPIサーバー

ginというフレームワークを使ってAPIサーバーを建て、CRUD処理を実装します。
プログラム全体はこちらにおいてあります。

環境

MacBook Pro (14-inch, 2021)
Go 1.18.2
gin 1.77

APIの設計

焼き鳥屋の商品情報を扱うものを想定します。

  • 商品一覧の取得
  • 単一商品の取得
  • 新商品の登録
  • 在庫情報の変更
  • 商品の削除

これらを実装します。

ディレクトリとファイル類を作成

$ mkdir go_api_server
$ cd go_api_sever
$ touch main.go # プログラムを書くファイルを作成
$ go mod init go_api_server # モジュールを管理するファイルを作成
$ go mod tidy

使用する外部モジュールであるginもここでインストールしておきます。

$ go get "github.com/gin-gonic/gin"

READ

商品一覧の取得

  • エンドポイント: /menu
  • メソッド: GET

main.goに処理を書いていきます。
リクエスト対して返却するJSONの構造を定義します。
各商品は以下の4つの情報を持ちます。

  • Id
  • Name
  • Price
  • Stock
type menu struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	Price int    `json:"price"`
	Stock bool   `json:"stock"`
}

この定義したmenuという構造体に予めいくつかの商品データを登録しておきます。

var menuList = []menu{
	{Id: 1, Name: "皮", Price: 120, InStock: true},
	{Id: 2, Name: "レバー", Price: 150, InStock: true},
	{Id: 3, Name: "ぼんじり", Price: 180, InStock: true},
}

リクエストに対してjsonを返却する関数です。

func getMenuList(c *gin.Context) {
	c.IndentedJSON(http.StatusOK, menuList)
}

main関数内でルーティングやサーバーの起動を行います。

func main() {
	router := gin.Default()
	router.GET("/menu", getMenuList) // ルーティング
	router.Run("localhost:8080")
}

一度ここでサーバーを起動してみましょう。

$ go run main.go

起動できたら、ターミナルでGETリクエストをしてみます。

$ curl http://localhost:8080/menu
[
    {
        "id": 1,
        "name": "皮",
        "price": 120,
        "instock": true
    },
    {
        "id": 2,
        "name": "レバー",
        "price": 150,
        "instock": true
    },
    {
        "id": 3,
        "name": "ぼんじり",
        "price": 180,
        "instock": true
    }
]

期待通り取得できました。

単一商品の取得

  • エンドポイント: /menu/:id
  • メソッド: GET
    線形的に探索して、idが一致する商品を返却します。
func getItemById(c *gin.Context) {
	id := c.Param("id")
	i, _ := strconv.Atoi(id)
	for _, a := range menuList {
		if a.Id == i {
			c.IndentedJSON(http.StatusOK, a)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Item not found"})
}
func main() {
	router := gin.Default()
	router.GET("/menu", getMenuList)
	router.GET("/menu/:id", getItemById) // new
	router.Run("localhost:8080")
}
$ curl http://localhost:8080/menu/1
{
    "id": 1,
    "name": "皮",
    "price": 120,
    "instock": true
}

CREATE

新商品のを登録を実装。

  • エンドポイント: /menu
  • メソッド: POST
{"id": 4, "name": "はつもと", "price": 200, "instock": true}

新たな商品を登録する関数を定義します。

func postItem(c *gin.Context) {
	var newMenu menu
	err := c.BindJSON(&newMenu)
	if err != nil {
		return
	}
	menuList = append(menuList, newMenu) // 追加処理
	c.IndentedJSON(http.StatusCreated, newMenu)
}

main関数に新たなルーティングの記述を追加します。

func main() {
	router := gin.Default()
	router.GET("/menu", getMenuList)
    router.GET("/menu/:id", getItemById)
	router.POST("/menu", postItem) // new
	router.Run("localhost:8080")
}

再度サーバーを起こして以下を叩いてみましょう。

$ curl http://localhost:8080/menu -X POST -H "Content-Type: application/json" \
   --data '{"id": 4, "name": "はつもと", "price": 200, "instock": true}'

登録された商品のデータが返却されました。

{
    "id": 4,
    "name": "はつもと",
    "price": 200,
    "instock": true
}

登録されたか確認するために商品一覧を取得してみます。

$ curl http://localhost:8080/menu
[
    {
        "id": 1,
        "name": "皮",
        "price": 120,
        "instock": true
    },
    {
        "id": 2,
        "name": "レバー",
        "price": 150,
        "instock": true
    },
    {
        "id": 3,
        "name": "ぼんじり",
        "price": 180,
        "instock": true
    },
    {
        "id": 4,
        "name": "はつもと", # new
        "price": 200,
        "instock": true
    }
]

はつもとが登録されているのが確認できます。

UPDATE

在庫情報の変更

  • エンドポイント: /menu/outofstock/:id
  • メソッド: GET
    レバーが品切れになってしまいました。instockfalseに更新します。
func updateStockById(c *gin.Context) {
	id := c.Param("id")
	i, _ := strconv.Atoi(id)
	for _, a := range menuList {
         // inStockを反転
		if a.Id == i {
			stock := a.InStock
			if stock == false {
				a.InStock = true
			} else {
				a.InStock = false
			}
			c.IndentedJSON(http.StatusOK, a)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Item not found"})
}
func main() {
	router := gin.Default()
	router.GET("/menu", getMenuList)
	router.GET("/menu/:id", getItemById)
	router.POST("/menu", postItem)
	router.GET("/menu/outofstock/:id", updateStockById) // new
	router.Run("localhost:8080")
}

確認します

$ curl http://localhost:8080/menu/outofstock/2
{
    "id": 2,
    "name": "レバー",
    "price": 150,
    "instock": false
}

DELETE

商品の削除

  • エンドポイント: /menu/delete:id
  • メソッド: GET

ボンジリを一覧から削除します。

func DeleteById(c *gin.Context) {
	id := c.Param("id")
	i, _ := strconv.Atoi(id)
	for j, a := range menuList {
		if a.Id == i {
			menuList = append(menuList[:j], menuList[j+1:]...) // 削除部分
			c.IndentedJSON(http.StatusOK, a)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "Item not found"})
}
func main() {
	router := gin.Default()
	router.GET("/menu", getMenuList)
	router.GET("/menu/:id", getItemById)
	router.POST("/menu", postItem)
	router.GET("/menu/outofstock/:id", updateStockById)
	router.GET("/menu/delete/:id", DeleteById) // new
	router.Run("localhost:8080")
}

idが3の商品を削除します

$ curl http://localhost:8080/menu/delete/3
{
    "id": 3,
    "name": "ぼんじり",
    "price": 180,
    "instock": true
}

一覧から削除されていることを確認します。
サーバーを起こし直したので、はつもとが無いのは期待通りです。

$ curl http://localhost:8080/menu
[
    {
        "id": 1,
        "name": "皮",
        "price": 120,
        "instock": true
    },
    {
        "id": 2,
        "name": "レバー",
        "price": 150,
        "instock": true
    }
]

おわりに

とっても簡単にAPIサーバーの構築ができました。次はDBにデータを登録できるよう実装します。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?