Phoenix 1.4正式版② DB+API編

[fukuoka.ex](https://fukuokaex.connpass.com/)代表のpiacereです
ご覧いただいて、ありがとうございます:bow:

[前回](https://qiita.com/piacere_ex/items/7426cff6bd64ba02d0a5)、インストールした、ElixirのWebフレームワーク「Phoenix」の新バージョン1.4で、DBと紐付くAPIを作ってみます

なお、Phoenix 1.4正式版のEctoは、メジャーバージョンアップした3.0になっているので、その試しにもなっています

この連載の、前回までの記事は、以下になります
　|> [<b>Phoenix 1.4正式版① インストール編</b>](https://qiita.com/piacere_ex/items/7426cff6bd64ba02d0a5)

あと、参考までに、rc版を試したときのコラムは、以下になります
　|> [<b>Phoenix 1.4rcを試す① アップデート紹介編</b>](https://qiita.com/piacere_ex/items/34a56a418a99701a0ac1)
　|> [<b>Phoenix 1.4rcを試す② インストール編（不具合時の復旧もあるよ）</b>](https://qiita.com/piacere_ex/items/f4fb3fecb359e798ef01)
　|> [<b>Phoenix 1.4rcを試す③ ローカルSSL開発編（Gigalixirのオマケ付）</b>](https://qiita.com/piacere_ex/items/28f2f655241d142980d8)
　|> [<b>Phoenix 1.4rcを試す④ webpack編</b>](https://qiita.com/piacere_ex/items/86c133c4ed97504ab991)

内容が、面白かったり、気に入ったら、「いいね」よろしくお願いします :wink:

# 事前準備：PostgreSQLをインストール

今回、DBを利用するため、PostgreSQLをあらかじめインストールする必要があります

[以前、書いたコラム](https://qiita.com/piacere_ex/items/a7558adc6856e3577dc6#db%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)をご参考ください

# DBを使うPhoenixのPJ作成、DB作成、起動

DB利用有のPhoenix PJを作成します

なお、PJ名に「Web」という文字列を含むと、フォルダ作成やモジュール命名の都合から、不具合が出るため、入れないよう気を付けてください（エラーメッセージから判断付けるのは、かなり困難なので、ご注意を）

```
mix phx.new phx14_db --no-webpack
　…（ファイル作成ログが続く）…
Fetch and install dependencies? [Yn] （←y、Enterを入力）
```

実際にインストールされたCowboyやEctoのバージョンが、幾つなのか、ライブラリバージョンを確認するため、mix.lockを見てみましょう

```elixir:mix.lock
%{
  "connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [], [], "hexpm"},
  "cowboy": {:hex, :cowboy, "2.5.0", "4ef3ae066ee10fe01ea3272edc8f024347a0d3eb95f6fbb9aed556dacbfc1337", [], [{:cowlib, "~> 2.6.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.6.2", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
  "cowlib": {:hex, :cowlib, "2.6.0", "8aa629f81a0fc189f261dc98a42243fa842625feea3c7ec56c48f4ccdb55490f", [], [], "hexpm"},
  "db_connection": {:hex, :db_connection, "2.0.1", "09454c6c6e8e4295f400b72580b19f0ac68fda2602e209533285206cb99bee6b", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
  "decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [], [], "hexpm"},
  "ecto": {:hex, :ecto, "3.0.1", "a26605ee7b243a754e6609d1c23da27bcb22823659b07bf03f9020da92a8e4f4", [], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},
  "ecto_sql": {:hex, :ecto_sql, "3.0.0", "8d1883376bee02a0e76b5ef797e39d04333c34b9935d0b4785dbf3cbdb571e2a", [], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.0.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.2.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
  "file_system": {:hex, :file_system, "0.2.6", "fd4dc3af89b9ab1dc8ccbcc214a0e60c41f34be251d9307920748a14bf41f1d3", [], [], "hexpm"},
  "gettext": {:hex, :gettext, "0.16.0", "4a7e90408cef5f1bf57c5a39e2db8c372a906031cc9b1466e963101cb927dafc", [], [], "hexpm"},
  "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
  "mime": {:hex, :mime, "1.3.0", "5e8d45a39e95c650900d03f897fbf99ae04f60ab1daa4a34c7a20a5151b7a5fe", [], [], "hexpm"},
  "phoenix": {:hex, :phoenix, "1.4.0", "56fe9a809e0e735f3e3b9b31c1b749d4b436e466d8da627b8d82f90eaae714d2", [], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
  "phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
  "phoenix_html": {:hex, :phoenix_html, "2.12.0", "1fb3c2e48b4b66d75564d8d63df6d53655469216d6b553e7e14ced2b46f97622", [], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
  "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.2.0", "3bb31a9fbd40ffe8652e60c8660dffd72dd231efcdf49b744fb75b9ef7db5dd2", [], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm"},
  "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [], [], "hexpm"},
  "plug": {:hex, :plug, "1.7.1", "8516d565fb84a6a8b2ca722e74e2cd25ca0fc9d64f364ec9dbec09d33eb78ccd", [], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}], "hexpm"},
  "plug_cowboy": {:hex, :plug_cowboy, "2.0.0", "ab0c92728f2ba43c544cce85f0f220d8d30fc0c90eaa1e6203683ab039655062", [], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
  "plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [], [], "hexpm"},
  "postgrex": {:hex, :postgrex, "0.14.0", "f3d6ffea1ca8a156e0633900a5338a3d17b00435227726baed8982718232b694", [], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
  "ranch": {:hex, :ranch, "1.6.2", "6db93c78f411ee033dbb18ba8234c5574883acb9a75af0fb90a9b82ea46afa00", [], [], "hexpm"},
  "telemetry": {:hex, :telemetry, "0.2.0", "5b40caa3efe4deb30fb12d7cd8ed4f556f6d6bd15c374c2366772161311ce377", [], [], "hexpm"},
}
```

Cowboyが2.5、Ectoが3.0.1であることが確認できました

Telemetryもインストールされていますね:wink:

参考までに、mix.exsも見ておきましょう

```elixir:mix.exs
…
  defp deps do
    [
      {:phoenix, "~> 1.4.0"},
      {:phoenix_pubsub, "~> 1.1"},
      {:phoenix_ecto, "~> 4.0"},
      {:ecto_sql, "~> 3.0"},
      {:postgrex, ">= 0.0.0"},
      {:phoenix_html, "~> 2.11"},
      {:phoenix_live_reload, "~> 1.2", only: :dev},
      {:gettext, "~> 0.11"},
      {:jason, "~> 1.0"},
      {:plug_cowboy, "~> 2.0"}
    ]
  end
…
```

CowboyやTelemetryの個別記載が無いことから、Phoenix 1.4かEctoの依存に含まれたことが確認できます（詳しくは、[HexのDependencies](https://hex.pm/packages/phoenix)を追ってください）

PostgreSQLのパスワードが、「postgres」で無い場合は、下記の「password」項目の修正が必要です

```exs:config/dev.exs　※PostgreSQLの場合
…
# Configure your database
config :phx14_db, Phx14Db.Repo,
  username: "postgres",
  password: "postgres",
  database: "blog_dev",
  hostname: "localhost",
  pool_size: 10
```

おや？「adapter: Ecto.Adapters.Postgres,」の記載が無くなっていますね…これで動くのかしら？

まぁ、ひとまず、DBを作成します　<font color="red">※作成しないとiex内でエラー連発となるのでお忘れなく</font>

```
cd phx14_db
mix ecto.create
```

作成できました（PostgreSQL側も、念のため確認しましたが、無事、作られていました）

Phoenixを起動します

```
iex -S mix phx.server
```

Phoenix起動の際、以下ログが出ていれば起動成功です

```
…
[info] Running BlogWeb.Endpoint with cowboy 2.5.0 at http://localhost:4000
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)
iex> 
```

ブラウザで「```http://localhost:4000```」をアクセスすると、以下ページが表示されます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/6f0e6970-5536-2af3-3bb9-50b07bf26ba3.png)

# PhoenixでDBアクセスするAPIを作る

以下コマンドで、DBアクセスするAPIを作ります

Ctrl+cを2回押して、一度、Phoenixを停止してから、コマンドを入力します

```
mix phx.gen.json Api Post posts title:string body:text
```

以下ログと、実行後の作業指示が示されます

```
* creating lib/blog_web/controllers/post_controller.ex
* creating lib/blog_web/views/post_view.ex
* creating test/blog_web/controllers/post_controller_test.exs
* creating lib/blog_web/views/changeset_view.ex
* creating lib/blog_web/controllers/fallback_controller.ex
* creating lib/blog/api/post.ex
* creating priv/repo/migrations/20181110064453_create_posts.exs
* creating lib/blog/api/api.ex
* injecting lib/blog/api/api.ex
* creating test/blog/api/api_test.exs
* injecting test/blog/api/api_test.exs

Add the resource to your :api scope in lib/blog_web/router.ex:

    resources "/posts", PostController, except: [:new, :edit]

Remember to update your repository by running migrations:

    $ mix ecto.migrate
```

まず、ルーティングにAPI用エントリーとして、上記「resources "/posts", ～」を、「get "/", ～」直下に追記します

```elixir:lib/phx14_db_web/router.ex
defmodule Phx14DbWeb.Router do
  use Phx14DbWeb, :router
  …
  scope "/", Phx14DbWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    resources "/posts", PostController, except: [:new, :edit]
  end
  …
```

マイグレートします

```
mix ecto.migrate
```

以下ログのように、テーブルが作成されます

```
[debug] QUERY OK source="schema_migrations" db=0.0ms
SELECT s0."version"::bigint FROM "schema_migrations" AS s0 FOR UPDATE []
[info] == Running 20181110064453 Phx14Db.Repo.Migrations.CreatePosts.change/0 forward
[info] create table posts
[info] == Migrated 20181110064453 in 0.0s
```

Phoenix 1.3＋Ecto 2系のときと、ログが少し変わってました

では、Phoenixを起動してください

```
iex -S mix phx.server
```

# APIでDBへのデータ投入、読み込み

では、APIでデータ投入してみましょう

APIを叩くために、REST APIクライアントが必要ですが、Firefoxの「RESTClient」、Chromeの「Postman」が便利です

Firefox「RESTClient」
https://addons.mozilla.org/ja/firefox/addon/restclient/

Chrome「Postman」
https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop

ここでは、FirefoxのRESTClientをインストールして、使ってみます

まず、RESTClientの「Headers」メニューで、「Custom Header」を選択して、REST API用のヘッダー設定をします
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/3fcf4caf-6348-6a5c-0435-9da669af6151.png)

次に、URLに「```http://localhost:4000/posts```」を入れ、「Method」を「POST」に変更し、「Body」に以下を入力します

```
{ "post": { "title": "t1", "body": "b1" } }
```

「SEND」ボタンをクリックすると、以下の通り、エラーになります
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/250e300c-f32e-ceba-9fb5-3ad1db090086.png)

「Response」の中身を見てみると、CSRFトークンエラーが出ています
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/ba74d0ce-1f16-9978-fc6d-0d06c08e38ec.png)

これは、リクエストにCSRFトークンが付与されていないことが原因ですが、[ステートレスなAPIでCSRF対応する](http://watanabe-tsuyoshi.hatenablog.com/entry/2015/03/04/123649)のは本筋から外れるので、いったんシンプルに、router.exに設定されているSCRF対策を解除します

```elixir:lib/phx14_db_web/router.ex
defmodule Phx14DbWeb.Router do
  use Phx14DbWebWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
#   plug :protect_from_forgery
    plug :put_secure_browser_headers
  end
  …
```

コンパイルします

```
iex> recompile
```

再度、「SEND」ボタンをクリックすると、今度は成功します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/d68fd338-11c9-7a73-a496-72cbc6008002.png)

「Method」を「GET」に切り替え、「SEND」ボタンをクリックすると、投入したデータが返っていることが確認できます
![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/23470997-96f8-5228-891d-da8583a635bf.png)

# 終わり

Phoenix 1.4でも、1.3時と変わらず、DB+APIがサクっと作れました

[次回は、GraphQLライブラリ「Absinthe」をPhoenix 1.4にて動かしてみよう](https://qiita.com/piacere_ex/items/b4f57b55663403f9ec8e)と思います

# p.s.「いいね」よろしくお願いします

よろしければ、ページ左上の ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/4d515047-cc48-382e-c2b1-3ad0cc50dbbf.png) や ![image.png](https://qiita-image-store.s3.amazonaws.com/0/155423/a4e3da58-70a3-4197-95a2-6a6906650d01.png) のクリックをお願いしますー:bow: