Grape Gemチュートリアル:RubyでRESTのようなAPIを構築する方法

公開: 2022-03-11

Ruby On Rails開発者として、JavaScriptを多用するリッチインターネットクライアントまたはネイティブのiPhoneおよびAndroidアプリをサポートするために、APIエンドポイントを使用してアプリケーションを拡張する必要があることがよくあります。 アプリケーションの唯一の目的がJSONAPIを介してiPhone/Androidアプリを提供することである場合もあります。

このチュートリアルでは、Ruby用のRESTのようなAPIマイクロフレームワークであるGrapeを使用して、JSONAPI用のRailsでバックエンドサポートを構築する方法を示します。 Grapeは、Webアプリケーションに干渉することなく、Webアプリケーションを補完するマウント可能なラックエンジンとして実行するように設計されています。

GrapeGemを使用したRubyのWebAPI

使用事例

このチュートリアルで焦点を当てるユースケースは、ペアプログラミングセッションをキャプチャして確認できるアプリケーションです。 アプリケーション自体はObjectiveCでiOS用に作成され、データを保存および取得するためにバックエンドサービスと通信する必要があります。 このチュートリアルでは、JSONAPIをサポートする堅牢で安全なバックエンドサービスの作成に焦点を当てています。

APIは、次のメソッドをサポートします。

  • システムにログインする
  • ペアプログラミングセッションレビューのクエリ

注:ペアプログラミングセッションのレビューをクエリする機能を提供することに加えて、実際のAPIは、データベースに含めるためにペアプログラミングレビューを送信するための機能も提供する必要があります。 APIを介してそれをサポートすることはこのチュートリアルの範囲を超えているため、データベースにペアプログラミングレビューのサンプルセットが入力されていると単純に想定します。

主な技術要件は次のとおりです。

  • すべてのAPI呼び出しは有効なJSONを返す必要があります
  • 失敗したすべてのAPI呼び出しは、後で再現できるように適切なコンテキストと情報とともに記録し、必要に応じてデバッグする必要があります

また、アプリケーションは外部クライアントにサービスを提供する必要があるため、セキュリティに注意を払う必要があります。 その目的に向けて:

  • 各リクエストは、追跡する開発者の小さなサブセットに制限する必要があります
  • すべてのリクエスト(ログイン/サインアップを除く)は認証される必要があります

テスト駆動開発とRSpec

APIの決定論的な動作を保証するために、ソフトウェア開発アプローチとしてテスト駆動開発(TDD)を使用します。

テストの目的で、RubyOnRailsコミュニティでよく知られているテストフレームワークであるRSpecを使用します。 したがって、この記事では「テスト」ではなく「仕様」を参照します。

包括的なテスト方法は、「ポジティブ」テストと「ネガティブ」テストの両方で構成されます。 負の仕様は、たとえば、一部のパラメーターが欠落しているか正しくない場合にAPIエンドポイントがどのように動作するかを指定します。 ポジティブスペックは、APIが正しく呼び出される場合をカバーします。

入門

バックエンドAPIの基盤を築きましょう。 まず、新しいRailsアプリケーションを作成する必要があります。

 rails new toptal_grape_blog

次に、 rspec-railsをgemfileに追加してRSpecをインストールします。

 group :development, :test do gem 'rspec-rails', '~> 3.2' end

次に、コマンドラインから次のコマンドを実行する必要があります。

 rails generate rspec:install

また、テストフレームワークに既存のオープンソースソフトウェアを活用することもできます。 具体的には:

  • Devise -Wardenに基づくRailsの柔軟な認証ソリューション
  • factory_girl_rails -Rubyオブジェクトをテストデータとして設定するためのライブラリであるfactory_girlのRails統合を提供します

ステップ1:これらをgemfileに追加します。

 ... gem 'devise' ... group :development, :test do ... gem 'factory_girl_rails', '~> 4.5' ... end

