はじめに
Webシステムでよく使われるREST-API、個人的に勉強した際「ドキュメントや実装を整理したいよな~」とか思って自分で頑張ろうとしていました。
しかし世の中には標準というものがあるmので、REST-APIにも**OpenAPI(swagger)**という記述方法が一般的になっています。
ここではswaggerと、swagger+golang web frameworkを利用出来るツールの紹介をしていきます。
OpenAPI(swagger)とは
ざっくりいうと、「REST-APIの定義に関する記述方法を取り決めたもの」になります。一定の法則に従って記述するため、整形された描画や自動生成が可能となっています。
詳細なSpecについてはOpenAPI Specificationを参照ください。
整形された描画
ファイルの形式はYAMLかJSON形式のファイルとなっています。
ファイル自体はテキストベースなんですが、これをswagger-uiのようなツールを使って開くと、綺麗に成形されたドキュメントに早変わりします。
以下図はswagger-uiのサイトから抜粋。/pet
というURIに対してGET/POST/PUT/DELETEでアクセスするとどうなるか?が見やすく表示されています。めっちゃ便利!
他にはswagger editorというものも非常に便利です。
ローカルインストールもオンラインでの利用も出来るエディタで、ファイルのインポート・エクスポートやJSON⇔YAMLの変換も出来ます。
自動生成
書き方が決まっているので、Swaggerファイルからコードのひな型を出力したり、またはコードからSwaggerファイルを作成したりといったことが可能になります。
「swagger コード生成」等で検索すると沢山情報が出てきます。swagger-codegen等使えそうなものが沢山ありますね。(触っていないので割愛)
Swagger→コード生成はこの辺りの記事が参考になりそうです。
まだAPI定義管理で消耗してるの?〜Swaggerを用いた大規模アプリ時代のAPI定義管理とコードジェネレート〜
ただ、現場に元々いた人の話を聞くと、ドキュメント作成→コード生成だと、最初はいいけど変更が多くなると割と面倒が増えるので、コード→Swaggerファイル作成の方がメンテナンスが楽かもという話。
個人的な経験としても、ドキュメント→コードよりもコード→ドキュメントの自動生成の方が嫌がる人が少ない、ハードルが低い印象があるので、こちらに注目していきたいと考えています。
Golangでのswagger利用例
golangのwebフレームワークgin-gonic/ginの中のissue Automatically generate RESTful API documentation with Swaggerで色々な方法がやり取りされていました。その中で今回はgithub.com/savaki/swagを紹介。
github.com/savaki/swagによる生成
まずは必要なライブラリを取得しましょう。コードのサンプルとして標準とgin-gonic/gin、labstack/echo、gorilla/mux、julienschmidt/httprouterというWebフレームワークのサンプルが用意されています。
今回はgin-gonic/ginを使ってみます。
$ 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を起動するだけ。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が取得できるようになります。
router.GET("/swagger", gin.WrapH(api.Handler(enableCors)))
api情報はこんな感じで各URIに対して必要な情報と、swaggerに吐き出すための情報を記載していく形です。
ただコードを書くよりは情報が増えますが、「コメントで説明する代わりに説明文を登録してる」くらいに考えれば、ドキュメントを書くよりは手間じゃないかな。
他のツールではコメントルールによりファイル出力をするものもありましたが、こちらの方がコードの整理にも繋がるので個人的には好きです。
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の導入としてもよさそうですね。