【Ruby】faradayを利用した外部API連携クラスの作成手順

Ruby

アプリケーション開発をするにあたり、外部サービスとAPI連携を行う場合があります。

前回はHTTP通信を行うRubyの標準ライブラリであるnet/httpを利用した外部APIとの連携方法について紹介しました。

【Ruby】net/httpを利用した外部API連携クラスの作成手順

2020年11月8日

今回はHTTP通信のクライアントライブラリとして人気の高いfaradayを利用した外部APIとの連携方法について紹介します。

今回作成するもの

今回は外部サービスの例としてQiitaを利用します。

今回のゴール
  • QiitaClientAPIというQiita APIと連携するクラスを作成する
  • get_itemsというクラスメソッドを利用することでQiitaの記事一覧が取得できるようにする
  • API連携でエラーが発生した場合は、エラーレスポンスの内容を例外として出力する
  • localhost:3000/qiita_itemsにアクセスするとQiitaClientAPI.get_itemsの結果が取得できる

なお、今回紹介するサンプルはrails 6.0.3.4のAPIモードで作成しています。

下準備

実装をするにあたり、Qiita APIと連携するための準備をします。

アクセストークンの取得

Qiita APIの認証認可に必要なアクセストークンを取得します。

アクセストークンはユーザの管理画面で取得できます。

なおQiita APIのGETリクエストではアクセストークンは不要なため1、今回紹介する記事一覧取得API(/api/v2/items)のみを実装したい場合はこの作業は不要です。

アクセストークンをRailsアプリケーションに登録する

今回はRails 6のcredentialsにトークンを保存しました。

### config/credentials/development.yml.encの編集
$ export EDITOR="vim"
$ rails credentials:edit -e development
→ このタイミングでconfig/credentials/development.keyとconfig/credentials/development.yml.encが作成される

config/credentials/development.yml.enc

qiita:
  # 取得したトークンをセットする
  token: xxxxxxx
$ rails c

### 取得したトークンが表示されればOK
> Rails.application.credentials.qiita[:token]
=> 'xxxxxxx'

ルーティングの追加

検証で利用するlocalhost:3000/qiita_itemsのエンドポイントを作成します。

config/routes.rb

Rails.application.routes.draw do
  resources :qiita_items, only: %i(index)
end

lib配下のクラスを読み込むようにする

今回はQiita APIと連携するクラスをlib配下に作成します。
lib配下のクラスが読み込まれるようにするため以下のように修正します。

config/application.rb

module RailsApiClient
  class Application < Rails::Application
    # 以下を追加
    config.paths.add 'lib', eager_load: true
  end
end

gemの追加

必要になるgemを追加します。

Gemfile

gem 'faraday'
gem 'faraday_middleware'

faradayを利用した外部APIとの連携方法

ここからは順を追って実装について紹介していきます。

シンプルな方法

Qiita APIと連携するクラスを作成せず、ロジック2を直接記述するパターンです。

app/controllers/qiita_items_controller.rb

class QiitaItemsController < ApplicationController
  def index
    response = Faraday.get('https://qiita.com/api/v2/items') do |request|
      request.headers['Authorization'] = "Bearer #{Rails.application.credentials.qiita[:token]}"
    end
    response_json = JSON.parse(response.body)

    # レスポンスを簡略化するため、titleプロパティのみ返すようにしている
    render json: response_json.map {|item| item.slice('title') }
  end
end

実行結果は以下のようになります。

$ curl 'http://localhost:3000/qiita_items'

[{"title":"highlight.jsを動的に使ってみた - CodePen"},{"title":"飛び飛びセル順次コピペ"},{"title":"テキスト入力中の点滅するカーソルに好きなCSSを当てる方法"},{"title":"jQueryいろいろ(wrapAll, MutationObserverなど)"},{"title":"高機能なSQL開発ツール「A5:SQL Mk-2」をUbuntuで使う"},{"title":"Amazon Aurora カスタムエンドポイントの検証と考察"},...
(略)
...
]

Qiita APIと連携する専用クラスを作成する

QiitaApiClient.get_itemsを呼ぶことでデータが取得できるようにします。
lib配下にQiitaApiClientクラスを作成し、API連携のロジックを移行します。

ソースコードは以下のようになります。

lib/qiita_api_client.rb

class QiitaApiClient
  class << self
    def connection
      Faraday::Connection.new('https://qiita.com')
    end

    def get_items
      response = connection.get do |request|
        request.url '/api/v2/items'
        request.headers['Authorization'] = "Bearer #{Rails.application.credentials.qiita[:token]}"
      end
      JSON.parse(response.body)
    end
  end