ステップ2:ユーザーモデルを生成し、 deviseを初期化して、それをユーザーモデルに追加します(これにより、ユーザークラスを認証に使用できるようになります)。

 rails g model user rails generate devise:install rails generate devise user

ステップ3:仕様でユーザー作成の短縮バージョンを使用するために、 rails_helper.rbファイルにfactory_girl構文メソッドを含めます。

 RSpec.configure do |config| config.include FactoryGirl::Syntax::Methods

ステップ4:ブドウの宝石をDSLに追加し、インストールします。

 gem 'grape' bundle

ユーザーログインのユースケースと仕様

バックエンドは、基本的なログイン機能をサポートする必要があります。 有効なログイン要求が登録された電子メールアドレスとパスワードのペアで構成されていると仮定して、 login_specのスケルトンを作成しましょう。

 require 'rails_helper' describe '/api/login' do context 'negative tests' do context 'missing params' do context 'password' do end context 'email' do end end context 'invalid params' do context 'incorrect password' do end context 'with a non-existent login' do end end end context 'positive tests' do context 'valid params' do end end end

いずれかのパラメータが欠落している場合、クライアントは「電子メールが欠落しています」または「パスワードが欠落しています」というエラーメッセージとともに、400のHTTPリターンステータスコード(つまり、不正な要求)を受け取る必要があります。

このテストでは、有効なユーザーを作成し、ユーザーの電子メールとパスワードをこのテストスイートの元のパラメーターとして設定します。 次に、パスワード/電子メールを省略するか、オーバーライドすることにより、特定の仕様ごとにこのパラメーターハッシュをカスタマイズします。

仕様の最初にユーザーとパラメーターのハッシュを作成しましょう。 このコードをdescribeブロックの後に配置します。

 describe '/api/login' do let(:email) { user.email } let(:password) { user.password } let!(:user) { create :user } let(:original_params) { { email: email, password: password } } let(:params) { original_params } ...

次に、「欠落しているパラメータ」/「パスワード」コンテキストを次のように拡張できます。

 let(:params) { original_params.except(:password) } it_behaves_like '400' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'password is missing'

ただし、「email」と「password」のコンテキスト全体で期待値を繰り返す代わりに、期待値と同じ共有例を使用できます。 このために、 rails_helper.rbファイルでこの行のコメントを解除する必要があります。

 Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }

次に、3つのRSpec共有例をspec/support/shared.rbに追加する必要があります。

 RSpec.shared_examples 'json result' do specify 'returns JSON' do api_call params expect { JSON.parse(response.body) }.not_to raise_error end end RSpec.shared_examples '400' do specify 'returns 400' do api_call params expect(response.status).to eq(400) end end RSpec.shared_examples 'contains error msg' do |msg| specify "error msg is #{msg}" do api_call params json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end

これらの共有例は、(DRYの原則に従って)仕様でAPIエンドポイントを1回だけ定義できるようにするapi_callメソッドを呼び出しています。 このメソッドを次のように定義します。

 describe '/api/login' do ... def api_call *params post "/api/login", *params end ...

また、ユーザー向けにファクトリをカスタマイズする必要があります。

 FactoryGirl.define do factory :user do password "Passw0rd" password_confirmation { |u| u.password } sequence(:email) { |n| "test#{n}@example.com" } end end

そして最後に、仕様を実行する前に、移行を実行する必要があります。

 rake db:migrate

ただし、APIエンドポイントをまだ実装していないため、この時点ではまだ仕様が失敗することに注意してください。 次です。

LoginAPIエンドポイントの実装

手始めに、ログインAPI( app/api/login.rb )の空のスケルトンを記述します。

 class Login < Grape::API format :json desc 'End-points for the Login' namespace :login do desc 'Login via email and password' params do requires :email, type: String, desc: 'email' requires :password, type: String, desc: 'password' end post do end end end

次に、APIエンドポイント( app/api/api.rb )を集約するアグリゲータークラスを記述します。

 class API < Grape::API prefix 'api' mount Login end

