イントロ
会社でSwaggerがブームっぽくなっているので、使ってみました。 気になっていた機能は、codeGenです。 これのおかげで、どれほどAPIサーバーの開発が楽になるのかを体感してみたいと思います。
なお、この記事では自分がしたことの履歴のみを記載し、「Swaggerとは」という内容は他のサイトなどに任せるとします。
SwaggerEditorの起動
まずは、Swagger Editorをダウンロードします。
$ git clone https://github.com/swagger-api/swagger-editor.git Cloning into 'swagger-editor'... remote: Counting objects: 33660, done. remote: Compressing objects: 100% (35/35), done. remote: Total 33660 (delta 16), reused 28 (delta 11), pack-reused 33613 Receiving objects: 100% (33660/33660), 198.93 MiB | 4.95 MiB/s, done. Resolving deltas: 100% (19003/19003), done. $ cd swagger-editor $ explorer .
あとは、index.htmlをブラウザで開くだけです。 なにこれ、簡単すぎ。
Swagger-Editorの画面
Swagger-Editor起動時には、サンプルのAPIドキュメントが書かれています。 Swagger Petstoreというサービスのようです。

サンプルを見て、いくつか、気付くところがあります。
- 鍵マークがついているAPIとついていないAPIがある
GET /pet/findByTagsに訂正線が付けられている- Modelを登録して、Responseとかに使える
- Try it outボタンの意味
鍵マーク
直感的には、これの意味が分かりませんでしたが、各APIのスコープ(APIをたたける人の制限)を示すものだったようです。
鍵マークが付いているものは、tokenによる認証が必須のエンドポイントだそうです。 鍵が外れたマークになっていますが、鍵がかかっているマークとかは特にないようです。
後で、token認証を、モックアップサーバーで実際に試してみましょう。 その内容は、別の記事で紹介したいと思います。
訂正線
Deprecated (非推奨)のAPIということだそうです。
Model
Responseデータのデータモデルを登録しておけば、API開発時にそのモデルを指定するだけで、Response内容が設計できるようです。 これだけで、だいぶ設計が楽になりそうです。

