このガイドの内容:
従来、Railsの「API」というと、プログラムからアクセスできるAPIをWebアプリケーションに追加することを指すのが通例でした。たとえば、GitHubが提供するAPI をカスタムクライアントから利用できます。
近年、さまざまなクライアント側フレームワークが登場したことによって、Railsで作ったバックエンドサーバ―を、他のWebアプリケーションとネイティブアプリケーションの間で共有する手法が増えてきました。
たとえば、Twitterは自社のWebアプリケーションで パブリックAPI を利用しています。このWebアプリケーションは、JSONリソースを消費するだけの静的サイトとして構築されています。
多くの開発者が、Railsで生成したHTMLフォームやリンクをサーバー間のやりとりに使うではなく、Webアプリケーションを単なるAPIクライアントにとどめて、JSON APIを利用するHTMLとJavaScriptの提供に徹するようになってきました。
本ガイドでは、JSONリソースをAPIクライアントに提供するRailsアプリケーションの構築方法を解説します。クライアント側フレームワークについても言及します。
RailsでJSON APIを構築することについて、多くの開発者から真っ先に受ける質問といえば「RailsでJSONを出力するのは大げさすぎませんか?Sinatraじゃだめなんですか?」です。
単なるAPIサーバーであれば、おそらくそうでしょう。しかし、フロントのHTMLの比重が非常に大きいアプリケーションであっても、ロジックのほとんどはビューレイヤ以外の部分にあるのです。
Railsが多くの開発者に採用されている理由は、細かな設定をいちいち決めなくても、すばやくアプリケーションを立ち上げられるからこそです。
APIアプリケーションの開発にすぐ役立つRailsの機能をいくつかご紹介します。
ミドルウェア層で提供される機能
paramsでアクセスできます。もちろん、ネストしたURLエンコードパラメータも扱えます。ETagやLast-Modifiedを使った条件付きGETを扱えます。条件付きGETはリクエストヘッダを処理し、正しいレスポンスヘッダとステータスコードを返します。コントローラに
stale? チェックを追加するだけで、HTTPの細かなやりとりはRailsが代行してくれます。HEADリクエストを透過的にGETリクエストに変換し、ヘッダだけを返します。これによって、すべてのRails APIでHEADリクエストを確実に利用できます。Rackミドルウェアのこうした既存の機能を自前で構築することもできますが、Railsのデフォルトのミドルウェアを「JSON生成専用」に使うだけでも多数のメリットが得られます。
Action Pack層で提供される機能
head :no_contentやredirect_to user_url(current_user)などをすぐ利用できます。ヘッダレスポンスを自分で書かずに済みます。もちろん、Railsのブートプロセスでは、登録済みのコンポーネントをすべて読み込んで連携します。たとえば、ブート中にconfig/database.ymlファイルを使ってActive Recordを設定します。
忙しい方へ: Railsからビュー層を取り除いた後で、どんな機能を引き続き利用できるのでしょう。手短に言うと「ほとんどの機能」です。
APIサーバーにするRailsアプリケーションをすぐにでも構築したいのであれば、機能を限定したRailsサブセットを作って、必要な機能を順次追加するのがよいでしょう。
API専用Railsアプリケーションの生成には次のコマンドを使います。
$ rails new my_api --api
上のコマンドを実行すると、次の3つの操作を行います。
ApplicationControllerを、通常のActionController::Baseの代わりにActionController::APIから継承します。ミドルウェアと同様、Action Controllerモジュールのうち、ブラウザ向けアプリケーションでしか使われないモジュールをすべて除外します。既存のアプリケーションをAPI専用に変えるには、次の手順をお読みください。
config/application.rbのApplicationクラス定義の冒頭に、次を追加します。
config.api_only = true
developmentモードでのエラー発生時にレスポンスで使う形式を設定するには、config/environments/development.rbファイルでconfig.debug_exception_response_formatを設定します。
値を:defaultにすると、HTMLページにデバッグ情報を表示します。
config.debug_exception_response_format = :default
値を:apiにすると、レスポンスの形式を保ったままデバッグ情報を表示します。
config.debug_exception_response_format = :api
config.api_onlyをtrueに設定すると、config.debug_exception_response_formatがデフォルトで:apiに設定されます。
最後に、app/controllers/application_controller.rbの以下のコードを置き換えます。
class ApplicationController < ActionController::Base end
上を以下に変更します。
class ApplicationController < ActionController::API end
APIアプリケーションでは、デフォルトで以下のミドルウェアを利用できます。
Rack::SendfileActionDispatch::StaticActionDispatch::ExecutorActiveSupport::Cache::Strategy::LocalCache::MiddlewareRack::RuntimeActionDispatch::RequestIdActionDispatch::RemoteIpRails::Rack::LoggerActionDispatch::ShowExceptionsActionDispatch::DebugExceptionsActionDispatch::ReloaderActionDispatch::CallbacksActiveRecord::Migration::CheckPendingRack::HeadRack::ConditionalGetRack::ETagMyApi::Application::Routes詳しくは、Rackガイドの「Rails と Rack - ミドルウェアスタックの内容」をご覧ください。
ミドルウェアは、Active Recordなど他のプラグインによって追加されることがあります。一般に、構築するアプリケーションの種類とミドルウェアは関係ありませんが、API専用Railsアプリケーションでは意味があります。
アプリケーションの全ミドルウェアを表示するには次のコマンドを使います。
$ rails middleware
Railsにデフォルトで追加されるミドルウェアは、アプリケーションの設定に基づくキャッシュストア(デフォルトはmemcache)を提供します。このため、Railsに組み込まれているHTTPキャッシュはこのキャッシュストアに依存します。
たとえば、次のようにstale?メソッドを呼び出すとします。
def show @post = Post.find(params[:id]) if stale?(last_modified: @post.updated_at) render json: @post end end
stale?呼び出しは、リクエストにあるIf-Modified-Sinceヘッダと@post.updated_atを比較します。ヘッダが最終更新時より新しい場合、「304 Not Modified」を返すか、レスポンスをレンダリングしてLast-Modifiedヘッダをそこに表示します。
通常、この動作はクライアントごとに行われますが、キャッシュミドルウェアがあるとクライアント間でこのキャッシュを共有できるようになります。クロスクライアントキャッシュは、stale?の呼び出し時に有効にできます。
def show @post = Post.find(params[:id]) if stale?(last_modified: @post.updated_at, public: true) render json: @post end end
キャッシュミドルウェアは、URLに対応するLast-Modified値をRailsキャッシュに保存し、以後同じURLへのリクエストを受信したときにIf-Modified-Sinceヘッダを追加します。
これは、HTTPセマンティクスを利用したページキャッシュと考えることができます。
Railsコントローラ内部でsend_fileメソッドを実行すると、X-Sendfileヘッダが設定されます。実際のファイル送信を担当するのはRack::Sendfileです。
ファイル送信のアクセラレーションをサポートするフロントエンドサーバーでは、Rack::Sendfileの代わりにフロントエンドサーバーがファイルを送信します。
フロントエンドサーバーでのファイル送信に使うヘッダの名前は、該当する環境設定ファイルのconfig.action_dispatch.x_sendfile_headerで設定できます。
著名なフロントエンドでRack::Sendfileを使う方法について、詳しくは the Rack::Sendfile documentation をご覧ください。
定番のサーバーでファイル送信アクセラレーションを有効にするには、ヘッダに次のような値を設定します。
# Apacheやlighttpd config.action_dispatch.x_sendfile_header = "X-Sendfile" # Nginx config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"
これらのオプションを有効にするには、Rack::Sendfileドキュメントに従ってサーバーを設定してください。
ActionDispatch::Request#paramsは、クライアントからのパラメータをJSON形式で受け取り、コントローラ内部のparamsでアクセスできるようにします。
この機能を使うには、JSONエンコード化したパラメータをクライアントから送信し、Content-Typeにapplication/jsonを指定する必要があります。
jQueryでは次のように行います。
jQuery.ajax({
type: 'POST',
url: '/people',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
success: function(json) { }
});
ActionDispatch::Requestでは、このContent-Typeで
次のパラメータを受け取ります。
{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }
Railsではこの他にも、APIアプリケーション向けのミドルウェアを多数利用できます。特に、ブラウザがAPIクライアントになる場合は、次のミドルウェアが便利です。
Rack::MethodOverrideActionDispatch::CookiesActionDispatch::FlashActionDispatch::Session::CacheStoreActionDispatch::Session::CookieStoreActionDispatch::Session::MemCacheStoreこれらのミドルウェアは、次の方法で追加できます。
config.middleware.use Rack::MethodOverride
API専用ミドルウェアに含めたくないミドルウェアは、次の方法で削除できます。
config.middleware.delete ::Rack::Sendfile
これらのミドルウェアを削除すると、Action Controllerの一部の機能が利用できなくなりますので、ご注意ください。
APIアプリケーション(ActionController::APIを利用)には、デフォルトで次のコントローラモジュールが含まれます。
ActionController::UrlFor: url_forなどのヘルパーを提供ActionController::Redirecting: redirect_toをサポートAbstractController::RenderingとActionController::ApiRendering: 基本的なレンダリングのサポートActionController::Renderers::All: render :jsonなどのサポートActionController::ConditionalGet: stale?のサポートActionController::BasicImplicitRender: 指定がない限り、空のレスポンスを返すActionController::StrongParameters: パラメータの許可リストをサポート(Active Modelのマスアサインメントと連携)ActionController::ForceSSL: force_sslのサポートActionController::DataStreaming: send_fileやsend_dataのサポートAbstractController::Callbacks: before_actionなどのヘルパーをサポートActionController::Rescue: rescue_fromをサポートActionController::Instrumentation: Action Controllerで定義するinstrumentationフックをサポート(詳しくはthe instrumentation guide を参照)ActionController::ParamsWrapper: パラメータハッシュをラップしてネスト化ハッシュにし、POSTリクエスト送信時のルート要素指定などを不要にするActionController::Head: コンテンツのないヘッダのみのレスポンスを返すのに用いる他のプラグインによってモジュールが追加されることもあります。ActionController::APIの全モジュールのリストは、次のコマンドで表示できます。
$ bin/rails c >> ActionController::API.ancestors - ActionController::Metal.ancestors => [ActionController::API, ActiveRecord::Railties::ControllerRuntime, ActionDispatch::Routing::RouteSet::MountedHelpers, ActionController::ParamsWrapper, ... , AbstractController::Rendering, ActionView::ViewPaths]
Action Controllerのどのモジュールも、自身が依存するモジュールを把握しているので、コントローラにモジュールを含めるだけで、必要な依存モジュールも同様に設定できます。
よく追加されるのは、次のようなモジュールです。
AbstractController::Translation: ローカライズ用のlメソッドや、翻訳用のtメソッドActionController::HttpAuthentication::Basic::ControllerMethodsActionController::HttpAuthentication::Digest::ControllerMethodsActionController::HttpAuthentication::Token::ControllerMethodsActionView::Layouts: レンダリングのレイアウトをサポートActionController::MimeResponds: respond_toをサポートActionController::Cookies: cookiesのサポート(署名や暗号化も含む)。cookiesミドルウェアが必要。モジュールはApplicationControllerに追加するのが最適ですが、個別のコントローラに追加しても構いません。
Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。
原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨
本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。
Railsガイド運営チーム (@RailsGuidesJP)
Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。