Go言語でREST APIを作成する

Go言語を使用してREST APIを作成する方法を記載します。
この記事では、基本的なHTTPメソッド（GET, POST, PUT, DELETE）を使用して、RESTfulなエンドポイントを作成します。

## 必要なパッケージ

以下のパッケージを使用します。

- `net/http`：HTTPクライアントとサーバーの作成に使用します。
- `encoding/json`：JSONのエンコーディングとデコーディングに使用します。
- `github.com/gorilla/mux`：リクエストルーティングとディスパッチに使用します。

## データモデル

まず、APIが扱うデータのモデルを定義します。この例では、`Book`という構造体を作成します。

```go
type Book struct {
	ID     int    `json:"id"`     // BookのID
	Title  string `json:"title"`  // Bookのタイトル
	Author string `json:"author"` // Bookの著者
}
```

## エンドポイントの作成

以下に、REST APIの各エンドポイントを作成する方法を示します。

### GET /books

すべてのBookを取得します。

```go
func getBooks(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")  // レスポンスのContent-Typeを設定
	json.NewEncoder(w).Encode(books)  // booksをJSONにエンコードしてレスポンスボディに書き込む
}
```

### GET /books/{id}

特定のIDのBookを取得します。

```go
func getBook(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")  // レスポンスのContent-Typeを設定
	params := mux.Vars(r)  // URLパラメータを取得

	i := findIndex(params["id"])  // Bookのインデックスを取得

	// Bookが見つからない場合は400エラーを返す
	if i == -1 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "No book found with given ID")
		return
	}

	json.NewEncoder(w).Encode(books[i])  // 見つかったBookをJSONにエンコードしてレスポンスボディに書き込む
}
```

### POST /books

新しいBookを作成します。

```go
func createBook(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")  // レスポンスのContent-Typeを設定
	var book Book
	_ = json.NewDecoder(r.Body).Decode(&book)  // リクエストボディのJSONをBookにデコード
	book.ID = id  // 新しいIDを割り当て
	id++  // IDをインクリメント
	books = append(books, book)  // booksに新しいBookを追加
	json.NewEncoder(w).Encode(book)  // 作成されたBookをJSONにエンコードしてレスポンスボディに書き込む
}
```

### PUT /books/{id}

特定のIDのBookを更新します。

```go
func updateBook(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")  // レスポンスのContent-Typeを設定
	params := mux.Vars(r)  // URLパラメータを取得

	i := findIndex(params["id"])  // Bookのインデックスを取得

	// Bookが見つからない場合は400エラーを返す
	if i == -1 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "No book found with given ID")
		return
	}

	books = append(books[:i], books[i+1:]...)  // 元のBookをbooksから削除
	var book Book
	_ = json.NewDecoder(r.Body).Decode(&book)  // リクエストボディのJSONをBookにデコード
	book.ID = id  // 新しいIDを割り当て
	id++  // IDをインクリメント
	books = append(books, book)  // booksに新しいBookを追加
	json.NewEncoder(w).Encode(book)  // 更新されたBookをJSONにエンコードしてレスポンスボディに書き込む
}
```

### DELETE /books/{id}

特定のIDのBookを削除します。

```go
func deleteBook(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")  // レスポンスのContent-Typeを設定
	params := mux.Vars(r)  // URLパラメータを取得

	i := findIndex(params["id"])  // Bookのインデックスを取得

	// Bookが見つからない場合は400エラーを返す
	if i == -1 {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "No book found with given ID")
		return
	}

	books = append(books[:i], books[i+1:]...)  // Bookをbooksから削除
}
```

## サーバーの起動

最後に、以下のコードでサーバーを起動します。

```go
func main() {
	r := mux.NewRouter()  // ルーターを作成

	// エンドポイントとハンドラ関数を関連付け
	r.HandleFunc("/books", getBooks).Methods("GET")
	r.HandleFunc("/books/{id}", getBook).Methods("GET")
	r.HandleFunc("/books", createBook).Methods("POST")
	r.HandleFunc("/books/{id}", updateBook).Methods("PUT")
	r.HandleFunc("/books/{id}", deleteBook).Methods("DELETE")

	fmt.Println("Server starting at 8000")  // サーバー起動メッセージを表示
	http.ListenAndServe(":8000", r)  // HTTPサーバ