【Go Echo】Swaggerチュートリアル

# 【Go Echo】Swaggerチュートリアル

このチュートリアルでは、GoのEchoフレームワークを使用してCRUD APIサーバーを構築し、Swaggerを用いてAPIドキュメントを生成する手順を説明します。
また、Swaggerアノテーションの詳細な解説も後半で行います。

## 前提条件

- Goがインストールされていること
- 基本的なGoの知識があること

## 1. プロジェクトのセットアップ

まず、プロジェクト用のディレクトリを作成し、初期化します。

```bash
mkdir echo-swagger-crud
cd echo-swagger-crud
go mod init github.com/yourusername/echo-swagger-crud
```

## 2. 必要なパッケージのインストール

EchoフレームワークとSwaggerツールをインストールします。

```bash
go get github.com/labstack/echo/v4
go get github.com/swaggo/echo-swagger
go get github.com/swaggo/swag/cmd/swag
```

```bash
go mod tidy
```

`swag`コマンドをパスに追加します（必要に応じて）。

```bash
echo 'export PATH=$PATH:$(go env GOPATH)/bin' >> ~/.zshrc
source ~/.zshrc
```

## 3. CRUD APIの実装

`main.go`ファイルを作成し、基本的なCRUDエンドポイントを実装します。

```go
package main

import (
    "net/http"

    "github.com/labstack/echo/v4"
    "github.com/labstack/echo/v4/middleware"
    echoSwagger "github.com/swaggo/echo-swagger"
    _ "github.com/yourusername/echo-swagger-crud/docs"
)

// @title Echo CRUD API
// @version 1.0
// @description サンプルのCRUD APIです。
// @host localhost:8080
// @BasePath /api/v1
func main() {
    e := echo.New()

    // ミドルウェア
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    // ルート
    e.GET("/swagger/*", echoSwagger.WrapHandler)

    // APIグループ
    api := e.Group("/api/v1")
    {
        api.GET("/items", getItems)
        api.GET("/items/:id", getItem)
        api.POST("/items", createItem)
        api.PUT("/items/:id", updateItem)
        api.DELETE("/items/:id", deleteItem)
    }

    e.Start(":8080")
}

// Item represents a simple item
type Item struct {
    ID    string `json:"id"`
    Name  string `json:"name"`
    Price int    `json:"price"`
}

// 仮のデータ
var items = map[string]Item{}

// @Summary Get all items
// @Tags items
// @Success 200 {array} Item
// @Router /items [get]
func getItems(c echo.Context) error {
    var list []Item
    for _, item := range items {
        list = append(list, item)
    }
    return c.JSON(http.StatusOK, list)
}

// @Summary Get an item by ID
// @Tags items
// @Param id path string true "Item ID"
// @Success 200 {object} Item
// @Failure 404 {string} string "Not Found"
// @Router /items/{id} [get]
func getItem(c echo.Context) error {
    id := c.Param("id")
    item, exists := items[id]
    if !exists {
        return c.String(http.StatusNotFound, "Not Found")
    }
    return c.JSON(http.StatusOK, item)
}

// @Summary Create a new item
// @Tags items
// @Param item body Item true "Item to create"
// @Success 201 {object} Item
// @Router /items [post]
func createItem(c echo.Context) error {
    var item Item
    if err := c.Bind(&item); err != nil {
        return err
    }
    items[item.ID] = item
    return c.JSON(http.StatusCreated, item)
}

// @Summary Update an existing item
// @Tags items
// @Param id path string true "Item ID"
// @Param item body Item true "Updated item"
// @Success 200 {object} Item
// @Failure 404 {string} string "Not Found"
// @Router /items/{id} [put]
func updateItem(c echo.Context) error {
    id := c.Param("id")
    if _, exists := items[id]; !exists {
        return c.String(http.StatusNotFound, "Not Found")
    }
    var item Item
    if err := c.Bind(&item); err != nil {
        return err
    }
    items[id] = item
    return c.JSON(http.StatusOK, item)
}

// @Summary Delete an item
// @Tags items
// @Param id path string true "Item ID"
// @Success 204 {string} string "No Content"
// @Failure 404 {string} string "Not Found"
// @Router /items/{id} [delete]
func deleteItem(c echo.Context) error {
    id := c.Param("id")
    if _, exists := items[id]; !exists {
        return c.String(http.StatusNotFound, "Not Found")
    }
    delete(items, id)
    return c.NoContent(http.StatusNoContent)
}
```

## 4. Swaggerドキュメントの生成

`swag`を使用してSwaggerドキュメントを生成します。

```bash
swag init
```

これにより、`docs`ディレクトリが作成され、Swaggerドキュメントが生成されます。

## 5. サーバーの起動

以下のコマンドでサーバーを起動します。

```bash
go run main.go
```

## 6. Swagger UIの確認

