Markdown で記述した API ドキュメントからお手軽にドキュメントサーバーとモックサーバーを生成する - Gulp で作る Web フロントエンド開発環境 #7

更新 : サンプルコードを ECMASCript 2015+ で書き直し、npm-scriptsでの例を加筆しました。

SPA 開発によるサーバーサイドとフロントエンドの完全分業化

Single Page Application ( 以下、SPA ) は従来のサーバーサイドレンダリングを基とした Web アプリケーションと異なり、iOS アプリや Android アプリと同じように Web API を通じてサーバーにリクエストを送り、JSON 形式などで返ってきたデータをもとにダイナミックにレンダリングすることで Web ページ全体をリロードすることなく画面を更新するのが特徴です。

SPA 開発に於けるメリットとして、サーバーサイド開発は View のレンダリングに関する処理から解放されて Web API を吐き出すことだけに注力出来る事が挙げられます。フロントエンド開発も Web API を通じてサーバーから取得したデータを元に動的に View を描画したりリッチなインタラクションの表現に注力することが出来るようになりました1)その分 JS コードの量も膨大になり、より高い設計力が求められるようになりましたが…

mybest_frontend_dev.001

つまりサーバーサイドとフロントエンドは Web API を通じてのみやり取りを行い、そこで取り交わされるデータの形式 ( インターフェース ) さえ間違わなければそれ以外の点において互いを意識しなくてよいということになります。

API ドキュメントの重要性

サーバーサイドとフロントエンドのやり取りが Web API を通じてのみとなるのであれば、インターフェースについての認識は常に合わせておく必要があります。もしデータベースや仕様の変更により Web API の一部に変更を加える必要が出てきてもフロントエンド開発者がそのことを認識していなければどうなるでしょう?やがて開発が佳境を迎え、いよいよサーバーサイドとフロントエンドを結合する時になって期待通りの動作が行われず、そこで初めて Web API 仕様の認識にズレが発覚したとなると悲惨です。サーバーサイドとフロントエンドが同じチームで開発している場合はあまり起こりえない事例ですが、受託案件のようにそれぞれが別の組織で開発する場合においては充分に考えられる話です。

そこで重要になってくるのがAPIドキュメントの存在です。もしAPIの仕様が変更になっても常にそれをドキュメントに反映して最新の状態を保つことが出来れば、そのような悲劇の多くは未然に防ぐことが出来るでしょう。更にAPIドキュメントをWebページとしていつでもどこからでもアクセス出来るようにしておけば、これ以上のことはありません。

しかし明確な取り決め無くWeb API 仕様をドキュメントに起こせるほど簡単ではありません。人によって書き方が異なったり定義に過不足が生まれがちです。そこで登場するのが API Blueprint という Web API 記述の仕様です。

API Blueprint

eb4b6f14a29a255c9e5bdf63e449aa28_400x400

API Blueprint ( 以下、Blueprint ) は Markdown ファイルから API ドキュメントやサンプルデータとなる JSON を生成する仕様を定めたものです。一般的に Web API のインターフェースで定義する内容というのは殆ど決まっているものです。Blueprint は API の記述フォーマットが明確に定義されているのが特徴です。従来のドキュメント作成となると、人によって書き方が異なったり定義に過不足が生まれてし品質の悪いものになりがちですが、このお作法に從うことで誰でも高い品質で API ドキュメントを書くことが可能となります。

基本的な記法

公式にあるサンプルをそのまま拝借しています。

# /message
メッセージの取得および更新をする API です。
## GET
メッセージを取得します。メッセージはプレーンテキストで返ってきます。
+ Response 200 (text/plain)
        Hello World!
## PUT
メッセージを更新します。
+ Request (text/plain)
        All your base are belong to us.
+ Response 204

RESTful API における GETPUT を定義した例です。文法自体は Markdown そのものですね。たったこれだけですが、後述する gulp-aglio というNodeモジュールを使うことで以下のような API ドキュメントページを生成することが出来てしまいます。クールですね。

gulp-aglio

その他の記法に関しては、公式の GitHub リポジトリをご参照ください。非常に詳しく且つ丁寧なドキュメントが用意されています。

Blueprint と gulp-aglio でお手軽にAPIドキュメント生成

gulp-aglio は Blueprint 形式で書かれた Markdown ファイルを HTML に変換する Node モジュールです。今回はこれを使って API ドキュメントページを作成する Gulp 環境を作ってみるとします。

サンプルコードはこちら

機能要件

  • Blueprint 仕様で書かれた Markdownファイルを HTML ページに変換する
  • 変換と同時にローカルサーバーを立ち上げ、Web ブラウザから確認出来るようにする
  • Markdown ファイルを更新すると自動で Web ブラウザをリロードする

それでは環境を作っていきましょう。適当なディレクトリを用意したらnpm initを実行して npm プロジェクトを作成しておきます。

1. 必要な Node モジュールをインストール

以下のコマンドを実行します。

$ npm install --save-dev gulp gulp-aglio gulp-watch rimraf run-sequence

全部で6つのモジュールをインストールしました。

  • gulp
  • gulp-aglio
  • gulp-watch
  • require-dir
  • rimraf
  • run-requence

