Rails
api

Apipie-railsでドキュメント生成してみます!

みなさまこんにちは。
Ruby on Rails Advent Calendar 2017 の 12/10分での投稿となります。

この記事では、apipie-railsというAIPドキュメント用のgemについて紹介させていただきます。

はじめに

お詫びとお礼

apipie-railsは最近職場で使ってみて面白かったので、テーマとしては決めていたものの、諸事情で期日までの投稿を逃してしまいました。参加希望だった方には、本当に申し訳ございません…

なんとかせねば!と思っていたところ、12/16投稿の@c5meruさまが「はじめてのRails API
」という記事を拝見。

こちらの記事では、GitHubのソースコード付きでAPIの例が紹介されていて、APIドキュメントの大切さについても触れられていました。
また、その中で、私のちょっとした過去記事にリンクして下さっていました。
実は、rspecでドキュメントが生成できるautodocの機能は全く知らなかったのでとても参考になりました。

その上で、勝手ながらGitHubのソースをフォークさせていただき、apipieを使った例を追加で添えて見ようと考えた次第です。

サンプルのコード

apipie-railsについて

では、あらためてapipie-railsと設定の仕方について簡単に説明してまいります。

apipie-railsは、REST API向けのドキュメント生成のためのツールです。
基本はソースコード中のコメント行 (#..) を利用して、ドキュメントを生成します。

さらに、ドキュメントは静的なドキュメント(HTMLやMarkdown)ではなく、デフォルトの設定では、Railsアプリケーション上の動的ページの1つとして、アプリケーションと同じURLの元で表示できるようになっています。

ざっくりと、以下のような機能があります。

  • 動的にAPIドキュメントを生成・表示できる
    • APIドキュメントをキャッシュする機能や、静的ページに書き出す機能がある
    • 多言語化に対応している
  • APIのバージョニングに対応したドキュメントが設定できる
  • APIのパラメータに対してバリデーション機能がある
    • バリデーションはカスタマイズ可能

簡単な設定方法

Gemfileに以下を追加します。
apipieでのドキュメントは、複数行に渡る説明は、デフォルトでTextile形式での記載になります。
ただし、設定に応じてMarkdown形式で記述することができますので、Markdownを使いたい場合は、maruku も追加になります。

# Githubから最新版を取得する場合はこちら
# gem 'apipie-rails', :github => 'Apipie/apipie-rails'
gem 'apipie-rails'
# for Markdown
gem 'maruku'

Gemfileに追加したら、bundle (bundle install) を実行して、gemを登録します。
これだけでは特にアプリケーションの動作に変更はありませんので、次の処理に進みます。

ルーティング / イニシャライザを追加

gemが入ったら、apipieのジェネレータを使ってセットアップします。
rails g apipie:installを実行すると、ルーティングも追加されます。
また、apipie用のイニシャライザも生成されます。

$ bundle exec rails g apipie:install
Running via Spring preloader in process 61
      create  config/initializers/apipie.rb
       route  apipie

追加された config/initializers/apipie.rb には、あまり細かい設定が書かれていませんが、実はかなり多くのオプション設定が出来ます。
コメント文だけで、どんな設定ができるかちょっとわかり難いです。

ここでも触れますが、興味のある方は 公式のドキュメントやデモ用のソースコード、それから、apipie-rails のspec/dummy のソースコードが参考になりますので、ぜひ参照してみてください。

念のためルーティングもチェックします。

routes.rb
Rails.application.routes.draw do
  apipie # ここが追加されました
  resources :users
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

デフォルトでは、"apipie" (http://localhost:3000/apipie) というパスで、ドキュメントが表示されるようになります。

ドキュメントはどこに書くの?

全体の設定はイニシャライザに記載

APIのドキュメントに対する全体の設定や、共通する説明は、config/initializers/apipie.rb に記載します。
今回のサンプルはこちらです。

config/initializers/apipie.rbから抜粋
Apipie.configure do |config|

  # ドキュメントのタイトル
  config.app_name = 'API Document'
  config.api_base_url['public'] = ''
  config.doc_base_url = '/apipie'
  config.default_locale = 'ja'

  # apiに該当するコントローラーのソースの場所
  config.api_controllers_matcher = Rails.root.join('app', 'controllers', '**', '*.rb')
  config.default_version = 'public'

  # ドキュメントのローカライズ
  # /apipie/public/users.ja.html, /apipie/public/users.en.html といった
  # URLでドキュメントが提示されます
  config.languages = %w[en ja]

  # ローカライズの必要がない場合は、コメントインします
  # config.translate = false
end

詳細はコントローラ側に記載

さらに、APIの個別のエンドポイントについては、対応するコントローラーのソースコード中にコメント形式でドキュメントを書いていくことになります。
今回のサンプルはこちらです。

uers_controller.rbから抜粋
  api :GET, '/users', 'ユーザ一覧を返します'
  # エラーの指定はこのような形で
  error code: 401, desc: 'Unauthorized'
  error code: 404, desc: 'Not Found'

  # 利用例は example に記載
  example <<-EDOC
  $ curl http://localhost:3000/users/
        [
          {
            "id": 1,
            "created_at": "2017-12-21T13:06:56.966Z",
            "updated_at": "2017-12-21T13:06:56.966Z",
            "name": "サンプルユーザ1"
          },
          {
            "id": 2,
            "created_at": "2017-12-21T13:06:56.966Z",
            "updated_at": "2017-12-21T13:06:56.966Z",
            "name": "サンプルユーザ2"
          }
        ]
  EDOC
  def index
    @users = User.all

    render json: @users
  end

上記のindexアクションに対するドキュメントの設定は、api :GET ... EDOC の部分までになります。

ドキュメント用のパスにアクセスしてみます

サンプルのソースコードでrails sでアプリケーションを起動してみます。
うまくいくと、 http://localhost:3000/apipie/ にアクセスすると下記のようなページが表示されます。

トップページの記載内容や、タイトルの設定は、イニシャライザでの指定が反映されているのが分かります。


document-top.png


画面右上のナビゲーションには、言語切り替えのリンクがあります。
クリックすると、http://localhost:3000/apipie/public.ja.html、public.en.html といった形でURLと見出しなどの言語表示が切り替わります。

Usersリソースのドキュメント

ドキュメントのトップページから、Usersリソースのドキュメントのリンクをクリックすると、コントローラに記載した内容が、アクションごとに表示されます。


resource_users.png


適度なcssとJavaScriptも提供されているので、クリックすると詳細説明がexpandで表示されたりします。

エラーコードやパラメータも、うまい具合にドキュメントに反映されていますね!

今回の例では、APIのバージョニングをしていないのですが、/apipie/api/v1/users, /apipie/api/v2/users といった形でドキュメントが提示可能になっています。

このあたり、ぜひ公式のリポジトリやデモ用のソースコードをご覧になって下さいね。

パラメーターバリデーションの機能

さて、apipieでのドキュメント記載は、実際はコメント文 (#...) 以外に、専用のメソッドを利用して充実させていく形になります。

ドキュメントに載せたい場合は、api! もしくは api での指定をして、そこから追加の情報を添えていきます。
エラーメッセージやフォーマットの他に、受け取るパラメータの指定が param もしくは param group という形で設定します。

この設定はドキュメント側に反映されるとともに、apipie-railsがリクエストパラメータをチェックして、ドキュメントで指定した以外のフォーマットなら、バリデーションエラーを返すという機能が付いています。

以下の例では、 "param :id, :number..." の行が該当します。

uers_controller.rbから抜粋
  api :GET, '/users/:id', '指定のIDのユーザ情報を返します'
  description '指定のIDのユーザ情報を返します'
  formats ['json']
  error 401, 'Unauthorized'
  error code: 404, description: 'Not Found'

  # パラメータ情報に加えて、簡単なバリデーションもapipie側で実施してくれます
  param :id, :number, desc: 'user id', required: true

  example <<-EDOC
  $ curl http://localhost:3000/users/

        {
          "id": 2,
          "created_at": "2017-12-21T13:06:56.966Z",
          "updated_at": "2017-12-21T13:06:56.966Z",
          "name": "サンプルユーザ2"
        }
  EDOC
  # まだこのapiのドキュメントを表示させたくない場合は show false を指定
  # show false
  def show
    render json: @user
  end

バリデーションの設定は、いくつかapipieが有効なものを提供してくれています。
プリミティブ型かどうかのチェック、パターンマッチ、配列のどれかに該当するか etc...。

さらに、自分でバリデータを用意して適用することもできます。バリデータはイニシャライザに設定する形になります。

こちらについては、公式リポジトリのspecのサンプルが役に立つと思います。

バリデーション実施タイミング (20171223追記)

Apipie-railsでのパラメータバリデーションの処理は、

  • before_actionの実施後
  • Controllerのアクションが呼ばれる前

に実施されます。
このため、before_actionで先にparam(:id) をモデルに渡すような処理をする場合は、バリデーション通過前の段階で渡されてしまいます。

もしbefore_actionよりも先にチェックしてしまいたいという場合は、"before_action :apipie_validations” を設定すると、そのほかのbefore_actionの処理が走る前にチェックしてくれます。

class UsersController < ApplicationController
  #
  # NOTE: Apipieのバリデーションをbefore_actionで実施すると、
 # before_actionでのset_userの処理の前にパラメータの
  # バリデーションチェックが走ります
  #
  before_action :apipie_validations
  before_action :set_user, only: %i[show update destroy]

  # 省略
end

また、Apipieでのバリデーションエラーが発生すると、Apipie::ParamInvalid の例外が上がります。
この辺りの処理を追加して、specを調整したソースはこちらになりますので、もしよかったら比べてみてくださいね。

staticなドキュメントを生成してみる

htmlで書き出し

さて、apipie はさらにドキュメントを静的ファイルに書き出す機能が付いています。
rake taskが用意されているので、コマンドを実行するだけで、以下のようなファイルが出来上がりました。

$ bundle exec rake apipie:static

パターンやrake taskのオプションも幾つかあり、CSSとJSを利用して少し見栄えが良いものや、シンプルなHTMLのものもあります。

rake_apipie_static.png

swagger_json形式で書き出し (masterブランチ)

この記事を書いている時点で、GitHub上のmasterブランチでは、swagger_json形式での書き出しができるようになっています。

試しにGemfileの設定を変えて、swaggerのjsonを生成してみました。

$ bundle exec rake apipie:static_swagger_json

$ tree doc/apidoc/ | grep swagger
├── schema_swagger_form_data.en.json
├── schema_swagger_form_data.ja.json
├── schema_swagger_form_data.json

 $ head doc/apidoc/schema_swagger_form_data.ja.json
{
  "swagger": "2.0",
  "info": {
    "title": "API Document (params in:formData)",
    "description": "APIエンドポイント",
    "version": "public",
    "x-copyright": "&copy; 2017 Sample Copyright"
  },
  "basePath": "",
  "consumes": [...]
............

ただし、apipie自体にはswagger jsonのプレビュー機能はありません。かと言って直にAPI Gatewayにアップする...という訳にもいかないので、いくつかあるswaggerのプレビュー用のツールを使ってみます。

今回はIntelliJ IDEAのSwagger pluginを使ってみました。

preview-swagger.png

ほかにも、オンラインの https://swagger.io/swagger-editor/ を使っても確認が出来ました!

おわりに

以上、毎回ですが駆け足ながら、キャプチャ付きで紹介させていただきました。

RailsのAPIとドキュメントは Grape + Swaggerという組み合わせも使ったことがあるのですが、職場では、「Rails5のAPI機能をそのまま使いたい、でもAPIドキュメントはやっぱりほしいよね」ということで、apipie-railsを使ってみることになりました。
導入当初は swagger_jsonの生成は出来ていなかったと思うのですが、いつの間にか機能が追加されていました。

職場での設定自体は、チームの方がうまい具合に行ってくれていたので、自分自身では意識していなかったのですが、こうして記事にしてみると、オプション本当にたくさんあるな....とびっくりしています。

記事に関しての不備や至らない点がありましたら、ご指摘いただけますと幸いです。

あらためてのお礼

冒頭に添えた通り、今回は紹介記事用のソースコードは、@c5meruさまのリポジトリを利用させていただきました。

記事を拝見した際に、実はspecを利用してドキュメントを生成するautodocについては知らなかったので、この情報ともども大変お世話になりました。
記事に関してご了解をいただいてから公開をさせていただいておりますが、
本当にありがとうございました!

また、apipie-railsの多言語(日本語)対応をされているみなさま、情報を発信してくださっているみなさま、ありがとうございました!