RareJob Tech Blog

レアジョブテクノロジーズのエンジニア・デザイナーによる技術ブログです

API仕様書作成にScribeを利用する

こんにちは。サービス開発チームのすずきです。

参画しているプロジェクトでAPI仕様書を作るためにScribeを利用してみたのでその経緯と使用方法をお話したいと思います。

Scribe

github.com

Laravel(Lumen/Dingo) で実装したソースコードのPHPDocをもとにAPIドキュメントを生成するためのツールになります。

  • ブラウザで閲覧できるHTMLのAPIドキュメントを作成
  • HTMLページではREST APIへのリクエストも送信可能(Swagger UIの"Try it out"と同様の機能)
  • Postman collectionファイルやOpenAPI Specificationの生成も可能

といった機能を持ちます。

以下のリンクからデモサイトを見ることができます。
デモ

なせScribeを利用したか

通常ある程度の大きさが予想されるシステム開発では、
フロントエンドはVueやNextで作成し、バックエンドはLaravelでREST APIを提供するような構成を取りそれぞれ別のリポジトリで管理しています。
フロントエンドとバックエンドを実装するメンバーはそれぞれ異なるため、コミュニケーションする際はAPI仕様書を参照しながら話すような場面が多くあり、
認識齟齬を少なくするためにAPI仕様書はOpenAPI(Swagger)を利用してしっかりと書いていくようにしています。

一方、今回のプロジェクトでは以下のような条件だったのでScribeを利用することにしました。

  • フロントエンド構成がMPAである
    • バックエンドはLaravelで処理し、描画に必要な値をbladeファイル上でVueに渡す
    • フロントエンドとバックエンドのコードはMonorepoで管理する
  • 画面の一部をAjaxで更新する処理が求められるのでフロントエンドとバックエンドはREST APIで通信することがある
    • APIエンドポイントの数は多くないが、両チームの認識齟齬を減らすために管理工数をあまりかけずに仕様書が欲しい
  • チームにはPHPDocを記述する習慣があり、ScribeのためにPHPDocを書くのではない

先述したOpenAPIで書いていく場合はバックエンドメンバーが管理しているOpenAPIのリポジトリをフロントエンドメンバーがローカルにcloneして立ち上げるのが手間になるため、
GitLab Pagesなどを利用してメンバー全員がAPI仕様書を閲覧できるようにします。
ガシガシ開発している最中だとOpenAPIへの変更もバンバン入り、どのブランチをPagesに上げておくのか、もしくは複数のブランチのAPI仕様書を見れるようにするのかといった工夫が求められ導入や管理コストがかかってしまいます。
しかし、今回のようにMonorepoで開発していくのであれば全メンバーが同じリポジトリをcloneすることになるので、
メンバー各々が自由なブランチのAPI仕様書をローカルで閲覧できるようにするためのコマンドを用意するだけ、という比較的低コストでAPI仕様書が用意できるようになりました。

Scribeの使用方法

今回はリクエスト時のパラメータとレスポンスデータのスキーマAPI仕様書でわかれば良いので、
それらをAPI仕様書で確認できるようにしていきます。

公式ドキュメントに沿ってScribeのインストールを進めていきます。

// composerを利用してScribeをインストール
$ composer require --dev knuckleswtf/scribe

// Scribeの設定ファイルを生成
$ php artisan vendor:publish --tag=scribe-config

これらのコマンドを実行するとsrc/configディレクトリ配下にscribe.phpという設定ファイルが作られます。

  • 認証を必要とする
  • DBを利用しない(全て外部APIからデータを取得する)