Try it outボタン
設計したAPIを試すためのボタンです。
APIの受け口になるWebサーバーを立ち上げていないと、このボタンは使えません。
今は、ブラウザでindex,htmlを開いているだけなので使えないようです。
しかし、次に記載するモックアップサーバーを起動したら、そちらでTry it outができます。
モックアップサーバー
立ち上げ
モックアップサーバーを立ち上げるためには、まずCodeGen機能でコードを取得しましょう。 上のタブのGenerate Serverをクリックし、自分が慣れている言語・フレームワークを選択します。 自分は、nodejs-serverを選択しました。
Zipで資材がダウンロード出来るので、それを解凍します。
解凍された中にREADMEが入っていました。
npm startを実行せよ、とのことです。
当然ここは、上で選択した言語・フレームワークごとに適切な指示が書かれており、それに従えば良いものと思われます。
APIドキュメントページ
実行後、http://localhost:8080が、API Webサーバーのホスト:ポートになっているようです。
http://localhost:8080/docs/が、APIドキュメントページです。
このページを、Swagger UIと呼ぶそうです。
API仕様をユーザーに見せるためには、これをちょこちょこっと修正するだけで良さそうです。
これは、自力でAPI開発するのが馬鹿馬鹿しくなる位、幸せになれそう。
docsのほうで、Try it outボタンがあります。これをクリックすると、試しにAPIがたたけるようです。
何もパラーメータが必要ない、GET /store/inventoryを試してみましょう。
すると、打ち込んだリクエスト、レスポンス、レスポンスコード、レスポンスヘッダーが出力されます。
モックアップサーバーとして、十分です。
モックアップサーバーのコード
CodeGenでどんなコードが生成されていたのか、気になりますよね。見てみましょう。
package.json
まずは、package.jsonを見てみましょう。
先ほどのnpm startで何が行われていたか、一応チェックしたところ、node index.jsだけでした。
あっさりしたものです。
あと、依存しているライブラリはこの通りです。
"dependencies": { "connect": "^3.2.0", "js-yaml": "^3.3.0", "swagger-tools": "0.10.1" }
Webサーバーフレームワークには、connectを使っているようです。 connectについては、以下の記事が分かりやすそうです。
swagger-toolsは、まだ謎です。 他のコードを見ていけば、何のために存在しているか、つかめることでしょう。
ディレクトリ構造
次に、ディレクトリ構造を見てみます。
$ tree -I node_modules .
.
├── api
│ └── swagger.yaml
├── controllers
│ ├── Pet.js
│ ├── Store.js
│ └── User.js
├── index.js
├── package.json
├── package-lock.json
├── README.md
├── service
│ ├── PetService.js
│ ├── StoreService.js
│ └── UserService.js
└── utils
└── writer.js
4 directories, 12 files
それぞれのディレクトリについて、見ていきましょう。
apiディレクトリ
apiディレクトリには、Swagger Editorで設計したyamlファイルが入っています。
最初は、なぜ設計に用いたファイルが、ここにも置かれているのか疑問でしたが、どうやらmiddlewareの作成に利用しているようです。
また、ディレクトリ構造や、各ファイルを軽く見ると、ルーティングを定義しているコードがない事にも気付きます。
index.js内で、後述するコントローラーの関数と、apiディレクトリ内のyamlファイルをパラメータとして、ルーティングを設定する関数middleware.swaggerRouterを呼ぶことで、ルーティングを設定しているようです。
これらのmiddlewareの作成などに、swagger-toolsが大きな役割を担っているようです。 では、他のディレクトリを見に行く前に、swagger-toolsを見に行きましょう。
swagger-tools
node_modules内のswagger-toolsの中身を見てみましょう。
全てのファイルを列挙すると、多すぎるので、2階層に限定しています。
$ tree swagger-tools -I node_modules -L 2
swagger-tools
├── bin
│ └── swagger-tools
├── index.js
├── lib
│ ├── helpers.js
│ ├── specs.js
│ └── validators.js
├── LICENSE
├── middleware
│ ├── helpers.js
│ ├── swagger-metadata.js
│ ├── swagger-router.js
│ ├── swagger-security.js
│ ├── swagger-ui
│ ├── swagger-ui.js
│ └── swagger-validator.js
├── package.json
├── README.md
└── schemas
├── 1.2
├── 2.0
└── json-schema-draft-04.json
7 directories, 15 files
index.jsで、module.exportsされているinitializeMiddlewareは、後述しますが、自動生成されたコードのindex.jsでも利用されています。
swagger-tools内のindex.jsでは、specsもmodule.exportされています。
specsは、./lib/specs.jsをrequireしているだけですが、specs.jsの中では、Specification関数があり、Swagger仕様オブジェクト、というものが作られます。
これも重要な気がします。
var initializeMiddleware = function initializeMiddleware (rlOrSO, resources, callback)
第一引数、The Resource Listing or the Swagger Objectの略だそうです。
ここに、自動生成された方のAPI仕様ファイル、./api/swagger.yamlをyamlとして読み込んだオブジェクトが投入されます。
ルーティングは、自動生成されたindex.jsの中で、後述するコントローラーや、Swagger UIのパスなどをパラメータとして、middlewareのルーティングを設定する関数swaggerRouterに渡してやれば良いようです。
これは、index.jsの33行目で行われています。
// Route validated requests to appropriate controller
app.use(middleware.swaggerRouter(options));
controllersディレクトリ
controllersディレクトリは、MVCのC、コントローラーですね。
届いたリクエストに応じたイベントハンドラで、モデルを操作します。
コントローラーのコードが自動生成出来るって、すごくないですか? どのようにして自動生成出来ているのか見るために、少しコードを見てみましょう。
var utils = require('../utils/writer.js'); var Pet = require('../service/PetService'); module.exports.addPet = function addPet (req, res, next) { var body = req.swagger.params['body'].value; Pet.addPet(body) .then(function (response) { utils.writeJson(res, response); }) .catch(function (response) { utils.writeJson(res, response); }); }; module.exports.deletePet = function deletePet (req, res, next) { var petId = req.swagger.params['petId'].value; var api_key = req.swagger.params['api_key'].value; Pet.deletePet(petId,api_key) .then(function (response) { utils.writeJson(res, response); }) .catch(function (response) { utils.writeJson(res, response); }); }; module.exports.findPetsByStatus = function findPetsByStatus (req, res, next) { var status = req.swagger.params['status'].value; Pet.findPetsByStatus(status) .then(function (response) { utils.writeJson(res, response); }) .catch(function (response) { utils.writeJson(res, response); }); };
writer.jsとserviceディレクトリ内のコードに依存しています。
これらの関数の名前は、yamlファイル内の各エンドポイントのoperationIdに設定された値と同じです。
それぞれの関数内をよく見てみると、モデルの操作は行っていません。
モデルの操作は、コントローラーでは行わず、serviceディレクトリ内のコードに処理を任せているようです。
コントローラーでは、そのサービスへの適切な引数・返値に合わせた処理のみが記載されています。
では、serviceディレクトリを見ていきましょう。
serviceディレクトリ
先ほどと同じように、PetService.jsの中を少し見ていきましょう。
/** * Add a new pet to the store * * * body Pet Pet object that needs to be added to the store * no response value expected for this operation **/ exports.addPet = function(body) { return new Promise(function(resolve, reject) { resolve(); }); } /** * Deletes a pet * * * petId Long Pet id to delete * api_key String (optional) * no response value expected for this operation **/ exports.deletePet = function(petId,api_key) { return new Promise(function(resolve, reject) { resolve(); }); } /** * Finds Pets by status * Multiple status values can be provided with comma separated strings * * status List Status values that need to be considered for filter * returns List **/ exports.findPetsByStatus = function(status) { return new Promise(function(resolve, reject) { var examples = {}; examples['application/json'] = [ { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, "category" : { "name" : "name", "id" : 6 }, "tags" : [ { "name" : "name", "id" : 1 }, { "name" : "name", "id" : 1 } ], "status" : "available" }, { "photoUrls" : [ "photoUrls", "photoUrls" ], "name" : "doggie", "id" : 0, "category" : { "name" : "name", "id" : 6 }, "tags" : [ { "name" : "name", "id" : 1 }, { "name" : "name", "id" : 1 } ], "status" : "available" } ]; if (Object.keys(examples).length > 0) { resolve(examples[Object.keys(examples)[0]]); } else { resolve(); } }); }
ここに、具体的なモデルの操作を、記載できそうですね。 基本的にAPIリクエストを受け取った後は、非同期的に処理を行う事が前提になっているようです。 具体的なモデルの操作は、yamlに書かれていないので、ほぼ空欄同然になっています。
findPetsByStatus関数だけは、色々と書かれています。
これは、この関数が、Petモデルを返すため、Petモデルに定義されていたexampleがここに書かれているのだと思われます。
このexampleは、Swagger EditorのyamlまたはJSONで修正する事ができます。
findPetsByStatus関数を修正する際は、このPetモデルのexampleを見ながら修正できるので、ものすごくありがたいですね。
残り
utilsディレクトリとindex.jsの説明は、特にAPI設計開発において主要な所ではないので飛ばしました。
見れば何をしているか、すぐに分かりますし。
おわりに
確かに、Swagger Editorを使えば、API開発の簡単化+ミスの抑制に繋がると思います。 ただし気になるのは、既にある程度のAPIが公開されているアプリケーションに対して、後からSwaggerを使ってAPIの設計を管理したい、と思ったとき、どう使っていけば良いのか…。 これは、追々、ちゃんと使ってみてから、また見解を書きたいと思います。
自動生成されたコードに関して言うと、ルーティングがハードコーディングされていない設計は、拡張性が高まるので、素晴らしいと思います。 サービス・コントローラーをあらかじめ用意しておけば、yamlを書き換えるだけで、すぐにAPIの中を切り替えられます。 ユーザーからして、突然APIで提供される機能が変わったら大事なので、API提供の思想としてそれをして良いときは、見極めないと行けませんが。
あと、MVCではなく、コントローラーとモデルの間に、サービスが挟まっていますね。 このサービスの意義を、今後、もう少し深掘りしたいと思います。 恐らく、Springと同じサービスで、ここにビジネスロジックを書くのでしょう。