ブラウザで [http://localhost:8080/swagger/index.html](http://localhost:8080/swagger/index.html) にアクセスすると、Swagger UIが表示され、APIのドキュメントを確認できます。

## 7. アノテーションの解説

Swaggerアノテーションを使用してAPIドキュメントを生成するために、コード内に特定のコメントを追加します。
以下では、主なアノテーションについて解説します。

### 7.1 主なアノテーション一覧

#### 1. API全体の設定

ファイルやパッケージの先頭にAPI全体の情報を記述します。
これにより、Swaggerドキュメントの基本情報が設定されます。

```go
// @title Echo CRUD API
// @version 1.0
// @description サンプルのCRUD APIです。
// @host localhost:8080
// @BasePath /api/v1
```

- `@title`: APIのタイトル
- `@version`: バージョン番号
- `@description`: APIの説明
- `@host`: APIのホスト名
- `@BasePath`: ベースとなるパス

#### 2. ハンドラー関数のアノテーション

各APIエンドポイントに対応するハンドラー関数にアノテーションを追加します。
これにより、エンドポイントごとの詳細なドキュメントが生成されます。

##### 基本構造

```go
// @Summary 簡単な説明
// @Description 詳細な説明
// @Tags タグ名
// @Accept リクエストのコンテンツタイプ
// @Produce レスポンスのコンテンツタイプ
// @Param パラメータ名 パラメータの場所 型 必須 "説明"
// @Success ステータスコード {型} モデル "説明"
// @Failure ステータスコード {型} モデル "説明"
// @Router パス [メソッド]
func ハンドラー関数名(c echo.Context) error {
    // 実装
}
```

##### 具体例

```go
// @Summary 全てのアイテムを取得
// @Description データベースから全てのアイテムを取得します。
// @Tags items
// @Accept json
// @Produce json
// @Success 200 {array} Item
// @Router /items [get]
func getItems(c echo.Context) error {
    // 実装
}
```

- `@Summary`: エンドポイントの概要
- `@Description`: エンドポイントの詳細説明
- `@Tags`: エンドポイントの分類タグ
- `@Accept`: リクエストのコンテンツタイプ（例: json）
- `@Produce`: レスポンスのコンテンツタイプ（例: json）
- `@Param`: パラメータの定義
  - `{パラメータ名}`: パラメータの名前
  - `{場所}`: パラメータの場所（path, query, bodyなど）
  - `{型}`: パラメータのデータ型
  - `{必須}`: パラメータが必須かどうか（true/false）
  - `"{説明}"`: パラメータの説明
- `@Success`: 成功時のレスポンス定義
  - `{ステータスコード}`: HTTPステータスコード
  - `{型}`: レスポンスのデータ型
  - `{モデル}`: レスポンスで使用するモデル
  - `"{説明}"`: レスポンスの説明
- `@Failure`: 失敗時のレスポンス定義
- `@Router`: エンドポイントのパスとHTTPメソッド

#### 3. パラメータの定義

パラメータは`@Param`アノテーションを使用して定義します。

```go
// @Param id path string true "アイテムID"
```

- `id`: パラメータ名
- `path`: パラメータの場所（path, query, header, body）
- `string`: パラメータの型
- `true`: 必須であることを示す
- `"アイテムID"`: パラメータの説明

**構文:**

```
@Param {name} {in} {type} {required} "{description}"
```

**例:**

パスパラメータとして`id`を必須とする場合

```go
// @Param id path string true "アイテムID"
```

#### 4. レスポンスの定義

成功時や失敗時のレスポンスは`@Success`と`@Failure`で定義します。

```go
// @Success 200 {object} Item "成功時の説明"
// @Failure 404 {object} HTTPError "見つからない場合の説明"
```

- `@Success`: 成功時のレスポンス
- `@Failure`: 失敗時のレスポンス

**構文:**

```
@Success {status_code} {type} {model} "{description}"
```

**例:**

```go
// @Success 201 {object} Item "アイテム作成成功"
// @Failure 400 {object} HTTPError "不正なリクエスト"
```

#### 5. モデル（構造体）のアノテーション

APIで使用するデータモデルにもアノテーションを追加できます。これにより、Swagger UIでモデルの詳細が表示されます。

```go
// Item represents a single item in the store
// @Description ストア内のアイテムの構造体
type Item struct {
    // @Description アイテムの一意な識別子
    ID    string `json:"id" example:"1"`
    // @Description アイテムの名前
    Name  string `json:"name" example:"Sample Item"`
    // @Description アイテムの価格（セント単位）
    Price int    `json:"price" example:"1000"`
}
```

- `@Description`: 構造体やフィールドの説明
- `example`: 各フィールドの例示値

### 7.2 モデルの詳細説明

モデル（構造体）にアノテーションを追加することで、Swagger UI上でモデルの詳細な説明や例示値を表示できます。
これにより、APIを利用する開発者に対して、データ構造の理解を深めてもらうことが可能です。

**例:**

```go
// Item represents a single item in the store
// @Description ストア内のアイテムの構造体
type Item struct {
    // @Description アイテムの一意な識別子
    ID    string `json:"id" example:"1"`
    // @Description アイテムの名前
    Name  string `json:"name" example:"Sample Item"`
    // @Description アイテムの価格（セント単位）
    Price int    `json:"price" example:"1000"`
}
```

- 各フィールドに`@Description`を追加することで、フィールドの役割や意味を明確に伝えることができます。
- `example`タグを使用して、各フィールドの具体的な例を示すことができます。これにより、API利用者が期待されるデータ形式や値を理解しやすくなります。

## 8. まとめ

以上で、GoのEchoフレームワークを使用したCRUD APIサーバーにSwaggerを実装する基本的な手順は完了です。
アノテーションを活用することで、APIの設計とドキュメント化が容易になり、開発効率が向上します。
Swagger UIを利用することで、APIの仕様を視覚的に確認・共有できるため、チーム開発においても非常に有用です。

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/