end

HTTP通信を行う際に利用するFaraday::ConnectionオブジェクトにはHTTP通信の設定を追加できます。
外部APIとの連携で必要になるベースの設定についてはFaraday::Connectionオブジェクトを作成する際にセットしておくとよいでしょう。

lib/qiita_api_client.rb

class QiitaApiClient
  class << self
    def connection
      Faraday::Connection.new('https://qiita.com') do |builder|
        builder.authorization :Bearer, "#{Rails.application.credentials.qiita[:token]}"
        builder.request  :url_encoded # リクエストパラメータをURLエンコードする
        builder.response :logger # レスポンスを標準出力する
        builder.adapter Faraday.default_adapter # アダプターの選択。デフォルトはNet::HTTP
      end
    end

    def get_items
      response = connection.get(
        '/api/v2/items'
      )
      JSON.parse(response.body)
    end
  end
end

API連携のロジックをQiitaApiClientに移行したので呼び出す側は以下のようになります。

app/controllers/qiita_items_controller.rb

class QiitaItemsController < ApplicationController
  def index
    response_json = QiitaApiClient.get_items
    render json: response_json.map {|item| item.slice('title') }
  end
end

Faradayミドルウェアを活用する

faraday_middlewareをインストールすることでFaraday::Connectionオブジェクトに追加できる設定の内容が増えます。

たとえばfaraday_middlewareを利用することで、レスポンスボディをJSONパースする設定を追加できます。(以下でいうbuilder.response :jsonの項目です。)
これにより、外部APIから受け取ったレスポンスボディのJSON.parse()が不要になります。

lib/qiita_api_client.rb

class QiitaApiClient
  class << self
    def connection
      Faraday::Connection.new('https://qiita.com') do |builder|
        builder.authorization :Bearer, "#{Rails.application.credentials.qiita[:token]}"
        builder.request  :url_encoded # リクエストパラメータを URL エンコードする
        builder.response :logger # レスポンスを標準出力する
        builder.adapter Faraday.default_adapter # アダプターの選択。デフォルトはNet::HTTP
        builder.response :json, :content_type => "application/json" # レスポンスボディをJSONパースする
      end
    end

    def get_items
      response = connection.get(
        '/api/v2/items'
      )
      response.body
    end
  end
end

Faradayミドルウェアの種類についてはFaradayの使い方 59のレシピ#第4章-Faradayミドルウェア一覧のまとめが参考になります。

参考: 外部APIとの連携エラー時の例外処理を追加する

Qiita APIのレスポンスがエラーだった場合、APIのエラーの内容がわかるよう例外処理を追加します。
今回は「エラー時のレスポンスの内容」と「エラーコード」を出力するカスタム例外を作成しました。

lib/qiita_api_client.rb

class QiitaApiClient
  class HTTPError < StandardError
    def initialize(response)
      super "status=#{response.status} body=#{response.body}"
    end
  end
  class << self
    def connection
      Faraday::Connection.new('https://qiita.com') do |builder|
        builder.authorization :Bearer, "#{Rails.application.credentials.qiita[:token]}"
        builder.request  :url_encoded # リクエストパラメータを URL エンコードする
        builder.response :logger # レスポンスを標準出力する
        builder.adapter Faraday.default_adapter # アダプターの選択。デフォルトはNet::HTTP
        builder.response :json, :content_type => "application/json" # レスポンスボディをJSONパースする
      end
    end

    def get_items
      response = connection.get(
        '/api/v2/items'
      )
      if response.success?
        response.body
      else
        raise QiitaApiClient::HTTPError.new(response)
      end
    end
  end
end

たとえば、不正なトークンをセットしてリクエストを送った場合、以下のような例外が発生します。

$ curl 'http://localhost:3000/qiita_items'

QiitaApiClient::HTTPError (code=401 body={"message":"Unauthorized","type":"unauthorized"}):

まとめ

以上でfaradayを利用した外部API連携の方法の紹介を終わります。

外部APIと連携する専用のクラスを作成することでQiitaApiClient.get_itemsのような形で外部サービスのデータを取得できます。
今回はシンプルなGETメソッドのみを実装しましたが、同様の手順でクエリやリクエストボディのついたメソッドの実装もできます。

今回のサンプルはあくまで一例ですので、よりよい方法があれば教えていただけるとありがたいです。

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考