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
レバーが品切れになってしまいました。instock
をfalse
に更新します。
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にデータを登録できるよう実装します。