GoのGinで簡易APIサーバー

作成：2023年3月13日
[前回まで](https://qiita.com/RYO_/items/265f6cb3e0834a116118)で、Goで簡易APIサーバー（GETのみ）とReactで簡易APIクライアントを試作しました。
今回はGinを使って、Goの簡易APIサーバーにPOST等の機能を追加します。

# Ginを使ってGET機能作成
Ginをインストールします。
また、CORS (Cross-Origin Resource Sharing )：javascript などで API を叩く時に下記のようなエラーが出る場合のサーバーサイド側の対応として cors をインストールします。

Access to XMLHttpRequest at 'http://api.sample.com' from origin 'http://sample.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
% go mod init example/go-api
go: creating new go.mod: module example/go-api
go: to add module requirements and sums:
	go mod tidy
% go get github.com/gin-gonic/gin
% go get github.com/gin-contrib/cors
```

GETまでコードを書きます（[Github](https://github.com/ryoyuki6/go-api202301/blob/qiita20230310a/go-api/main.go))
```:main.go
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-contrib/cors"
	"net/http"
	// "strconv"
)

type HealthData struct {
	Month	int		`json:"month"`
	Day		int		`json:"day"`
	Weight	float64	`json:"weight"`
}

var health_data = []HealthData{
	{Month:	1,	Day:	1,	Weight:	55.5},
	{Month:	1,	Day:	2,	Weight:	66.6},
	{Month:	1,	Day:	3,	Weight:	77.7},
}

func getHalthdata(c *gin.Context) {
	c.IndentedJSON(http.StatusOK, health_data)
}

func main() {
	router := gin.Default()

	// CORS 対応
	config := cors.DefaultConfig()
	config.AllowOrigins = []string{"http://localhost:3000"}
	router.Use(cors.New(config))

	router.GET("/health_data", getHalthdata)
	router.Run("localhost:8080")
}
```

実行して、PostmanでGETできていることが確認できました。
```
 % go run .
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:	export GIN_MODE=release
 - using code:	gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /health_data              --> main.getHalthdata (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on localhost:8080
[GIN] 2023/03/10 - 21:52:00 | 200 |      89.092µs |       127.0.0.1 | GET      "/health_data"
```
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/e0af98c4-5762-72ba-59de-fba1938f4bd6.png">

ReactのAPIクライアントでもGETできました（[Github](https://github.com/ryoyuki6/go-api202301/tree/qiita20230310a/react))
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/e1bff024-30a7-6a85-0d23-2e5bb22d8482.png">

# POST機能を追加
コードを追加します（[Githubのコード置き場:qiita20230311](https://github.com/ryoyuki6/go-api202301/tree/qiita20230311/go-api)）
```
func postHalthdata(c *gin.Context) {
	var newHalthdata HealthData
	if err := c.BindJSON(&newHalthdata); err != nil {
		return
	}
	health_data = append(health_data, newHalthdata)
}

func main() {

	router.POST("/health_data", postHalthdata)

}
```
実行して Postman でPOSTしてみます。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/523172f2-955c-775c-5e12-eeda88ad4ca4.png">

試しに GET してみたら、ちゃんとデータ追加されていました。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/1c946735-0c4c-e542-c824-d09165ec8889.png">

# 以降の機能追加のために id を追加
コードに追加（[Githubのコード：qiita20230311a](https://github.com/ryoyuki6/go-api202301/tree/qiita20230311a)）
```:main.go
type HealthData struct {
	Id		int		`json:"id"`

var health_data = []HealthData{
	{Id:1, Month:1,	Day:1, Weight:55.5},
```
```:App.js
        {getData.map((data) => (
          <li key={data.day}>
            id : {data.id}, month : {data.month}, day : {data.day}, weight : {data.weight}
```
実行して GET したら id付きで取れています。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/955d86e3-cf16-c5de-dad8-309ee1c1935d.png">
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/ff136e59-9489-2350-3ec2-9c3a4a34750e.png">

# 指定したidを1件GETする機能

コードを追加します（[Githubのコード：qiita20230311c](https://github.com/ryoyuki6/go-api202301/tree/qiita20230311c)）
```:main.go
func getHealthdataById(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		return
	}
	for _, t := range health_data {
		if t.ID == id {
			c.IndentedJSON(http.StatusOK, t)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "the ID data does not exit!!!"})
}

func main() {

	router.GET("/health_data/:id", getHealthdataById)
```

id に 2 を指定してデータが取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/de6d79ab-3263-ccef-b4ea-470e725216a4.png)

# PATCH でデータを更新する機能を追加
コードを更新します（[Githubのコード置き場：qiita20230312](https://github.com/ryoyuki6/go-api202301)）
```:main.go
func patchHealthdata(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		return
	}

	var health_data_patch HealthData
	health_data_patch.ID = id
	if err = c.BindJSON(&health_data_patch); err != nil {
		return
	}

	for i, t := range health_data {
		if t.ID == id {
			health_data[i] = health_data_patch
			c.IndentedJSON(http.StatusOK, health_data_patch)
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "the id's health data not fount"})
}

func main() {

	router.PATCH("/health_data/:id", patchHealthdata)
```
実行してPostmanで確認すると、id:2が更新されてレスポンスがありました。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/8c5dadab-6916-28f5-ad99-f4f450f541ba.png">

# DELETE で指定した id のデータを1件削除する機能
コードを更新します（[Githubのコード置き場：qiitq20230313b](https://github.com/ryoyuki6/go-api202301/tree/qiita20230313b)）
```:main.go
func deleteHealthdata(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		return
	}

	for i, t := range health_data {
		if t.ID == id {
			health_data = append(health_data[:i],health_data[i+1:]...)
			c.IndentedJSON(http.StatusOK, gin.H{"message": "health_data(" + strconv.Itoa(id) + ") is deleted"})
			return
		}
	}
	c.IndentedJSON(http.StatusNotFound, gin.H{"message": "health_data not found"})
}

func main() {

	router.DELETE("/health_data/:id", deleteHealthdata)
```
実行 % go run . すると、 id:2 が削除されました。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/298045/ca3f9c81-40d4-949b-4206-5beafcece998.png">

以上で、5つのAPIをGoのGinで実装できました。次は簡易APIクライアントのReactを更新していきたいと思います。
```
	router.GET("/health_data", getHealthdata)
	router.GET("/health_data/:id", getHealthdataById)
	router.POST("/health_data", postHealthdata)
	router.PATCH("/health_data/:id", patchHealthdata)
	router.DELETE("/health_data/:id", deleteHealthdata)
```

# 参考
・[GoのフレームワークGinを利用してRESTfulAPIを開発する方法](https://xminatolog.com/post/2518)
・[go mod init](https://blog.framinal.life/entry/2021/04/11/013819)
・[Golang フレームワーク Gin で CORS対応](http://psychedelicnekopunch.com/archives/820)
・[DockerComposeでGoのGinとMySqlの環境を作ってみた-第2弾](https://www.w2solution.co.jp/corporate/tech/dockercompose%E3%81%A7go%E3%81%AEgin%E3%81%A8mysql%E3%81%AE%E7%92%B0%E5%A2%83%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F-%E7%AC%AC2%E5%BC%BE/)