OK、これでAPIをルートにマウントできます。

 Rails.application.routes.draw do ... mount API => '/' ... end

次に、不足しているパラメーターをチェックするコードを追加しましょう。 Grape::Exceptions::ValidationErrorsからレスキューすることで、そのコードをapi.rbに追加できます。

 rescue_from Grape::Exceptions::ValidationErrors do |e| rack_response({ status: e.status, error_msg: e.message, }.to_json, 400) end

無効なパスワードについては、http応答コードが不正アクセスを意味する401であるかどうかを確認します。 これを「不正なパスワード」コンテキストに追加しましょう。

 let(:params) { original_params.merge(password: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'contains error msg', 'Bad Authentication Parameters'

次に、同じロジックが「ログインが存在しない」コンテキストにも追加されます。

次に、無効な認証の試行を処理するロジックを次のようにlogin.rbに実装します。

 post do user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) else error_msg = 'Bad Authentication Parameters' error!({ 'error_msg' => error_msg }, 401) end end

この時点で、ログインAPIのすべてのネガティブな仕様は適切に動作しますが、ログインAPIのポジティブな仕様をサポートする必要があります。 私たちの肯定的な仕様では、エンドポイントが有効なJSONと有効なトークンを含む200のHTTP応答コード(つまり、成功)を返すことを期待しています。

 it_behaves_like '200' it_behaves_like 'json result' specify 'returns the token as part of the response' do api_call params expect(JSON.parse(response.body)['token']).to be_present end

また、応答コード200の期待値をspec/support/shared.rbに追加しましょう。

 RSpec.shared_examples '200' do specify 'returns 200' do api_call params expect(response.status).to eq(200) end end

ログインに成功した場合、最初の有効なauthentication_tokenをユーザーの電子メールと一緒に次の形式で返します。

 {'email':<the_email_of_the_user>, 'token':<the users first valid token>}

そのようなトークンがまだない場合は、現在のユーザー用にトークンを作成します。

 ... if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 else ...

これが機能するためには、ユーザーに属するAuthenticationTokenクラスが必要です。 このモデルを生成してから、対応する移行を実行します。

 rails g model authentication_token token user:references expires_at:datetime rake db:migrate

また、対応する関連付けをユーザーモデルに追加する必要があります。

 class User < ActiveRecord::Base has_many :authentication_tokens end

次に、 AuthenticationTokenクラスに有効なスコープを追加します。

 class AuthenticationToken < ActiveRecord::Base belongs_to :user validates :token, presence: true scope :valid, -> { where{ (expires_at == nil) | (expires_at > Time.zone.now) } } end

whereステートメントでRuby構文を使用したことに注意してください。 これは、activerecordクエリでRuby構文のサポートを可能にするsqueelを使用することで可能になります。

検証済みのユーザーの場合、 grape-entityの宝石の機能を活用して、「トークンエンティティを持つユーザー」と呼ばれるエンティティを作成します。

エンティティの仕様を記述して、 user_with_token_entity_spec.rbファイルに入れましょう。

 require 'rails_helper' describe Entities::UserWithTokenEntity do describe 'fields' do subject(:subject) { Entities::UserWithTokenEntity } specify { expect(subject).to represent(:email)} let!(:token) { create :authentication_token } specify 'presents the first available token' do json = Entities::UserWithTokenEntity.new(token.user).as_json expect(json[:token]).to be_present end end end

次に、エンティティをuser_entity.rbに追加しましょう。

 module Entities class UserEntity < Grape::Entity expose :email end end

そして最後に、 user_with_token_entity.rbに別のクラスを追加します。

 module Entities class UserWithTokenEntity < UserEntity expose :token do |user, options| user.authentication_tokens.valid.first.token end end end

トークンが無期限に有効であり続けることを望まないため、トークンは1日後に期限切れになります。

 FactoryGirl.define do factory :authentication_token do token "MyString" expires_at 1.day.from_now user end end

これで、新しく作成したUserWithTokenEntityを使用して期待されるJSON形式を返すことができます。

 ... user = User.find_by_email params[:email] if user.present? && user.valid_password?(params[:password]) token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user) status 200 present token.user, with: Entities::UserWithTokenEntity else ...

