簡単なAPIを作ってみたまとめ

# 簡単なAPIを作ってみた

APIについて色々読んだりしてみたものの、設計ルールをがっつり盛り込んでAPIを設計するほどのアイデアが出てこなかったのでまずはAPIを作る事に慣れようの回とした。

今回はローカルホストだけどこんな感じに返ってくるAPIを作ってみた。

<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/119188b5-bd53-35ec-651c-365ea8d1e3c2.png">

# 参考リンク

今回はこちらをメインに参考

https://qiita.com/entaku0818/items/c29add790718c215381e

https://chicog.me/posts/m30-7wf2g6

Go公式で参考にしたページ

https://pkg.go.dev/net/http

APIの設計ルール諸々紹介されている記事

https://qiita.com/kazuki_tachikawa/items/7dab01ac2ea08b85fb15


# 準備

使用したのはGo言語。PythonのFlaskでも作れるらしい。
（上記に挙げていないけど）色々なAPIの作成に関する記事を見て、読みやすいなと思った事と、Go言語に触った事がないので触ってみたくてGoを使用した。

* 開発環境：VS Code

まずは拡張機能からGoをインストール。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/392ee008-fbcf-80bf-ca9a-62e04f4b2e63.png)

VS CodeでGoのフォルダとファイルを作成する。例えばこんな感じ。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/f67a70aa-0326-174b-59d4-eae36549fd72.png">

この時点で適当にコード書いてデバッグするとgo.modファイルが見つからないよというエラーが発生する。（画像はgo.mod作成後。go.modがない場合にデバッグすると見つからないエラーが発生する。）
なのでVS Codeのターミナルでコマンド `go mod init <好きなモジュール名(今回はapi)>`を実行してファイルを作成する。
作成されたファイルの中身はこちら（一応備忘録として）。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/242f1935-93bf-1535-b277-e8d3b241dab3.png">

# 作成

作成したコードはこちら。
~~Goのコードが手探り状態だったので、全然もう少し上手く書き直せそうではある。~~
```go:Go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type Holiday struct {
	MonthMessage string
	Date         string
	Holiday_Data string
}

func handleRequests() {
	http.HandleFunc("/", homePage)
	http.HandleFunc("/January", returnHolidayData_Jan)
	// 省略しているのでコメントアウト http.HandleFunc("/February", returnHolidayData_Feb)
	// 省略しているのでコメントアウト http.HandleFunc("/March", returnHolidayData_Mar)
	// 省略しているのでコメントアウト http.HandleFunc("/April", returnHolidayData_Apr)
	http.HandleFunc("/May", returnHolidayData_May)

	log.Fatal(http.ListenAndServe(":8081", nil))
}

func homePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Welcome to the HomePage!")
	fmt.Println("Endpoint Hit: homePage")
}

func returnHolidayData_Jan(w http.ResponseWriter, r *http.Request) {
	holiday := []Holiday{}
	arr_date := []string{"1/1", "1/8"}
	arr_name := []string{"元日", "成人の日"}

	for i := 0; i < 2; i++ {
		Msg_txt := fmt.Sprintf(" 1月の%dつ目のお休み", i+1)
		holiday = append(
			holiday,
			Holiday{MonthMessage: Msg_txt, Date: arr_date[i], Holiday_Data: arr_name[i]})
	}
	json.NewEncoder(w).Encode(holiday)
}

//2月～4月は省略

func returnHolidayData_May(w http.ResponseWriter, r *http.Request) {
	holiday := []Holiday{}
	arr_date := []string{"5/3", "5/4", "5/5"}
	arr_name := []string{"憲法記念日", "みどりの日", "こどもの日"}

	for i := 0; i < 3; i++ {
		Msg_txt := fmt.Sprintf(" 5月の%dつ目のお休み", i+1)
		holiday = append(
			holiday,
			Holiday{MonthMessage: Msg_txt, Date: arr_date[i], Holiday_Data: arr_name[i]})
	}
	json.NewEncoder(w).Encode(holiday)
}

func main() {
	handleRequests()
}

```

# コードの詳細
基本的にはメインに参考した記事を元に作成した。
自分の中でへぇ～となったところを以下でピックアップ。

### import
```go:
import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)
```
作るために上記４つのインポートが必要。
`net/http`はAPIリクエスト、レスポンスを処理するために必要なHTTPサーバを実装するために必要なパッケージ。
`encoding/json`はJSONデータを扱うために必要なパッケージ。


