【本コラムは、5分で読めて、15分くらいでお試しいただけます】
piacereです、ご覧いただいてありがとございます
前回までは、シンプルなPhoenixのサーバサイドレンダリング(SSR)により、Web上にDBデータや外部APIデータを表示してきました
今回からは、ElixirサーバサイドでSPAを開発できる「LiveView」を使ったフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、よりElixir/Phoenixらしさを実感できるWeb開発へとステージアップします
なお、本コラムと全く同じ内容のVue.js版とReact版もあります
Vue.js版
https://qiita.com/piacerex/items/50d847170291c41fef64
React版
https://qiita.com/piacerex/items/27052ac628ec394c7560
ちなみにVue.jsやReactと同じ構成にするため、LiveViewでもAPIを用意して操作を行っていますが、本来LiveViewはAPI無しでSPAを開発できます … これは、シリーズのこの先にある第9回で実践します
■「ExcelからElixir入門」シリーズの目次
①データ並替え/絞り込み
|> ②データ列抽出、Web表示
|> ③WebにDBデータ表示
|> ④Webに外部APIデータ表示
|> ⑤Webにグラフ表示
|> ⑥SPAからPhoenix製APIを呼び出す(表示編)【LiveView版】
|> ⑦SPAからPhoenix製APIを呼び出す(更新編)【LiveView版】
|> ⑧Gigalixirに本番リリース
|> ⑨ElixirサーバサイドのみでReactと同じSPA/リアルタイムUIが作れる「LiveView」
|> ⑩ElixirサーバサイドSPAをスマホで見るためにGigalixirリリース
|> ⑪Gigalixir上のLiveViewアプリに独自ドメイン名を付与して正式なアプリ公開
|> ⑫Elixir/PhoenixのCRUD Webアプリをリリース
Elixir Advent Calendar: 言語カテゴリ1位 & 全カテゴリ2位!
例年を遥かに超える盛り上がりを見せ、堂々のトップ獲得ッ!
https://qiita.com/advent-calendar/2022/elixir
https://qiita.com/advent-calendar/2022/ranking/feedbacks
https://qiita.com/advent-calendar/2022/ranking/feedbacks/categories/programming_languages
LiveView:リアルタイムSPAをサーバサイドのみで開発可
Elixirサーバサイドのみで書かれたコードにも関わらず、Vue.jsやReactのようなリアルタイムSPAが構築できるのが「LiveView」です
Vue.jsやReactとほぼ変わらない反応速度やフィーリングで、しかもフロントとサーバの書き分けが一切要らず、フロント側の入力内容やデータ状態をサーバサイドで一元管理でき、しかもAPI構築要らず … なんだか魔法のような体験ができるLiveViewをお楽しみください
PhoenixでScaffoldを使わずにデータ一覧APIを作る
通常、PhoenixでAPIを作る場合、mix phx.gen.json
によるDB CRUD付きREST API、いわゆる「Scaffold」で生成するのが一般的ですが、別に使わなくてもPhoenixでのAPI作成はカンタンですので、早速作ってみましょう
まず 「Sqlex」をインストールすることで、DbMnesia
/Db
モジュールを導入します(このシリーズの第3回まではこの2つの実装も扱っていましたが、入門編を超えた範囲になるのでOSS化しました)
mix.exsの def deps do
配下の :phoenix
の直上に追記します
defmodule Basic.Mixfile do
use Mix.Project
…
defp deps do
[
{:req, "~> 0.3"},
+ {:sqlex, "~> 0.1.0"},
{:phoenix, "~> 1.6.15"},
…
]
end
…
PhoenixをCtrl+C2回で止めて、ライブラリを取得(要ネット接続)し、Phoenixを起動します
mix deps.get
iex -S mix phx.server
次に、controllers
フォルダ配下に member_controller.ex
というファイルを追加し、下記のように index
で members
テーブルの一覧を返すようにします
Phoenixは、マップやリスト、リストマップを json
に渡すと、自動的にJSON化してくれるのです
前回までで、DB(Mnesia)を使うPhoenix PJを作りましたが、そこに下記ファイルを追加しましょう
defmodule BasicWeb.MemberController do
use BasicWeb, :controller
def index(conn, _p) do
conn
|> json(
"select * from members"
|> Db.query
|> Db.columns_rows
|> Enum.sort(fn current, next -> current["id"] < next["id"] end)
)
end
end
次に、ルーティングにAPI用エントリーとして、CRUD全部(GET/POST/PUT/DELETE)のエンドポイントを一括生成する resources "/", MemberController
を含む scope "/members", ~
ブロックを追加します
defmodule BasicWeb.Router do
use BasicWeb, :router
…
+ scope "/members", BasicWeb do
+ pipe_through :api
+
+ resources "/", MemberController
+ end
…
Phoenixを起動してください
iex -S mix phx.server
http://localhost:4000/members
にブラウザでアクセスすると、下記のように返ってくれば成功です
データ一覧APIをREST APIクライアントで直接叩いて確認
REST APIの確認には、「REST APIクライアント」が便利ですが、REST APIクライアントは下記が代表的です
Chrome向け「Postman」
Firefox向け「RESTClient」
VScode向け「REST Client」
ここではPostmanで説明しますが、下記のように「Headers」に Content-Type
を application/json
で設定してから、URLを設定し、「Send」ボタンをクリックしてください
このように、PhoenixでAPIを作成するのは、とてもカンタンです
SPAからデータ一覧APIを呼んだ後、表示する
LiveViewと、ElixirからREST APIを呼ぶためのライブラリ「Req」を使って、先ほど作ったAPIを呼ぶSPA(Single Page Application)を作ってみましょう
Reqは、mix.exsの def deps do
配下の :phoenix
の直上に追記します
defmodule Basic.MixProject do
…
defp deps do
[
{:req, "~> 0.3"},
{:phoenix, "~> 1.6.15"},
…
ライブラリをインストールして、Phoenixを起動します(Phoenixが起動しっ放しの場合は、Ctrl+Cを2回で止めて下記コマンドを入れてください)
mix deps.get
iex -S mix phx.server
次にLiveViewで、まずLiveView用フォルダを作ります
mkdir lib/basic_web/live
LiveViewは、LiveViewモジュールとHTMLの2ファイルに分かれます
LiveViewモジュールは、初期表示時に実行される mount
ハンドラで、ReqによるAPI呼出を行い、members
にAPIで取得したデータを格納し、それがHTMLに渡されます
defmodule BasicWeb.IndexLive do
use Phoenix.LiveView
use BasicWeb, :live_view
@url "http://localhost:4000/members"
@headers ["Content-Type": "application/json"]
def get() do
Req.get!(@url, headers: @headers).body
|> Enum.map(& &1 |> Map.new(fn {k, v} -> {String.to_atom(k), v} end))
end
def mount(_params, _session, socket) do
{:ok, assign(socket, [members: get()])}
end
end
HTMLでは、LiveViewモジュールの mount
から渡された members
を for
で1件ずつ取り出し、各フィールドを <%= n.【フィールド名】%>
で取り出して表示します
<h1>Members</h1>
<table>
<tr>
<th>id</th>
<th>name</th>
<th>age</th>
<th>team</th>
<th>position</th>
</tr>
<%= for n <- @members do %>
<tr>
<td><%= n.id %></td>
<td><%= n.name %></td>
<td><%= n.age %></td>
<td><%= n.team %></td>
<td><%= n.position %></td>
</tr>
<% end %>
</table>
最後にRouterを PageController, :index
のgetから、IndexLive
のliveに変更します
defmodule BasicWeb.Router do
use BasicWeb, :router
…
scope "/", BasicWeb do
pipe_through :browser
+ live "/", IndexLive
- get "/", PageController, :index
end
…
http://localhost:4000
にブラウザでアクセスすると、APIで取得したデータがテーブル表示されます
見た目が、SSR版と変わらないので、ちゃんとSPAとして動いているか分からないかも知れませんが、Phoenixを起動したコンソールで確認すると、下記の通り、API呼出である MemberController.index
の実行が、ページ呼出である PageController.index
の実行の後に行われていることが確認できます
データ追加APIを実装する
APIによるデータ参照はできたので、次は、データ追加APIを実装してみます
MemberController
モジュールに create
を追加し、members
テーブルへのinsert文を Db.query
に渡し、その後、ステータスコードとして 201 Created
を返却します
defmodule BasicWeb.MemberController do
use BasicWeb, :controller
def index(conn, _p) do
conn
|> json(
"select * from members"
|> Db.query
|> Db.columns_rows
|> Enum.sort(fn current, next -> current["id"] < next["id"] end)
)
end
+ def create(conn, p) do
+ "insert into members values('#{p["name"]}', #{p["age"]}, '#{p["team"]}', '#{p["position"]}')"
+ |> Db.query
+ send_resp(conn, :created, "")
+ end
end
データ追加APIから直接データを追加し、Web上で確認
では、APIでデータ追加してみましょう
「GET」を「POST」に変更し、「Body」を「raw」で設定した後、下記内容をbodyに入力して、藤井名人をチームにjoinしてもらい、大幅戦力増強しましょうw
{
"name": "藤井 聡太",
"age": 20,
"team": "杉本昌隆八段門下",
"position": "竜王"
}
「Send」ボタンをクリックすると、「Status」に 201 Created
が返ってきます
データ一覧APIを叩くと、藤井名人が追加されているのが確認できます
ブラウザをリロードすると、藤井名人がチームにjoinしたことが確認できました
【参考】本コラムの検証環境
本コラムは、以下環境で検証しています(恐らくUbuntu実機やMacでも動きます)
-
Windows 10
- 実機+Elixir 1.14.2 (Erlang/OTP 25)
- Phoenix 1.6.15
- LiveView 0.17.12
- WSL2/Ubuntu 20.04+Elixir 1.14.2 (Erlang/OTP 25) ※最新版のインストール手順はコチラ
- Phoenix 1.6.15
- LiveView 0.17.12
- Docker/Debian 11.6+Elixir 1.14.2 (Erlang/OTP 25)
- Phoenix 1.6.15
- LiveView 0.17.12
- 実機+Elixir 1.14.2 (Erlang/OTP 25)
-
Windows11
- WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
- Phoenix 1.6.15
- LiveView 0.17.12
- WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
終わり
今回は、Reactによるフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、APIで取得したデータをWeb表示してみました