いいね。 これで、すべての仕様に合格し、基本的なログインAPIエンドポイントの機能要件がサポートされます。

ペアプログラミングセッションレビューAPIエンドポイント:はじめに

バックエンドでは、ログインした認定開発者がペアプログラミングセッションのレビューをクエリできるようにする必要があります。

新しいAPIエンドポイントは/api/pair_programming_sessionにマウントされ、プロジェクトに属するレビューを返します。 この仕様の基本的なスケルトンを書くことから始めましょう:

 require 'rails_helper' describe '/api' do describe '/pair_programming_session' do def api_call *params get '/api/pair_programming_sessions', *params end context 'invalid params' do end context 'valid params' do end end end

対応する空のAPIエンドポイント( app/api/pair_programming_sessions.rb )も記述します。

 class PairProgrammingSessions < Grape::API format :json desc 'End-points for the PairProgrammingSessions' namespace :pair_programming_sessions do desc 'Retrieve the pairprogramming sessions' params do requires :token, type: String, desc: 'email' end get do end end end

次に、新しいAPI( app/api/api.rb )をマウントしましょう。

 ... mount Login mount PairProgrammingSessions end

要件に合わせて、仕様とAPIエンドポイントを1つずつ拡張してみましょう。

ペアプログラミングセッションレビューAPIエンドポイント:検証

最も重要な非機能セキュリティ要件の1つは、追跡する開発者の小さなサブセットにAPIアクセスを制限することだったので、次のように指定しましょう。

 ... def api_call *params get '/api/pair_programming_sessions', *params end let(:token) { create :authentication_token } let(:original_params) { { token: token.token} } let(:params) { original_params } it_behaves_like 'restricted for developers' context 'invalid params' do ...

次に、 shared_exampleshared.rbを作成して、登録済みの開発者の1人からリクエストが送信されていることを確認します。

 RSpec.shared_examples 'restricted for developers' do context 'without developer key' do specify 'should be an unauthorized call' do api_call params expect(response.status).to eq(401) end specify 'error code is 1001' do api_call params json = JSON.parse(response.body) expect(json['error_code']).to eq(ErrorCodes::DEVELOPER_KEY_MISSING) end end end

また、( app/models/error_codes.rbに) ErrorCodesクラスを作成する必要があります。

 module ErrorCodes DEVELOPER_KEY_MISSING = 1001 end

APIは将来拡張されると予想されるため、アプリケーション内のすべてのAPIエンドポイントで再利用できるauthorization_helperを実装して、登録済みの開発者のみにアクセスを制限します。

 class PairProgrammingSessions < Grape::API helpers ApiHelpers::AuthenticationHelper before { restrict_access_to_developers }

ApiHelpers::AuthenticationHerlperモジュール( app/api/api_helpers/authentication_helper.rb )でメソッドrestrict_access_to_developersを定義します。 このメソッドは、ヘッダーの下のキーAuthorizationに有効なApiKeyが含まれているかどうかを確認するだけです。 (APIへのアクセスを希望するすべての開発者は有効なApiKeyを必要とします。これはシステム管理者または自動登録プロセスを介して提供できますが、そのメカニズムはこの記事の範囲を超えています。)

 module ApiHelpers module AuthenticationHelper def restrict_access_to_developers header_token = headers['Authorization'] key = ApiKey.where{ token == my{ header_token } } Rails.logger.info "API call: #{headers}\tWith params: #{params.inspect}" if ENV['DEBUG'] if key.blank? error_code = ErrorCodes::DEVELOPER_KEY_MISSING error_msg = 'please aquire a developer key' error!({ :error_msg => error_msg, :error_code => error_code }, 401) # LogAudit.new({env:env}).execute end end end end

