(この記事は、「サーバーサイドプログラミング Advent Calendar 2017」の2日目です)
fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます
「季節外れのfukuoka.ex Advent Calendar」も、先週で終わりましたので、今回からは、小ネタ中心にスイッチします
今回は、「mix phx.gen.json ~」で自動生成したPhoenixのAPIが、クライアントから、どのような形でリクエストを受け付け、クライアントにレスポンスを返すのかのコードの流れを解析していきます
この解析を元に、DBテーブルと紐付くCRUDとして生成されたAPIを、DBテーブルとは関係無いAPIへと改造する方法を、次回以降のどこかで実装しようと思います
お礼:「いいね」638件いただき、毎週1/2/3フィニッシュ + 87回ランクイン達成
4/27から6/13までの48日間に渡り、毎日お届けした、「季節外れのfukuoka.ex Elixir Advent Calendar」と「季節外れのfukuoka.ex(その2) Elixir Advent Calender」ですが、沢山の応援をいただき、誠にありがとうございます
Elixirウィークリーランキングにて、開始の週から、終了の週までの8週間、毎週連続で1/2/3フィニッシュを飾りました
また、Qiitaトップページランキングには13回入賞し、各種ランキングにてトータル87回ものランクインを達成しました
これらは一重に、皆さまからいただいた「いいね」によるもので、fukuoka.exアドバイザーズ/キャスト一同、感謝しています
Phoenix自動生成APIの作り方
Phoenixで自動生成するAPIの作り方は、「Elixir入門「第3回:Phoenix 1.3で高速webアプリ & REST APIアプリをサクッと書いてみる」のP22以降をご覧ください
なお、API自動生成以前に、Elixir/Phoenix/DBが、未だインストールされていない方は、「Excelから関数型言語マスター1回目:行の「並べ替え」と「絞り込み」」を第3回までなぞってください
以降では、上記手順にしたがって、以下コマンドで、Phoenix PJ作成/DBテーブル作成/API自動生成/Phoenix起動を行っている前提でお話します
mix phx.new sample_analytics --no-brunch --database=mysql
mix ecto.create
mix phx.gen.json Api Sample sample title:string body:string
【lib/sample_analytics_web/router.exにルート追加】
mix ecto.migrate
iex -S mix phx.server
APIのリクエストはコントローラで受け付ける
PhoenixのAPIは、下記コントローラモジュールの各関数で受け付けます
index()はGETで取得できるリスト一覧、create()はPOSTで行うデータ追加、show()はID指定のGETで取得できる各データ、update()はPUTで行うデータ更新、deleteはDELETEで行うデータ削除、といった感じに1対1で対応しています
defmodule SampleAnalyticsWeb.SampleController do
use SampleAnalyticsWeb, :controller
alias SampleAnalytics.Api
alias SampleAnalytics.Api.Sample
action_fallback SampleAnalyticsWeb.FallbackController
def index(conn, _params) do
sample = Api.list_sample()
render(conn, "index.json", sample: sample)
end
def create(conn, %{"sample" => sample_params}) do
with {:ok, %Sample{} = sample} <- Api.create_sample(sample_params) do
conn
|> put_status(:created)
|> put_resp_header("location", sample_path(conn, :show, sample))
|> render("show.json", sample: sample)
end
end
def show(conn, %{"id" => id}) do
sample = Api.get_sample!(id)
render(conn, "show.json", sample: sample)
end
def update(conn, %{"id" => id, "sample" => sample_params}) do
sample = Api.get_sample!(id)
with {:ok, %Sample{} = sample} <- Api.update_sample(sample, sample_params) do
render(conn, "show.json", sample: sample)
end
end
def delete(conn, %{"id" => id}) do
sample = Api.get_sample!(id)
with {:ok, %Sample{}} <- Api.delete_sample(sample) do
send_resp(conn, :no_content, "")
end
end
end
index()は、DBデータ一覧をDBアクセッサであるApi.list_sample()経由で取得し、後述するビューモジュールのredner()でJSONオブジェクトリストとして返却します
show()は、引数で指定されたID指定でパターンマッチし、DBデータ1件をApi.get_sample!()経由で取得し、ビューモジュールのredner()にてJSONオブジェクトで返却します
create()は、リクエストで投げられた下記形式のJSONオブジェクトをパターンマッチし、Api.create_sample()にてバリデーション付insertを行い、insertが成功したら、show()同様に登録したデータ1件を、ビューモジュールのredner()にてJSONオブジェクトで返却します
DBデータ一覧をDBアクセッサであるApi.list_sample()経由で取得し、後述するビューモジュールのredner()でJSONオブジェクトリストとして返却します
なお、mixコマンドを使うと、下記のように、各HTTPメソッドと、コントローラ+関数の関係が表示できます
mix phx.routes
page_path GET / SampleAnalyticsWeb.PageController :index
sample_path GET /sample SampleAnalyticsWeb.SampleController :index
sample_path GET /sample/:id SampleAnalyticsWeb.SampleController :show
sample_path POST /sample SampleAnalyticsWeb.SampleController :create
sample_path PATCH /sample/:id SampleAnalyticsWeb.SampleController :update
PUT /sample/:id SampleAnalyticsWeb.SampleController :update
sample_path DELETE /sample/:id SampleAnalyticsWeb.SampleController :delete
APIのレスポンスJSON返却はビューで行う
PhoenixのAPIは、下記ビューモジュールのrender()で行います
コントローラのindex()は、render("index.json", ~)をパターンマッチして呼び出し、index()以外の全ては、render("show.json", ~)をパターンマッチして呼び出します
なお、"index.json"や"show.json"は、ファイル名では無く、コントローラからのパターンマッチ呼び分けのための文字列です
defmodule SampleAnalyticsWeb.SampleView do
use SampleAnalyticsWeb, :view
alias SampleAnalyticsWeb.SampleView
def render("index.json", %{sample: sample}) do
%{data: render_many(sample, SampleView, "sample.json")}
end
def render("show.json", %{sample: sample}) do
%{data: render_one(sample, SampleView, "sample.json")}
end
def render("sample.json", %{sample: sample}) do
%{id: sample.id,
title: sample.title,
body: sample.body}
end
end
index()から呼ばれた際に使われるrender_many()は、DBデータ全件のマップリストが、JSONのオブジェクトリストとして返却されます
index()以外から呼ばれた際に使われるrender_one()は、DBデータ1件のマップを、JSONオブジェクトで返却します
そのいずれも、内部では、ビュー内のrender("sample.json", ~)を呼び出しており、render_many()/render_one()のいずれにも、引数で渡されたデータから、1データ分のマップを作成し、render_many()/render_one()に戻しています
render_many()は、DBデータ一覧をEnum.map()することで、複数回、render("sample.json", ~)を呼び出します
終わり
今回は、Phoenixで自動生成したAPIのコードの流れを解析しました
PhoenixのAPIにおける、リクエスト受付と、JSONレスポンス返却の流れが、理解できましたでしょうか?
この内容を理解すれば、DBテーブルと紐付くCRUDとして生成されたAPIを、DBテーブルとは関係無いAPIへと改造する方法も、何となく想像が付くのでは無いかと思います
お知らせ:Elixir MeetUpを6月末に開催します
「fukuoka.ex#11:DB/データサイエンスにコネクトするElixir」を6/22(金)19時に開催します
fukuoka.exの発足から、ちょうど1周年となる、記念的なイベントでもあるため、このコラムを気に入っていただいた方と、ぜひ一緒に盛り上がりたいです
福岡近辺にお住まいの方であれば、遊びに来てください
大人気により、一度は満席となりましたが、増枠しましたので、下記URLよりご参加ください
https://fukuokaex.connpass.com/event/87241
特別ゲストは、Erlang/Elixirの両面で世界的に有名な「力武 健次さん」と、北九州の飯塚市で「e-ZUKA Tech Night」を6年間主催し続けるハウインターナショナルの「谷口 耕平さん」のお二人と、実に豪華なイベントです
私は、今回のシリーズを踏まえた、「1家に1台、パーソナルなデータ分析AIを全員が持つ2020年を作る」というタイトルで、Elixirによる、ブラウザUI上からサクっとデータ分析プラットフォームを披露するLTをお届けします