アプリケーション開発をするにあたり、外部サービスとAPI連携を行う場合があります。
前回はHTTP通信を行うRubyの標準ライブラリであるnet/httpを利用した外部APIとの連携方法について紹介しました。
今回は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)やってます。フォローしてもらえるとうれしいです!