次に、ApiKeyモデルを生成し、移行を実行する必要があります。rails g model api_key token rake db:migrate

これが完了すると、 spec/api/pair_programming_spec.rbで、ユーザーが認証されているかどうかを確認できます。

 ... it_behaves_like 'restricted for developers' it_behaves_like 'unauthenticated' ...

また、すべての仕様( spec/support/shared.rb )で再利用できるunauthenticatedされていない共有の例を定義しましょう。

 RSpec.shared_examples 'unauthenticated' do context 'unauthenticated' do specify 'returns 401 without token' do api_call params.except(:token), developer_header expect(response.status).to eq(401) end specify 'returns JSON' do api_call params.except(:token), developer_header json = JSON.parse(response.body) end end end

この共有例では、開発者ヘッダーにトークンが必要なので、それを仕様( spec/api/pair_programming_spec.rb )に追加しましょう。

 ... describe '/api' do let(:api_key) { create :api_key } let(:developer_header) { {'Authorization' => api_key.token} } ...

ここで、 app/api/pair_programming_session.rbで、ユーザーの認証を試みましょう。

 ... class PairProgrammingSessions < Grape::API helpers ApiHelpers::AuthenticationHelper before { restrict_access_to_developers } before { authenticate! } ...

authenticate! AuthenticationHelperのメソッド( app/api/api_helpers/authentication_helper.rb ):

 ... module ApiHelpers module AuthenticationHelper TOKEN_PARAM_NAME = :token def token_value_from_request(token_param = TOKEN_PARAM_NAME) params[token_param] end def current_user token = AuthenticationToken.find_by_token(token_value_from_request) return nil unless token.present? @current_user ||= token.user end def signed_in? !!current_user end def authenticate! unless signed_in? AuditLog.create data: 'unauthenticated user access' error!({ :error_msg => "authentication_error", :error_code => ErrorCodes::BAD_AUTHENTICATION_PARAMS }, 401) end end ...

(エラーコードBAD_AUTHENTICATION_PARAMSErrorCodesクラスに追加する必要があることに注意してください。)

次に、開発者が無効なトークンを使用してAPIを呼び出した場合に何が起こるかを特定しましょう。 その場合、戻りコードは「不正アクセス」を通知する401になります。 結果はJSONになり、監査可能なものが作成されます。 したがって、これをspec/api/pair_programming_spec.rbに追加します:

 ... context 'invalid params' do context 'incorrect token' do let(:params) { original_params.merge(token: 'invalid') } it_behaves_like '401' it_behaves_like 'json result' it_behaves_like 'auditable created' it_behaves_like 'contains error msg', 'authentication_error' it_behaves_like 'contains error code', ErrorCodes::BAD_AUTHENTICATION_PARAMS end end ...

「auditablecreated」、「contains error code」、「containserrormsg」の共有例をspec/support/shared.rbに追加します。

 ... RSpec.shared_examples 'contains error code' do |code| specify "error code is #{code}" do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_code']).to eq(code) end end RSpec.shared_examples 'contains error msg' do |msg| specify "error msg is #{msg}" do api_call params, developer_header json = JSON.parse(response.body) expect(json['error_msg']).to eq(msg) end end RSpec.shared_examples 'auditable created' do specify 'creates an api call audit' do expect do api_call params, developer_header end.to change{ AuditLog.count }.by(1) end end ...

また、audit_logモデルを作成する必要があります。

 rails g model audit_log backtrace data user:references rake db:migrate

ペアプログラミングセッションレビューAPIエンドポイント:結果を返す

認証および承認されたユーザーの場合、このAPIエンドポイントを呼び出すと、プロジェクトごとにグループ化されたペアプログラミングセッションレビューのセットが返されます。 それに応じてspec/api/pair_programming_spec.rbを変更しましょう。

 ... context 'valid params' do it_behaves_like '200' it_behaves_like 'json result' end ...

