phx.gen.jsonでAPIを生成する

# `phx.gen.json`でAPIを生成する

- [`phx.gen.json`でAPIを生成する](#phxgenjsonでapiを生成する)
  - [`*_controller.ex`とは](#_controllerexとは)
  - [`*_json.ex`とは](#_jsonexとは)
    - [使用例](#使用例)
  - [`fallback_controller.ex`とは](#fallback_controllerexとは)
    - [`{:error, %Ecto.Changeset{}}`](#error-ectochangeset)
    - [`Ecto.NoResultsError`](#ectonoresultserror)
  - [`changeset_json.ex`とは](#changeset_jsonexとは)
- [用語](#用語)
  - [pipeline](#pipeline)

<font color="darkgreen">**`mix phx.gen.json`**</font>というコマンドを使用すると、
Schemaと基本的な<font color="red">**CRUDを行うAPIを生成する**</font>ことができる。

以下の作業を行い、生成したAPIを利用できるようにする。

1. <font color="darkgreen">`mix phx.gen.json`</font>の実行
2. `router.ex`に追記

<font color="darkgreen">`mix phx.gen.json`</font>を実行することで、`/controllers`以下に次のファイルが生成される。

* **`*_controller.ex`**
* **`*_json.ex`**
* **`fallback_controller.ex`**
* **`changeset_json.ex`**

router.exには画面のしたがって記述するとよい。

## `*_controller.ex`とは

<font color="darkgreen">**`mix phx.routes`**</font>を実行すると、ルートが追加されている確認できる。

`*_controller.ex`は、このルートに対するアクションを定義している。

## `*_json.ex`とは

<font color="sienna">`pipeline :api`</font>には<font color="sienna">`plug :accepts, ["json"]`</font>と記述されている。

このルートのレスポンスのコンテンツタイプは**JSON**となり、
よってViewは<font color="red">JSONモジュール</font>となる。

このモジュールでは`*_contoller.ex`の<font color="navy">**`render()`**</font>で使用する<font color="red">**出力形式を定義**</font>している。

indexアクションのルートに対し<font color="darkgreen">`curl`</font>でリクエストを送信すると、ここで定義した形式で出力されることが確認できる。

```
curl http://localhost:4000/api/users
{"data":[]}
```

### 使用例

ここでは以下を実行し、出力を変更していない場合とする。
<font color="darkgreen">`mix phx.gen.json Users User users name:string age:integer`</font>

```shell
// index 実行
curl http://localhost:4000/api/users

// index 出力
{"data":[]}

// create 実行
curl -X POST -H 'Content-Type: application/json' -d '{"user": {"name" : "Taro", "age" : "32"}}' http://localhost:4000/api/users

// show 実行
curl http://localhost:4000/api/users/1

// show 出力
{"data":{"id":1,"name":"Taro","age":32}}

// update 実行
curl -X PATCH -H 'Content-Type: application/json' -d '{"user" : {"age" : "41"}}' http://localhost:4000/api/users/1

// update 出力
{"data":{"id":1,"name":"Taro","age":41}}

// delete 実行
curl -X DELETE http://localhost:4000/api/users/1
```

## `fallback_controller.ex`とは

コントローラの<font color="red">アクションが失敗したとき</font>に呼び出される処理を定義している。

<font color="sienna">`action_fallback`</font>という記述でフォールバックコントローラとして呼び出されている。

このモジュールには2つの<font color="blue">`call()`</font>が定義されている。

* <font color="blue">`call(conn, {:error, %Ecto.Changeset{} = changeset})`</font>
* <font color="blue">`call(conn, {:error, :not_found})`</font>

コントローラーのアクションで呼び出される関数を確認する。

関数にエラー時に次のエラータプル、または例外を発生している

* `{:error, %Ecto.Changeset{}}`
* `Ecto.NoResultsError`

このような場合にフォールバックコントローラによるフォールバック処理が行われる。

<font color="blue">`call(...)`</font>は以下のように記述される。

```elixir
def call(conn, ...) do
  conn
  |> put_status(ステータスコード)
  |> put_view(ビュー)
  |> render(テンプレート)
end
```

<font color="blue">`put_status/2`</font>は、エラーコードを整数またはアトムを指定できる。
エラーコードは整数、アトムは[**Plug.Conn.Status**](https://hexdocs.pm/plug/1.14.2/Plug.Conn.Status.html#code/1)で定義されたものが使用できる。

<font color="blue">`put_view/2`</font>は、ビューを指定する。
<font color="sienna">`html: HTMLビュー, json: JSONビュー`</font>と記述する。
Phoenixプロジェクトの生成時に`ErrorHTML`と`ErrorJSON`が生成されているのでこれを指定する。

### `{:error, %Ecto.Changeset{}}`

アクションでは<font color="sienna">`with`</font>句によるパターンマッチが行われている。

```elixir
with {:ok, %User{} = user} <- Users.create_user(user_params) do
  conn
  |> put_status(:created)
  |> put_resp_header("location", ~p"/api/users/#{user}")
  |> render(:show, user: user)
end
```

パターンマッチしない場合は、値をそのまま返す。

そのため<font color="red">Ectoによる更新処理に失敗した場合</font>、
アクションは`{:error, %Ecto.Changeset{}`を出力し、コールバック処理が呼び出される。

### `Ecto.NoResultsError`

この例外は<font color="blue">`Repo.get!()`</font>などで発生する可能性のある例外である。

実際にこの例外が発生するようAPIを呼び出してみると、iexに以下のでデバックログが出力されている。

`[debug] Converted error Ecto.NoResultsError to 404 response`

Phoenixでは特定の例外は、このように変換される(らしい)。

よってこの例外が発生したときは、アクションは`{:error, :not_found}`が出力される。

なおこれ以外のEcto例外は、デフォルトの動作と同様`{:error, :internal_server_error}}`が出力される(らしい)。

## `changeset_json.ex`とは

Ecto.Changesetから**エラー文を抽出**し、jsonとして返すためのビュー

# 用語

## pipeline

plugとはアクションを実行する前処理のことである。

`router.ex`の<font color="sienna">**`pipeline`**</font>とは<font color="red">**plug**をまとめた</font>のことである。

<font color="sienna">**pipe_through**</font>でスコープごとに実行する<font color="sienna">`pipeline`</font>を指定することができる。。