作成するAPIがこれらの条件を持つので、以下のような修正を加えました。

    /*
     * How is your API authenticated? This information will be used in the displayed docs, generated examples and response calls.
     */
    'auth' => [
        /*
         * Set this to true if any endpoints in your API use authentication.
         */
-        'enabled' => false,
+        'enabled' => true,


    /**
     * For response calls, API resource responses and transformer responses,
     * Scribe will try to start database transactions, so no changes are persisted to your database.
     * Tell Scribe which connections should be transacted here.
     * If you only use one db connection, you can leave this as is.
     */
-    'database_connections_to_transact' => [config('database.default')]
+    'database_connections_to_transact' => []

HTMLファイルの出力先やドキュメントを表示するURLなども変更できるようなので細かい設定はこちらをご確認ください。

Requestパラメータの表示方法

次にリクエスト時のパラメータを表示できるようにしていきます。
そのためにはScribeでは2つのやり方を用意しています。

  • PHPDocの@queryParam/@bodyParamアノテーションを利用して明示的に記述する
  • LaravelのValidation rulesの内容をもとに自動生成する

今回は前者の方法を選択しました。
Validation rulesを利用しているのですが、私たちが全てのパラメータに対してルールを記述しているわけではないためです。
Validation rulesはinline($request->validate([...]))で記述したもの、Validator::make()で記述したもの、RequestFormクラスのrules()メソッドに記述したものすべてに対応しているようです。

@queryParam/@bodyParamアノテーションを利用する場合は、
QueryStringに対しては@queryParam、RequestBodyに対しては@bodyParamを利用して以下のフォーマットに記載します。

@queryParam パラメータ名 型 ディスクリプション

これはControllerクラス、RequestFormクラスのどちらに記述しても大丈夫です。

公式ドキュメントからRequestFormクラスに記述する場合のサンプルを引用したものとそのときの表示結果が以下になります。

/**
 * @queryParam lang required The language.
 * @bodyParam title string The title of the post.
 * @bodyParam body string required The content of the post.
 */
class CreatePostRequest extends \Illuminate\Foundation\Http\FormRequest
{

}

// in your controller...
public function createPost(CreatePostRequest $request)
{
    // ...
}

f:id:keshipi:20220216100318p:plain
表示結果

Responseデータの表示方法

ここまででリクエスト時のパラメータをAPI仕様書で確認できるようになったので、次はレスポンスデータを表示できるようにしていきます。 レスポンスに関してもいくつか方法があるようです。

今回はエンドポイントに認証をかけている、Eloquent API resourcesを利用していないという理由で3と4番目は排除しました。(認証をかけていても3は利用できるかもしれませんがそこまで調査できておりません。ご存知の方いらしたら教えてください🙇‍♂️) 次に1と2それぞれの方法を見てみます。 @responseを利用する方法はインラインでレスポンスを記述する

/**
 * @response {
 *  "id": 4,
 *  "name": "Jessica Jones",
 *  "roles": ["admin"]
 * }
 */
public function getUser(int $id)
{
  // ...
}

@responseFileを利用する方法は外出ししたファイルのパスを記述する

/**
 * @responseFile storage/responses/users.get.json
 */
public function getUser(int $id)
{
  // ...
}

レスポンスが大きくなる(キーが増える)ことが見込まれる、PHPDocに記載するよりもjsonファイルに記載した方が記述の際にエディタの補助を受けやすいのでControllerクラスの見通しも良さそうなので今回は2番目の「@responseFileを利用する方法」を選択しました。 その際の記述と表示結果が以下になります。

// a file named users.get.json in storage/responses/
// {"id":4,"name":"Jessica Jones"}

/**
 * @responseFile responses/users.get.json
 */
public function getUser(int $id)
{
  // ...
}

f:id:keshipi:20220216105546p:plain
表示結果

さいごに

簡易でありますが工数をあまりかけることなく認識齟齬を低減させるためのAPI仕様書を用意することができました。 今回は利用しませんでしたがScribeはHTTPステータスごとにレスポンスのサンプルを用意したり、リクエストヘッダーやURLパスパラメータに関する情報を記述することができたりとライトユースからヘビーユースを幅広くカバーできる機能を持ち合わせています。 またPostman collectionファイルやOpenAPI Specificationを生成してくれるの嬉しい機能ですね。