これは、有効なapi_keyと有効なパラメーターを使用して送信されたリクエストが200のHTTPコード(つまり、成功)を返し、結果が有効なJSONの形式で返されることを指定します。

クエリを実行してから、参加者のいずれかが現在のユーザーであるペアプログラミングセッション( app/api/pair_programming_sessions.rb )をJSON形式で返します。

 ... get do sessions = PairProgrammingSession.where{(host_user == my{current_user}) | (visitor_user == my{current_user})} sessions = sessions.includes(:project, :host_user, :visitor_user, reviews: [:code_samples, :user] ) present sessions, with: Entities::PairProgrammingSessionsEntity end ...

ペアプログラミングセッションは、データベースで次のようにモデル化されています。

  • プロジェクトとペアプログラミングセッション間の1対多の関係
  • ペアプログラミングセッションとレビューの間の1対多の関係
  • レビューとコードサンプル間の1対多の関係

それに応じてモデルを生成してから、移行を実行してみましょう。

 rails g model project name rails g model pair_programming_session project:references host_user:references visitor_user:references rails g model review pair_programming_session:references user:references comment rails g model code_sample review:references code:text rake db:migrate

次に、 has_manyアソシエーションを含むように、 PairProgrammingSessionクラスとReviewクラスを変更する必要があります。

 class Review < ActiveRecord::Base belongs_to :pair_programming_session belongs_to :user has_many :code_samples end class PairProgrammingSession < ActiveRecord::Base belongs_to :project belongs_to :host_user, class_name: :User belongs_to :visitor_user, class_name: 'User' has_many :reviews end

注:通常の状況では、最初に仕様を記述してこれらのクラスを生成しますが、それはこの記事の範囲を超えているため、その手順はスキップします。

次に、モデルをJSON表現に変換するクラスを作成する必要があります(ブドウの用語ではブドウのエンティティと呼ばれます)。 簡単にするために、モデルとブドウの実体の間で1対1のマッピングを使用します。

まず、 CodeSampleEntityapi/entities/code_sample_entity.rb )からcodeフィールドを公開します。

 module Entities class CodeSampleEntity < Grape::Entity expose :code end end

次に、定義済みのUserEntityCodeSampleEntityを再利用して、 userと関連するcode_samplesを公開します。

 module Entities class ReviewEntity < Grape::Entity expose :user, using: UserEntity expose :code_samples, using: CodeSampleEntity end end

ProjectEntityからnameフィールドも公開します。

 module Entities class ProjectEntity < Grape::Entity expose :name end end

最後に、エンティティを新しいPairProgrammingSessionsEntityにアセンブルし、 projecthost_uservisitor_user 、およびreviewsを公開します。

 module Entities class PairProgrammingSessionsEntity < Grape::Entity expose :project, using: ProjectEntity expose :host_user, using: UserEntity expose :visitor_user, using: UserEntity expose :reviews, using: ReviewEntity end end

これで、APIが完全に実装されました。

テストデータの生成

テストの目的で、 db/seeds.rbにいくつかのサンプルデータを作成します。 このファイルには、データベースにデフォルト値をシードするために必要なすべてのレコード作成が含まれている必要があります。 次に、データをrake db:seedでロードできます(または、 db:setupが呼び出されたときにdbで作成されます)。 これに含まれる可能性のあるものの例を次に示します。

 user_1 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' user_2 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' user_3 = User.create email: '[email protected]', password: 'password', password_confirmation: 'password' ApiKey.create token: '12345654321' project_1 = Project.create name: 'Time Sheets' project_2 = Project.create name: 'Toptal Blog' project_3 = Project.create name: 'Hobby Project' session_1 = PairProgrammingSession.create project: project_1, host_user: user_1, visitor_user: user_2 session_2 = PairProgrammingSession.create project: project_2, host_user: user_1, visitor_user: user_3 session_3 = PairProgrammingSession.create project: project_3, host_user: user_2, visitor_user: user_3 review_1 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your code' review_2 = session_1.reviews.create user: user_1, comment: 'Please DRY a bit your specs' review_3 = session_2.reviews.create user: user_1, comment: 'Please DRY your view templates' review_4 = session_2.reviews.create user: user_1, comment: 'Please clean your N+1 queries' review_1.code_samples.create code: 'Lorem Ipsum' review_1.code_samples.create code: 'Do not abuse the single responsibility principle' review_2.code_samples.create code: 'Use some shared examples' review_2.code_samples.create code: 'Use at the beginning of specs'

