Goで作ったAPIのドキュメントを自動で生成する

こんにちは、Pairs事業部の後藤です。

今回は私が作ったAPIドキュメント自動生成ツール apidoc を紹介します。
 
https://github.com/gotokatsuya/apidoc

view.v1

apidocを作った経緯

社内ツールなど、弊社で使用している中小規模のアプリケーションのほとんどは、僕がGoで作っているのですが、それらのプロジェクトのAPIに関するドキュメントは十分であるとは言えません。
 
一人で開発するなら必要無いかもしれませんが、クライアントサイドとの連携が必要であったり、最近はメンバーの数も増え複数人で開発するケースが多くあったりすることから、ドキュメントが無いと辛い気持ちになります。
 
ですが、ドキュメントを書くのが非常に面倒であることもまた事実。そこで、自動化することで僕自身が少しでも楽をできたらという思いに駆られ作成するに至りました。
 
今回ご紹介するライブラリとは別ですが、JsonSchemaからAPIドキュメントを自動生成しているプロジェクトもあるので、そちらを参考にするのも良いと思います。

 

JSON Schema から API 仕様と Go コードを自動で生成する

 

apidocの使い方

$ go get github.com/gotokatsuya/apidoc

今回は gin に的を絞って解説していきたいと思います。

 

標準的な net/http を利用したサンプルも用意してあります。詳細は以下のGitHubページをご覧ください。
https://github.com/gotokatsuya/apidoc/tree/master/example/basic
 
順を追って、apidocに関する説明をしていきます。

apidocを初期化する

apidocを無効化するためのフラグを指定可能にしておくと、テスト時のみドキュメントを書き出すなどの分岐が可能になります。

func init() {
	d := flag.Bool("d", false, "disable api doc")
	flag.Parse()
	if *d {
		apidoc.Disable()
	}
	if apidoc.IsDisabled() {
		return
	}

	// DocumentTitle : APIドキュメントのタイトル名
	// DocumentPath  : APIドキュメントのファイル名
	// TemplatePath  : APIドキュメントのテンプレートファイル名
	apidoc.Init(apidoc.Project{
		DocumentTitle: "example-gin",
		DocumentPath:  "custom-apidoc.html",
		TemplatePath:  "custom.tpl.html",
	})
}

テンプレートは自由に作成できます。

渡しているのは title, apis という変数です。

template.Execute(io.Writer(documentFile), map[string]interface{}{
	"title": project.DocumentTitle,
	"apis":  project.APIs,
})

titleはAPIドキュメントのタイトル名
apisはAPI情報を格納した構造体の配列

になっています。これらを使ってカスタムテンプレートを作成することが可能です。

API構造体の定義

gin用にapidocのミドルウェアを作成する

func apidocMiddleware(c *gin.Context) {
	// apidocが無効になっていれば処理を終了
	if apidoc.IsDisabled() {
		return
	}

	// API情報を格納する構造体を宣言する
	api := apidoc.NewAPI()

	// APIドキュメントに書き出したくないリクエストヘッダーを指定する
	api.SuppressedRequestHeaders("Cache-Control", "Content-Length", "X-Request-Id", "ETag", "Set-Cookie")
	
	// http.Request構造体からリクエストに関する情報をapi構造体にセットする
	// 第2引数はReadRequest関数がErrorを返すようにするかどうかの真理値
	api.ReadRequest(c.Request, false)

	// ginにはインターナルなResponseWriterが存在しているので、Write(), WriteString() 関数をオーバーライドしてレスポンスボディを取得する
	gbw := newGinBodyWriter(c.Writer)
	c.Writer = gbw

	// リクエストを処理する
	c.Next()

	// APIドキュメントに書き出したくないレスポンスヘッダーを指定する
	api.SuppressedResponseHeaders("Cache-Control", "Content-Length", "X-Request-Id", "X-Runtime", "X-XSS-Protection", "ETag")
	
	// レスポンスヘッダーをapi構造体にセットする
	api.ReadResponseHeader(c.Writer.Header())
	
	// レスポンスボディを読み取りやすい形式にラップしてからapi構造体にセットする
	api.WrapResponseBody(gbw.Body())
	
	// レスポンスステータスコードをapi構造体にセットする
	api.ResponseStatusCode = c.Writer.Status()

	// APIドキュメントにapi構造体を書き出す
	apidoc.Gen(api)
}

ginにミドルウェアをセットする

func getEngine() *gin.Engine {
	r := gin.Default()

	r.Use(apidocMiddleware)

	... Handlerを定義する ...

	return r
}

func main() {
	getEngine().Run(":8080")
}

以上で、apidocの設定は完了です。ローカルホストの8080ポートにアクセスすると、自動的にAPIドキュメントが生成されます。
 
APIを叩くたびにドキュメントが生成されるのはアレなので、APIのテストを書き、テスト実行時のみドキュメント生成されるようにするのがオススメです。

まとめ

Railsでいうautodocのようなライブラリが欲しくなって作ってみました。
 

大規模アプリケーション開発で使うにはまだまだ弱い部分がありますが、中小規模のドキュメント生成に使うのはありだと思います。PRお待ちしているので、是非!
 
ginのサンプルコード全文

  • このエントリーをはてなブックマークに追加

エウレカでは、一緒に働いていただける方を絶賛募集中です。募集中の職種はこちらからご確認ください!皆様のエントリーをお待ちしております!

Recommend

速習!Angular1からAngular2への移行

新社会人に送る。peco + anyframe + zshで実現する快適コマンドライン環境