6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ExcelからElixir入門⑥:SPAからPhoenix製APIを呼び出す(表示編)【LiveView版】

Last updated at Posted at 2023-01-09

【本コラムは、5分で読めて、15分くらいでお試しいただけます】
piacereです、ご覧いただいてありがとございます :bow:

前回までは、シンプルな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アプリをリリース

:ocean::ocean::ocean: Elixir Advent Calendar: 言語カテゴリ1位 & 全カテゴリ2位! :ocean::ocean::ocean:

例年を遥かに超える盛り上がりを見せ、堂々のトップ獲得ッ! :qiita: :tada: :confetti_ball:

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
image.png

LiveView:リアルタイムSPAをサーバサイドのみで開発可

Elixirサーバサイドのみで書かれたコードにも関わらず、Vue.jsやReactのようなリアルタイムSPAが構築できるのが「LiveView」です

Vue.jsやReactとほぼ変わらない反応速度やフィーリングで、しかもフロントとサーバの書き分けが一切要らず、フロント側の入力内容やデータ状態をサーバサイドで一元管理でき、しかもAPI構築要らず … なんだか魔法のような体験ができるLiveViewをお楽しみください :wink:

PhoenixでScaffoldを使わずにデータ一覧APIを作る

通常、PhoenixでAPIを作る場合、mix phx.gen.json によるDB CRUD付きREST API、いわゆる「Scaffold」で生成するのが一般的ですが、別に使わなくてもPhoenixでのAPI作成はカンタンですので、早速作ってみましょう

まず 「Sqlex」をインストールすることで、DbMnesiaDb モジュールを導入します(このシリーズの第3回まではこの2つの実装も扱っていましたが、入門編を超えた範囲になるのでOSS化しました)

mix.exsの def deps do 配下の :phoenix の直上に追記します

mix.exs
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 というファイルを追加し、下記のように indexmembers テーブルの一覧を返すようにします

Phoenixは、マップやリスト、リストマップを json に渡すと、自動的にJSON化してくれるのです

前回までで、DB(Mnesia)を使うPhoenix PJを作りましたが、そこに下記ファイルを追加しましょう

lib/basic_web/controllers/member_controller.ex
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", ~ ブロックを追加します

lib/vue_sample_web/router.ex
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 にブラウザでアクセスすると、下記のように返ってくれば成功です
image.png

データ一覧APIをREST APIクライアントで直接叩いて確認

REST APIの確認には、「REST APIクライアント」が便利ですが、REST APIクライアントは下記が代表的です

Chrome向け「Postman」

Firefox向け「RESTClient」

VScode向け「REST Client」

ここではPostmanで説明しますが、下記のように「Headers」に Content-Typeapplication/json で設定してから、URLを設定し、「Send」ボタンをクリックしてください
image.png

すると、下記のように返ってきます
image.png

このように、PhoenixでAPIを作成するのは、とてもカンタンです

SPAからデータ一覧APIを呼んだ後、表示する

LiveViewと、ElixirからREST APIを呼ぶためのライブラリ「Req」を使って、先ほど作ったAPIを呼ぶSPA(Single Page Application)を作ってみましょう

Reqは、mix.exsの def deps do 配下の :phoenix の直上に追記します

mix.exs
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に渡されます

lib/basic_web/live/index_live.ex
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 から渡された membersfor で1件ずつ取り出し、各フィールドを <%= n.【フィールド名】%> で取り出して表示します

lib/basic_web/live/index_live.html.heex
<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に変更します

lib/basic_web/router.ex
defmodule BasicWeb.Router do
  use BasicWeb, :router

  scope "/", BasicWeb do
    pipe_through :browser

+   live "/", IndexLive
-   get "/", PageController, :index
  end

http://localhost:4000 にブラウザでアクセスすると、APIで取得したデータがテーブル表示されます
image.png

見た目が、SSR版と変わらないので、ちゃんとSPAとして動いているか分からないかも知れませんが、Phoenixを起動したコンソールで確認すると、下記の通り、API呼出である MemberController.index の実行が、ページ呼出である PageController.index の実行の後に行われていることが確認できます
image.png

データ追加APIを実装する

APIによるデータ参照はできたので、次は、データ追加APIを実装してみます

MemberController モジュールに create を追加し、members テーブルへのinsert文を Db.query に渡し、その後、ステータスコードとして 201 Created を返却します

lib/basic_web/controllers/member_controller.ex
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": "竜王"
}

入力後は、こんな感じになります
image.png

「Send」ボタンをクリックすると、「Status」に 201 Created が返ってきます
image.png

データ一覧APIを叩くと、藤井名人が追加されているのが確認できます
image.png

ブラウザをリロードすると、藤井名人がチームにjoinしたことが確認できました :wink:
image.png

【参考】本コラムの検証環境

本コラムは、以下環境で検証しています(恐らく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
  • Windows11

    • WSL2/Ubuntu 22.04+Docker Compose+Elixir 1.13.4 (Erlang/OTP 25)
      • Phoenix 1.6.15
      • LiveView 0.17.12

終わり

今回は、Reactによるフロントサイドと、Phoenixによる内部APIを組み合わせを用いて、APIで取得したデータをWeb表示してみました

次回は、画面入力と、APIによるデータ更新 を行ってみます

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?