### JSONの中身の型
```go:
type Holiday struct {
	MonthMessage string
	Date         string
	Holiday_Data string
}
```
それぞれの値は文字列型に設定。
実際に返す内容は以下のようになる。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/950af125-104e-e155-088a-042b6c250e05.png">

### func handleRequests()とhttp.HandleFunc
```go:
func handleRequests() {
	http.HandleFunc("/", homePage)
    http.HandleFunc("/January", returnHolidayData_Jan)
```
ここではリクエストのURI（`/`や`/January`）からどの関数を呼び出すかを決めている。リクエストが来た時にどのような内容を返すのかのハンドリングはここで設定できるのだなと。~~もっとこうびっくりするようなコードを書くのかと勝手に想像していました何故か。~~

### ポートの指定
```go:
log.Fatal(http.ListenAndServe(":8081", nil))
```
リクエストを受け付けるポート番号はこちらで指定。他の通信と競合しないように気を付ける。
実際のリクエストも、リクエストを受け付けるポートを指定している事がわかる。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/8247c5ca-5475-7e0f-7525-c7b3e831e9d9.png">


### 呼び出される関数の中身
```go:呼び出される関数
func returnHolidayData_May(w http.ResponseWriter, r *http.Request) {
	holiday := []Holiday{}
	arr_date := []string{"5/3", "5/4", "5/5"}
	arr_name := []string{"憲法記念日", "みどりの日", "こどもの日"}

	for i := 0; i < 3; i++ {
		Msg_txt := fmt.Sprintf(" 5月の%dつ目のお休み", i+1)
		holiday = append(
			holiday,
			Holiday{MonthMessage: Msg_txt, Date: arr_date[i], Holiday_Data: arr_name[i]})
	}
	json.NewEncoder(w).Encode(holiday)
}
```
呼び出される関数`returnHolidayData_May`と呼び出し元はこちら。
```go:呼び出し元
func handleRequests() {
//省略
http.HandleFunc("/May", returnHolidayData_May)
```
実際のJSONの中身は呼び出される関数`returnHolidayData_May`にて作成。
上記で紹介したJSONの中身の型`Holiday`の`holiday`を宣言。~~ややこしい。~~
~~クラスとインスタンスに似てるなとふんわり感じた。~~
今回は1月～5月ごとにその月の休日をレスポンスとして返す設計とした。
そのため、`returnHolidayData_May`では、5月の休日の日付、休日の名前を配列に格納。
```go:
holiday := []Holiday{}
	arr_date := []string{"5/3", "5/4", "5/5"}
	arr_name := []string{"憲法記念日", "みどりの日", "こどもの日"}
```

for文は休日の数だけ回す。
~~数字直入れではなくて配列の長さで回した方が良いね。~~
JSONの中身の型`Holiday`のそれぞれの項目`MonthMessage`、`Date`、`Holiday_Data`に値を入れつつ、`holiday`に追加して返す。
```go:
	for i := 0; i < 3; i++ {
		Msg_txt := fmt.Sprintf(" 5月の%dつ目のお休み", i+1)
		holiday = append(
			holiday,
			Holiday{MonthMessage: Msg_txt, Date: arr_date[i], Holiday_Data: arr_name[i]})
	}
```
諸々値を入れた`holiday`を最後にエンコード。

```go:
	json.NewEncoder(w).Encode(holiday)
```

# デバッグしてレスポンスを確認
上記のコードをデバッグする。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/9a5789b6-039f-9df0-3f8d-32f26c043a51.png">

ポップアップが表示されるので「許可」をクリック。
これでリクエスト、レスポンスを処理できる状態となる。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/235f6a6b-fe18-b49d-8eb2-fff79229b65d.png">

Postmanで`/May`のリクエストを送信すると`200 OK`と返ってきている事を確認できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/af855956-ebf1-f3ec-dcfe-d2f63e735933.png)

ボディをJSONに変更する。
<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/82adc76b-d3ec-cbd9-8df0-8ad89f557f04.png">

`holiday`の中身がレスポンスとして返ってきている事が分かる。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2608279/f5b502ce-2711-4d60-5f05-83a722c7a6e9.png">


# さいごに
Go言語の書き方に慣れてくればもう少しレスポンスの中身は綺麗に書けそうだと感じた。
それと次回はAPIの設計ルールを元に、DBの用意と、メソッド周り含めて作りたいAPIの構成を考えた上で、リベンジしたい。