これでアプリケーションを使用する準備が整い、Railsサーバーを起動できるようになりました。

APIのテスト

Swaggerを使用して、APIの手動ブラウザーベースのテストを実行します。 ただし、Swaggerを使用できるようにするには、いくつかのセットアップ手順が必要です。

まず、gemfileにいくつかのgemを追加する必要があります。

 ... gem 'grape-swagger' gem 'grape-swagger-ui' ...

次に、 bundleを実行して、これらのgemをインストールします。

また、これらをアセットパイプライン( config/initializers/assets.rb )のアセットに追加する必要があります。

 Rails.application.config.assets.precompile += %w( swagger_ui.js ) Rails.application.config.assets.precompile += %w( swagger_ui.css )

最後に、 app/api/api.rbで、Swaggerジェネレーターをマウントする必要があります。

 ... add_swagger_documentation end ...

これで、Swaggerの優れたUIを利用して、 http://localhost:3000/api/swaggerにアクセスするだけでAPIを探索できます。

Swaggerは、APIエンドポイントをわかりやすく探索可能な方法で提供します。 エンドポイントをクリックすると、Swaggerはその操作を一覧表示します。 操作をクリックすると、Swaggerに必須およびオプションのパラメーターとそれらのデータ型が表示されます。

先に進む前に、残りの詳細が1つあります。有効なapi_keyを使用してAPI開発者の使用を制限したため、サーバーがHTTPヘッダーに有効なapi_keyを必要とするため、ブラウザーからAPIエンドポイントに直接アクセスすることはできません。 これは、GoogleChromeプラグインのヘッダーの変更を利用することでGoogleChromeでのテスト目的で実現できます。 このプラグインを使用すると、HTTPヘッダーを編集して、有効なapi_keyを追加できます(データベースシードファイルに含めた12345654321のダミーapi_keyを使用します)。

OK、これでテストの準備が整いました。

pair_programming_sessions APIエンドポイントを呼び出すには、最初にログインする必要があります。次に示すように、データベースシードファイルからの電子メールとパスワードの組み合わせを使用して、Swaggerを介してログインAPIエンドポイントに送信します。

上記のように、そのユーザーに属するトークンが返され、ログインAPIが意図したとおりに正しく機能していることを示します。 これで、そのトークンを使用して、 GET /api/pair_programming_sessions.json操作を正常に実行できます。

示されているように、結果は適切にフォーマットされた階層JSONオブジェクトとして返されます。 プロジェクトには複数のレビューがあり、レビューには複数のコードサンプルがあるため、JSON構造は2つのネストされた1対多の関連付けを反映していることに注意してください。 この方法で構造を返さない場合、APIの呼び出し元は、プロジェクトごとに個別にレビューをリクエストする必要があり、APIエンドポイントにN個のクエリを送信する必要があります。 したがって、この構造を使用して、N+1クエリのパフォーマンスの問題を解決します。

要約

ここに示されているように、APIの包括的な仕様は、実装されたAPIが意図された(そして意図されていない!)ユースケースに適切かつ適切に対処することを保証するのに役立ちます。

このチュートリアルで紹介するAPIの例はかなり基本的なものですが、ここで示したアプローチと手法は、Grapegemを使用した任意の複雑さのより洗練されたAPIの基盤として役立ちます。 このチュートリアルは、GrapeがRailsアプリケーションでのJSONAPIの実装を容易にするのに役立つ便利で柔軟なgemであることを示していると思います。 楽しみ!