Swaggerは、REST APIの仕様とそれに関連するツール群の総称です。REST APIの仕様を定義したJSONファイル(Swagger Spec)を軸に以下のようなツールから構成されています。
- Swagger UI - Swagger Spec から動的にAPIドキュメントを生成するツール
- Swagger Editor - Swagger Specのエディタ
- Swagger Codegen - Swagger Specからクライアントのコードを生成するツール
最近では、Open API InitiativeがAPIの記述のためにSwaggerを採用して話題になりました。
www.publickey1.jp
APIドキュメントのメンテは結構面倒
一般的にAPIの仕様書は、古くはExcel/Wordなどを使ったり、最近ではWikiやMarkdown形式で記述したりなどプロジェクトによって色々な方法で記述するのが一般的かなと思います。
このには幾つか問題があって、APIの実装ができてもドキュメントがない状態があったり、せっかくドキュメントを作ってもAPIの修正・追加に追いていない状態になったりすることがよくあります。
そこで今回は、実際に開発が進んでいたCakePHP3で作成したiOSアプリのバックエンドのAPIドキュメントをSwaggerで記述したので、そこで得た知見をもとに実際の記述方法などを解説します。
SwaggerでAPIの仕様を書く
CakePHPのブログチュートリアル(http://book.cakephp.org/3.0/en/tutorials-and-examples/bookmarks/intro.html)で使用するスキーマを若干変更した、以下の様なデータをAPIで扱う場合のドキュメントを書いてみます。
- User
- id
- username
- password
- created
- modified
- Article
- id
- user_id
- title
- body
- created
- modified
アプリケーションの定義
まずは、今回使用するアプリケーション自体の定義を記述します。以下の様に、@SWG\Info内にアプリケーションの名前、説明、APIバージョンなどの情報を記載します。
後ほど一覧取得APIで共通で使用するPageInfoデータに関してもここに記載します。詳細は後述します。
このファイル自体は、コメントだけなのでどこに置いておいても構わないのですが、今回は app/config/swagger.phpとしておきます。
app/config/swagger.php
<?php
@example
Modelの定義
CakePHP3では、エンティティの内容を明示的に宣言しないので今回は、各Entityクラスのヘッダーに定義を記述しました。実際の記述は以下のような感じになります。
Model/Entity/User.php
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class User extends Entity
{
...
}
Model/Entity/Article.php
<?php
namespace App\Model\Entity;
use Cake\ORM\Entity;
class Article extends Entity
{
...
}
APIで使うモデルの定義が完了したので、次に実際のAPIの定義を記述します。APIの定義は各コントローラのアクションのコメントに記述します。すべてのAPIの記述方法を書くのとなかなかの量になるので、特徴的な記述を使うことが多い一覧系のAPI仕様の書き方について説明します。
今回は、CakePHP3+Crud Pluginを使用しましたので、一覧取得時の実際のレスポンスは以下の様な形式になります。
CakePHP3+Crud Pluginに関しては id:skywaker さんの以下の記事が詳しいです。
slywalker.hateblo.jp
{
"success": true,
"data": [
{
"id": "1",
"username": 'test',
"pasword": "xxxx",
"created": "2016-01-01 01:01:01"
}
],
"pagination": {
"page_count": 1,
"current_page": 1,
"has_next_page": false,
"has_prev_page": false,
"count": 1,
"limit": 20
}
}
*** Controller/UsersController.php
/api/users.json はユーザ一覧を取得するAPIで20件ごとにページングされ、検索文字列を指定することで、ユーザ名で検索できる仕様とします。
レスポンスコードごとに@SWG\Responseブロックを記述してレスポンス内容の解説と、どんなデータが帰るかを記述します。
@SWG\Propertyのtypeをarrayとして記述し、参照先のモデルを@SWG\Itemsで参照することができます。また、プロパティ自体が、定義済みのモデルを使用する場合は、ref="#/definitions/PageInfo"の様に記述することで参照できます。
<?php
namespace App\Controller\Api;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class UsersController extends ApiController
{
...
@return
public function index()
{
...
}
*** Controller/ArticlesController.php
api/articles.jsonは、記事の一覧を返すAPIを想定しています。api/users.jsonと違い、記事のデータだけでなく記事を投稿したユーザを含む形で以下の様なレスポンスを返すことにします。
{
"success": true,
"data": [
{
"id": "1",
"user_id": 1,
"title": 'test title',
"body": "test body",
"created": "2016-01-01 01:01:01"
"user": {
"id": "1",
"username": 'test',
"pasword": "xxxx",
"created": "2016-01-01 01:01:01"
}
}
],
"pagination": {
"page_count": 1,
"current_page": 1,
"has_next_page": false,
"has_prev_page": false,
"count": 1,
"limit": 20
}
}
記事一覧のレスポンスのdata内で定義しているItemは先ほど定義したArticleではなく、ユーザ情報を含む形で定義した、IndexArticleを指定しています。
IndexArticleの定義は allOfを使うことで定義済みのArticleモデルの情報を再利用して、userプロパティを追加しています。
この様に、@ref,@SWG\Items、allOfなどを使用して定義済みのモデルを参照することで、個別のAPIごとにレスポンス内容が微妙み異なる場合などに記述量をかなり減らすことができます。
<?php
namespace App\Controller\Api;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
class ArticleController extends ApiController
{
...
@return
public function index()
{
...
}
swagger-phpのインストール
APIの定義の記述が終わったら、記述したアノテーションを解析するツール swagger-php をインストールしてSpecを生成します。composerでインストールできるので、いつものようにこんな感じですね。
$ composer require --dev zircote/swagger-php
Specファイルの生成
アプリケーションのルートで以下の様な感じでswagger-phpを実行します。-o で出力先のディレクトリを指定できるので適宜環境に合わせて指定してください。今回は、Specファイルは後述のswagger-uiで参照しますので、ブラウザからアクセスできる必要があります。
$ ./app/vendor/bin/swagger ./app/src/ -o ./app/webroot/api-documents/
swagger-uiのインストール
Specファイルを解析して、APIドキュメントとして表示する swagger-ui は以下で公開されています。
https://github.com/swagger-api/swagger-ui
このリポジトリの `dist` ディレクトリの内容をブラウザからアクセスできる場所に適宜コピーしてください。
swagger-uiの設定を修正
設置したswagger-ui内のindex.htmlの以下の箇所を、先ほど作成したSpecファイルを参照するように修正します。
var url = window.location.search.match(/url=([^&]+)/);
if (url && url.length > 1) {
url = decodeURIComponent(url[1]);
} else {
url = "http://petstore.swagger.io/v2/swagger.json";
}
問題なく生成されれば以下のように、APIドキュメントが参照できます。
今回作成したファイルは gistで公開しましたので swagger-uiで実際にAPIドキュメントをみられます。
まとめ
APIドキュメント作成は、なかなかに気の重い作業という人も多いと思いますがswaggerを使うとかなり手間を減らせる上に、実際のコード内に仕様を記述するため、APIが更新されたのにドキュメントが更新されないといった問題も防ぎやすくなると思います。また、CIに組み込んでリポジトリの更新時に自動生成・公開するようにすることも簡単にできますので、APIの実装が完了したらAPIドキュメントも自動で公開するみたいなこともできますね。
チームで効率良く開発を進めるには、必要なドキュメントをいかに効率良く作成するかがとても重要だと思うので是非swaggerを試してみてください!