はじめに
ニフティでエンジニアをしている添野 翔太です。
ここ最近、@niftyトップページシステム基盤の刷新を進めていく中で、ニフティの様々なサービスのデータを取得するために、APIをGoで作成していました。
そして、API仕様ドキュメントの管理をどうするか悩む中で、こちらの記事(@pei0804さんに感謝)を見つけ、Swagというツールに着目しました。
本稿ではAPI仕様ドキュメントを生成するSwagを導入した際にハマったことを紹介します。
Swagとは
API仕様定義に基づくツールセットSwaggerに関連するツールであり、Go向けのものとなります。
SwaggerにはYAML形式やJSON形式で記述したAPI仕様定義に基づいてHTMLドキュメントを生成する機能があります。Swagを使うと、コードコメントからYAML形式とJSON形式で記述したAPI仕様定義を生成できます。
コード内に記述するため、ドキュメントの更新漏れリスクを低減させることが可能となります。なお、詳しい記述ルールはSwag公式リポジトリにあるREADMEを参照してください。
実際にSwagを使ってみる
実際にコードコメントからYAML形式とJSON形式のAPI仕様定義を生成してみます。
そのために適当なユーザーの一覧を返却するサンプルAPIを用意しました。
以下は、抽出したいユーザーが所属するサービスの識別子をもとに、ユーザー一覧を返却するAPIのコードです。import文の下とhandler関数の下に、Swagで使用されるコードコメントを記載しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
package main import ( "bytes" "encoding/json" "fmt" "log" "net/http" "time" ) // @title ブログ記事用サンプルAPI // @version 1.0 // @description ブログ記事用サンプルAPI // @host localhost:8081 type User struct { ID json.Number `json:"id"` Name string `json:"name"` LastUpdateDate time.Time `json:"last_update_date"` } type Error struct { ID string `json:"id"` Message string `json:"message"` } var users = []User{{ ID: "1", Name: "太郎", LastUpdateDate: time.Now(), }, { ID: "2", Name: "花子", LastUpdateDate: time.Now(), }, { ID: "3", Name: "次郎", LastUpdateDate: time.Now(), }} var error_400 = Error{ ID: "ERROR-001", Message: "リクエストパラメータが不足しています", } // handler godoc // @Summary users // @Description get the users list // @Accept json // @Produce json // @Param service query string true "Service you want to get the users list" // @Success 200 {object} main.User // @Failure 400 {object} main.Error // @Router /users [get] func handler(w http.ResponseWriter, r *http.Request) { var buf bytes.Buffer enc := json.NewEncoder(&buf) service := r.URL.Query().Get("service") fmt.Println("service =>", service) if service == "" { if err := enc.Encode(&error_400); err != nil { log.Fatal(err) } fmt.Println(buf.String()) http.Error(w, buf.String(), 400) return } if err := enc.Encode(&users); err != nil { log.Fatal(err) } fmt.Println(buf.String()) _, err := fmt.Fprint(w, buf.String()) if err != nil { return } } func main() { // GET /users http.HandleFunc("/users", handler) log.Fatal(http.ListenAndServe(":8081", nil)) } |
さて、API仕様ドキュメントを生成するための準備が揃いましたので生成します。
APIのコード(main.go)が存在するディレクトリで以下のコマンドを入力すると、
1 2 |
$ go install github.com/swaggo/swag/cmd/swag@latest $ swag init |
構文解析エラーが表示されました。これが本稿の題名にあるハマりポイントでした。
1 2 3 4 |
Generate swagger docs.... Generate general API Info, search dir:./ Generating main.User ParseComment error in file ./main.go :cannot find type definition: json.Number |
ここでエラーメッセージをもとに調べると、こちらのissueやこちらのissueがヒットし、モジュール依存を解決するためのオプションが必要だと判明しました。
そこで、
1 |
$ swag init --parseDependency --parseInternal --parseDepth 1 |
を実行してみると、今度は上手く行ったようです。
1 2 3 4 5 6 7 |
Generate swagger docs.... Generate general API Info, search dir:./ Generating main.User Generating main.Error create docs.go at docs/docs.go create swagger.json at docs/swagger.json create swagger.yaml at docs/swagger.yaml |
生成されたYAMLファイルを https://editor.swagger.io/ で確認してみると、API仕様ドキュメントを閲覧できました。
おわりに
本稿では、Swagとそれを初めて使ったときにハマったことを紹介しました。
Swagの記述ルールは個人的には分かりやすいと感じていて、導入してみてAPI仕様ドキュメントをすぐに生成できることに感動しました。
皆さんもぜひSwagを使うことを検討してみましょう。