Golangで学ぶREST-APIの世界　OpenAPI(Swagger)

## はじめに

Webシステムでよく使われるREST-API、個人的に勉強した際「ドキュメントや実装を整理したいよな～」とか思って[自分で頑張ろう](https://qiita.com/developer-kikikaikai/items/c354c85880b993268649)としていました。
しかし世の中には標準というものがあるｍので、REST-APIにも**OpenAPI(swagger)**という記述方法が一般的になっています。
ここではswaggerと、swagger+golang web frameworkを利用出来るツールの紹介をしていきます。

## OpenAPI(swagger)とは

ざっくりいうと、「REST-APIの定義に関する記述方法を取り決めたもの」になります。一定の法則に従って記述するため、整形された描画や自動生成が可能となっています。
詳細なSpecについては[OpenAPI Specification](https://swagger.io/specification/)を参照ください。

### 整形された描画

ファイルの形式は[YAML](https://ja.wikipedia.org/wiki/YAML)か[JSON](https://ja.wikipedia.org/wiki/JavaScript_Object_Notation)形式のファイルとなっています。
ファイル自体はテキストベースなんですが、これを[swagger-ui](https://swagger.io/tools/swagger-ui/)のようなツールを使って開くと、綺麗に成形されたドキュメントに早変わりします。

以下図はswagger-uiのサイトから抜粋。`/pet`というURIに対してGET/POST/PUT/DELETEでアクセスするとどうなるか？が見やすく表示されています。めっちゃ便利！
![](https://swagger.io/swagger/media/Images/Tools/Opensource/Swagger_UI.png?ext=.png)

他には[swagger editor](https://editor.swagger.io/)というものも非常に便利です。
ローカルインストールもオンラインでの利用も出来るエディタで、ファイルのインポート・エクスポートやJSON⇔YAMLの変換も出来ます。

### 自動生成

書き方が決まっているので、Swaggerファイルからコードのひな型を出力したり、またはコードからSwaggerファイルを作成したりといったことが可能になります。
「swagger コード生成」等で検索すると沢山情報が出てきます。[swagger-codegen](https://github.com/swagger-api/swagger-codegen)等使えそうなものが沢山ありますね。(触っていないので割愛)
Swagger→コード生成はこの辺りの記事が参考になりそうです。
[まだAPI定義管理で消耗してるの？〜Swaggerを用いた大規模アプリ時代のAPI定義管理とコードジェネレート〜](https://speakerdeck.com/magiepooh/madaapiding-yi-guan-li-dexiao-hao-siterufalse-swaggerwoyong-itada-gui-mo-apurishi-dai-falseapiding-yi-guan-li-tokodozienereto)

ただ、現場に元々いた人の話を聞くと、ドキュメント作成→コード生成だと、最初はいいけど変更が多くなると割と面倒が増えるので、コード→Swaggerファイル作成の方がメンテナンスが楽かもという話。
個人的な経験としても、ドキュメント→コードよりもコード→ドキュメントの自動生成の方が嫌がる人が少ない、ハードルが低い印象があるので、こちらに注目していきたいと考えています。

## Golangでのswagger利用例

golangのwebフレームワーク[gin-gonic/gin](https://github.com/gin-gonic/gin)の中のissue [Automatically generate RESTful API documentation with Swagger](https://github.com/gin-gonic/gin/issues/155)で色々な方法がやり取りされていました。その中で今回は[github.com/savaki/swag](https://github.com/savaki/swag)を紹介。

### [github.com/savaki/swag](https://github.com/savaki/swag)による生成

まずは必要なライブラリを取得しましょう。コードのサンプルとして標準と[gin-gonic/gin](https://github.com/gin-gonic/gin)、[labstack/echo](https://github.com/labstack/echo)、[gorilla/mux](https://github.com/gorilla/mux)、[julienschmidt/httprouter](https://github.com/julienschmidt/httprouter)というWebフレームワークのサンプルが用意されています。

今回はgin-gonic/ginを使ってみます。

```console
$ go get github.com/savaki/swag
$ go get github.com/gin-gonic/gin
```

使い方はsampleを動かした方がはやいのでコードをcloneします。

```
$ git clone https://github.com/savaki/swag.git
```

後は[swag/examples/gin/main.go](https://github.com/savaki/swag/blob/master/examples/gin/main.go)を起動するだけ。ginの場合は設定したAPIの情報が出力されます。

```
$ cd swag/examples/gin/
$ go run main.go
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] POST   /pet                      --> main.handle (1 handlers)
[GIN-debug] GET    /pet/:petId               --> main.handle (1 handlers)
[GIN-debug] GET    /swagger                  --> github.com/gin-gonic/gin.WrapH.func1 (1 handlers)
```

ここで気になるのが`/swagger`。コードとしては以下のような形で、apiというのはURIとmethod等の情報を登録したものになります。
これをやっておくと、`/swagger`にアクセスした際にJSON形式のswaggerが取得できるようになります。

```go:main.go
router.GET("/swagger", gin.WrapH(api.Handler(enableCors)))
```

api情報はこんな感じで各URIに対して必要な情報と、swaggerに吐き出すための情報を記載していく形です。
ただコードを書くよりは情報が増えますが、「コメントで説明する代わりに説明文を登録してる」くらいに考えれば、ドキュメントを書くよりは手間じゃないかな。
他のツールではコメントルールによりファイル出力をするものもありましたが、こちらの方がコードの整理にも繋がるので個人的には好きです。

```go:main.go
        post := endpoint.New("post", "/pet", "Add a new pet to the store",
                endpoint.Handler(handle),
                endpoint.Description("Additional information on adding a pet to the store"),
                endpoint.Body(Pet{}, "Pet object that needs to be added to the store", true),
                endpoint.Response(http.StatusOK, Pet{}, "Successfully added pet"),
        )
        get := endpoint.New("get", "/pet/{petId}", "Find pet by ID",
                endpoint.Handler(handle),
                endpoint.Path("petId", "integer", "ID of pet to return", true),
                endpoint.Response(http.StatusOK, Pet{}, "successful operation"),
        )

        api := swag.New(
                swag.Endpoints(post, get),
        )

        router := gin.New()
        api.Walk(func(path string, endpoint *swagger.Endpoint) {
                h := endpoint.Handler.(func(c *gin.Context))
                path = swag.ColonPath(path)

                router.Handle(endpoint.Method, path, h)
        })
```

実際に`/swagger`にアクセスして取得した情報をswagger editorで表示するとこのような形になります。
JSON形式を自動でYAMLに変換してもらえるのが便利。後はswaggerの書式を細かく覚えなくてもいいので、swaggerの導入としてもよさそうですね。

<img width="959" alt="swagger.png" src="https://qiita-image-store.s3.amazonaws.com/0/252277/7451bedb-9e7e-98aa-e6c5-600c8d9cc95b.png">