はじめに
この記事はRails Tutorialを1周終えたくらいの初学者向けの内容です。
私はRuby on Railsから本格的にプログラミングの勉強をはじめました。そのときに使っていた教材(Progate, Rails Tutorialなど)にはAPIで何かを作るものはなく、APIに触れる機会がありませんでした。
外部APIを使えば、作れるアプリケーションの幅も広がり、プログラミングがより楽しくなるきっかけになるかなと思いましたので、外部APIを使った簡易なアプリケーションのチュートリアルを作ってみます。
本アプリケーション作成時のRuby, Railsのバージョンです。
ruby '2.6.3'
rails '5.2.4'
作るもの
ポケモンのデータを扱えるPoke API
を使って、「ポケモンを検索」、「ゲット(保存)」できるアプリケーションを作ります。
なぜPoke API
かというと、ポケモン剣盾を買うお金がないので、ポケモンのデータで遊んで、気をまぎらわせたいのです!
PokeAPIについて
Poke APIにはポケモン関連のデータが大量にあります。
トップページでAPIを簡単に試せるようになっているので、ぜひ触ってみてください。
なお、今回作るアプリケーションでは取得できるデータのほんの一部しか使いません。
それでは実装していきましょう
1.PokeAPIからポケモンのデータを取得して表示する
rails new をします。
rails new pokeapi_app
アプリケーションの土台ができたら、FaradayというGemを入れます。
-
Faradayのざっくり説明
- HTTPクライアントライブラリ。
- Rubyの標準ライブラリのnet/httpでもHTTPリクエストはできますが、Faradayはより簡単にHTTPリクエストを行うことができる。
gem 'faraday'
bundle install
次に、Pokemonコントローラーを作ります。
rails g controller pokemons new
ルーティングを設定します。(後々の実装を考えてresources :pokemons
にする)
Rails.application.routes.draw do
resources :pokemons
end
Faradayを使ってHTTPリクエストを行います。
Faraday.get "URL"
でリクエストできるので、とても簡単です。
※他にもできることはたくさんあるので、気になる方はドキュメントを参照ください。
Faraday.getで返ってくるレスポンスは、response
をputs
したり、rails consoleを起動してFaraday.get "https://pokeapi.co/api/v2/pokemon/pikachu"
入力すると確認できます。
class PokemonsController < ApplicationController
def new
raw_response = Faraday.get "https://pokeapi.co/api/v2/pokemon/pikachu"
@response = JSON.parse(raw_response.body)
# JSON.parseをすることで、json形式の文字列をRubyオブジェクトに変換することができる。
end
end
APIから取得したデータをpokemons/new.html.erb
に表示します。
<h1>Pokemon</h1>
<div>
<table>
<tr>
<td>No.</td>
<td><%= @response["id"] %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @response["name"] %></td>
</tr>
</table>
<%= image_tag(@response["sprites"]["front_default"], size: "200") %>
</div>
rails serverを起動して、http://localhost:3000/pokemons/new
をブラウザで開いてください。
rails s
ピカチュウが表示されましたね!はい、可愛い〜。
2.ポケモンを検索する
Faraday.get "https://pokeapi.co/api/v2/pokemon/pikachu"
と固定のURLにリクエストをしているので、いまはピカチュウしか表示できません。
いろいろなポケモンをゲットするために、検索できるようにします。
mkdir app/views/shared
touch app/views/shared/_search.html.erb
form_with
を使って、探したいポケモンをURLパラメータで送ります。
<div style="margin-bottom: 50px;">
<%= form_with url: path, method: :get, local: true do |form| %>
<%= form.text_field :search %>
<%= form.submit '検索', name: nil %>
<% end %>
</div>
<h1>Pokemon</h1>
<!-- 追加 -->
<%= render "shared/search", { path: new_pokemon_path } %>
<div>
<% if @response.present? %>
<table>
<tr>
<td>No.</td>
<td><%= @response["id"] %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @response["name"] %></td>
</tr>
</table>
<%= image_tag(@response["sprites"]["front_default"], size: "200") %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
送られてきたパラメータをリクエスト先のURLに入れます。
また、リクエストが失敗した時の処理も追加します。
class PokemonController < ApplicationController
def new
return if params[:search].blank?
raw_response = Faraday.get "https://pokeapi.co/api/v2/pokemon/#{params[:search]}"
if raw_response.status == 200
@response = JSON.parse(raw_response.body)
else
redirect_to new_pokemon_path, notice: "#{raw_response.status}エラー!"
end
end
end
フラッシュメッセージは共通で使うことが多くなるのでerb:layouts/application.html.erb
で表示します。
<body>
<% if flash[:notice] %>
<div><%= flash[:notice] %></div>
<% end %>
<%= yield %>
</body>
これでポケモンの検索ができるようになりました。
3.ポケモンを保存する
ポケモンのデータを保存するためにモデルとテーブルを作成します。
rails g model pokemon order:integer name:string image_url:string
生成されたmigrationファイルを開いて、各カラムにNOT NULL制約を追加して、rails db:migrate
をします。
class CreatePokemons < ActiveRecord::Migration[5.2]
def change
create_table :pokemons do |t|
t.integer :order, null: false
t.string :name, null: false
t.string :image_url, null: false
t.timestamps
end
end
end
rails db:migrate
次に空の値が保存されないように、Pokemonモデルにバリデーションをかけます。
class Pokemon < ApplicationRecord
validates :order, presence: true
validates :name, presence: true
validates :image_url, presence: true
end
コントローラーのnewアクションに変更を加えていきます。
class PokemonController < ApplicationController
def new
return if params[:search].blank?
raw_response = Faraday.get "https://pokeapi.co/api/v2/pokemon/#{params[:search]}"
if raw_response.status == 200
response = JSON.parse(raw_response.body)
# Pokemonインスタンスを生成するようにします。
@pokemon = Pokemon.new(order: response["id"], name: response["name"], image_url: response["sprites"]["front_default"])
else
redirect_to new_pokemon_path, notice: "#{raw_response.status}エラー!"
end
end
end
pokemons/new.html.erb
で、form_with
を使って、検索したポケモンを保存できるようにします。
<h1>Pokemon</h1>
<%= render "shared/search", { path: new_pokemon_path } %>
<div>
<% if @pokemon.present? %>
<table>
<tr>
<td>No.</td>
<td><%= @pokemon.order %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @pokemon.name %></td>
</tr>
</table>
<%= image_tag(@pokemon.image_url, size: "200") %>
<%= form_with model: @pokemon, local: true do |f| %>
<%= f.hidden_field :order %>
<%= f.hidden_field :name %>
<%= f.hidden_field :image_url %>
<%= f.submit 'ゲット', name: nil %>
<% end %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
コントローラーのcreateアクションを追加。送られてきたパラメータを処理して、レコードに保存できるようにします。
def create
@pokemon = Pokemon.new(pokemon_params)
if @pokemon.save
redirect_to pokemons_path, notice: "「#{@pokemon.name}」をゲットしました。"
else
render :new
end
end
private
def pokemon_params
params.require(:pokemon).permit(:order, :name, :image_url)
end
pokemon/index.html.erb
にレコードに保存されているポケモンを表示できるようにします。
def index
@pokemons = Pokemon.all
end
touch app/views/pokemons/index.html.erb
<h1>Pokemon</h1>
<%= render "shared/search", { path: new_pokemon_path } %>
<div>
<% if @pokemons.present? %>
<% @pokemons.each do |pokemon| %>
<div style="display: inline-block;">
No. <%= pokemon.order %>,
Name: <%= pokemon.name %>
<div>
<%= image_tag(pokemon.image_url, size: "200") %>
</div>
</div>
<% end %>
<% end %>
</div>
また、バリデーションエラーが起きた場合に、エラーメッセージを表示できるようにします。
<h1>Pokemon</h1>
<%= render "shared/search", { path: new_pokemon_path } %>
<div>
<% if @pokemon.present? %>
<!-- 追加 -->
<%= render "shared/error_messages", { object: @pokemon } %>
<table>
<tr>
<td>No.</td>
<td><%= @pokemon.order %></td>
</tr>
<tr>
<td>Name:</td>
<td><%= @pokemon.name %></td>
</tr>
</table>
<%= image_tag(@pokemon.image_url, size: "200") %>
<%= form_with model: @pokemon, local: true do |f| %>
<%= f.hidden_field :order %>
<%= f.hidden_field :name %>
<%= f.hidden_field :image_url %>
<%= f.submit 'ゲット', name: nil %>
<% end %>
<% else %>
<p>検索してね!</p>
<% end %>
</div>
touch app/shared/_error_messages.html.erb
<% if object.errors.present? %>
<ul>
<% object.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
とりあえず完成
デザイン無しなので、見栄えはないですね…気になる方はBootstrapなど使っていただければと思います。
今後は元気があれば、CRUDの実装、他のデータ(itemなど)の取り込み、Api Clientの作成、例外処理を丁寧にしたいです。(たくさんある…)
これを読んだ初学者の方々がプログラミング学習をさらに楽しめるきっかけになれば幸いです。