2. Gulp タスクを定義

gulpfile.js にタスクを定義します。

const gulp = require('gulp');
const watch = require('gulp-watch');
const rimraf = require('rimraf');
const aglio = require('gulp-aglio');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const browserSync = require('browser-sync');
const runSequence = require('run-sequence');
gulp.task('clean', (cb) => rimraf('./public', cb));
gulp.task('aglio', () =>
  gulp.src('./app/md/**/*.md')
    .pipe(plumber({
      errorHandler: notify.onError('<%= error.message%>')
    }))
    .pipe(aglio({
      template: 'default'
    }))
    .pipe(gulp.dest('./public/'))
    .pipe(browserSync.reload({
      stream: true
    }))
);
gulp.task('browser-sync', () =>
  browserSync.init(null, {
    server: './public',
    reloadDelay: 1000
  })
);
gulp.task('watch', () =>
  watch('./app/md/**/*.md', () => gulp.start(['aglio']))
);
gulp.task('doc', () => runSequence('clean', 'aglio', ['browser-sync', 'watch']));

4つのタスクを定義し、それらを順番に実行するようにしました。

タスク名 概要
clean publicディレクトリを削除する
aglio Blueprint 仕様で書かれた Markdownファイルを HTML ファイルに変換する
browser-sync ローカルサーバーを起動して Web ブラウザからページにアクセスする
watch Markdownファイルを監視し、更新されたら aglio タスクを実行する

3. Gulp タスクを実行する

$ gulp doc

タスクが正常に実行され、Web ブラウザが起動して以下のページが表示されるはずです。

3.1 Gulp を使わず npm-script だけで実行する方法

Gulp を使わずよりシンプルな構成でほぼ同様のことを実現することも可能です。先ほどは Gulp 用にラップされた gulp-aglio を使いましたが、npm-script から呼び出す場合はノーマルの aglio を使います。

aglio インストール

$ npm install -D aglio

npm-scripts を定義

package.json に以下の script を追記します。

{
  "scripts": {
    "reset": "rm -rf public; mkdir public",
    "doc": "aglio -i ./app/**/*.md -s",
    "build": "aglio -i ./app/**/*.md -o ./public/index.html"
  }
}

aglio コマンドは -i オプションで読み込む Markdown ファイルを指定します。更に -s オプションを指定するとライブプレビューサーバを起動します。プレビューには http://localhost:3000 でアクセス出来ます。ただし browser-sync のようなライブリロード機能は搭載していないため、ファイル編集後は手動でブラウザをリロードする必要があります。

-o オプションの後に出力先ファイル名を指定すると HTML ファイルとして生成します。

# ドキュメントサーバ起動
$ npm run doc
# HTML ファイルとして出力
$ npm run build

Blueprint と api-mock で簡易 Web API サーバーを立ててみる ( by Gulp )

api-mock は Blueprint 仕様で書かれた Markdown ファイルから簡易的な Web API モックサーバーを立ち上げる Node モジュールです。簡易的とは言ってもきちんと RESTful な Web API を立ち上げることが出来るので、サーバーサイドの実装を待たずにちょっとした疎通確認が出来てしまうという優れものです。

公式ドキュメントによるとグローバルインストールして単独で使用するものらしいですが、これから紹介する方法で Gulp からでも操作することが可能です。

1. 必要なNodeモジュールをインストール

以下のコマンドを実行して api-mock をインストールします。

$ npm install --save-dev api-mock

2. Gulp タスクを定義

gulpfile.js にタスクを定義します。以下のコードを追記してください。

const ApiMock = require('api-mock');
gulp.task('api-mock', () => {
  const mockServer = new ApiMock({
    blueprintPath: './app/md/index.md',
    options: {
      port: 5557
    }
  });
  return mockServer.run();
});
gulp.task('mock', ['api-mock']);

api-mock はそれ自体が Gulp に対応しておらず、現時点でラッパーモジュールも存在していません。そこで少々トリッキーですが上記のように定義することで Gulp から実行出来るようにしました。PORT は適当に 5557 としています。

3. Gulp タスクを実行する

$ gulp mock

タスクが正常に実行され、Web API サーバーが起動します。試しにhttp://localhost:5557/messageとリクエストしてみると、ドキュメントに定義した文字列が返ってくるのが分かります。

Hello World!

プログラミングコードを一行も書くことなく、いきなり Web API の動作確認が出来てしまいました。

締め

旧来のドキュメント作成というものは、多大な労力を費やして Excel などに書いたにも関わらず、ろくにメンテナンスもされず腐敗していくのがザラでした。今回紹介した Blueprint であれば、記述のお作法が明確になっていることに加えて Markdown というエンジニアにとって書きやすい書式で定義することができます。

gulp-aglio は僕が現在関わっている新規開発案件でも活用されており、絶えず最新版に更新していることから常に品質の保たれたドキュメントに育っています。機械に任せられるところは積極的に自動化することで、エンジニアはより価値のあるプログラミングに専念することができるというわけです。

ぜひとも参考にしてみてはいかがでしょうか。

脚注

脚注
1 その分 JS コードの量も膨大になり、より高い設計力が求められるようになりましたが…