既存APIのリネーム②：リクエストJSON、レスポンスJSON

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

[前回](https://qiita.com/piacere_ex/items/688b3e2542bc67ef52c7)に引き続き、mix phx.gen.jsonで自動生成したAPIのリネームをしたいとき、どこを直す必要があり、直さなくて良いところがどこか、イマイチ分かりにくいので、まとめてみました

後半は、「リクエストJSON」と「レスポンスJSON」のリネームです

なお、[「Phoenix」](https://qiita.com/piacerex/items/b7787580fce5f148242f#phoenix%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)は、ElixirのWebフレームワークです

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

# 本コラムの検証環境、事前構築のコマンド

本コラムは、以下環境で検証しています（Windowsで実施していますが、Linuxやmacでも動作する想定です）

- Windows 10
- Elixir 1.8.1　　　　※<b>[最新版のインストール手順はコチラ](https://qiita.com/piacere_ex/items/6714e1440e3f25fb46a1#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99elixir%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)</b>
- Phoenix 1.4.0 　　 ※<b>[最新版のインストール手順はコチラ](https://qiita.com/piacere_ex/items/b7787580fce5f148242f#phoenix%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)</b>
- PostgreSQL 10.1 　※<b>[最新版のインストール手順はコチラ](https://qiita.com/piacere_ex/items/a7558adc6856e3577dc6#postgresql%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)</b>

また、以下コマンドでPhoenix PJ＋JSON APIを構築しています

```
mix phx.new api_sample --no-webpack
cd api_sample 
mix ecto.create
mix phx.gen.json Api Post post title:string body:string
mix ecto.migrate
【手動でルーティング追加とprotect_from_forgery無効化をする】
iex -S mix phx.server
```

# リクエストJSONのキーを変更するとき

「POST」と「PUT」が、リクエストJSONのキーを要求するので、コントローラのcreate()とupdate()を変更する必要があります

いずれも、関数パターンマッチで実装されているので、マッチするパターンを変更するだけです

```elixir:lib/api_sample_web/controllers/post_controller.ex
defmodule ApiSampleWeb.PostController do
  …
  def create(conn, %{"hoge" => post_params}) do
    # modify here------^
    with {:ok, %Post{} = post} <- Api.create_post(post_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.post_path(conn, :show, post))
      |> render("show.json", post: post)
    end
  end
  …
  def update(conn, %{"id" => id, "hoge" => post_params}) do
    # modify here------------------^
    post = Api.get_post!(id)

    with {:ok, %Post{} = post} <- Api.update_post(post, post_params) do
      render(conn, "show.json", post: post)
    end
  end
  …
```

# リクエストJSON

## リクエストJSONのキーを無くしたいとき

そもそも、キーを無くしたい場合は、以前書いた、[「mix phx.gen.jsonで作った既存APIの非DB化」の「リクエストJSONのバリデーションを解除する」](https://qiita.com/piacere_ex/items/bc80fd25e98687fd029f#%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88json%E3%81%AE%E3%83%90%E3%83%AA%E3%83%87%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A7%A3%E9%99%A4%E3%81%99%E3%82%8B)をご参考ください

# レスポンスJSON

## レスポンスJSONのキーを変更するとき

Phoenixの自動生成JSON APIは、レスポンスJSONに「data」というキーを付与します

```json
{
  "data":
  [
    { 
      "id":   1,
      "title":"t1",
      "body": "b1"
    }
  ]
}
```

これを以下のように変更します

```json
{
  "hoge":
  [
    { 
      "id":   1,
      "title":"t1",
      "body": "b1"
    }
  ]
}
```

これを行うには、ビューのrender()2箇所の「%{data:～}」の「data」の部分を変更します

```elixir:lib/api_sample_web/views/post_view.ex
defmodule ApiSampleWeb.PostView do
  def render("index.json", %{post: post}) do
    %{hoge: render_many(post, PostView, "post.json")}
    #  ^---modify here
  end

  def render("show.json", %{post: post}) do
    %{hoge: render_one(post, PostView, "post.json")}
    #  ^---modify here
  end
  …
```

## レスポンスJSONのキーを無くしたいとき

Phoenixの自動生成JSON APIは、レスポンスJSONに「data」というキーが付与されますが、Vue.jsのaxiosなんかでこれを取得すると、``response.data.data``みたいな書き方になり、ウザイので、キーを無くし、``response.data``で取得できるようにしたい…なんてケースがあります

レスポンスJSONで言うと、以下のようにしたいときです

```json
{
  [
    { 
      "id":   1,
      "title":"t1",
      "body": "b1"
    }
  ]
}
```

これを行うには、ビューのrender()2箇所から、「data」で包むコードを削除します

```elixir:lib/api_sample_web/views/post_view.ex　※変更前
defmodule ApiSampleWeb.PostView do
  …
  def render("index.json", %{post: post}) do
    %{data: render_many(post, PostView, "post.json")}
  end

  def render("show.json", %{post: post}) do
    %{data: render_one(post, PostView, "post.json")}
  end
  …
```

```elixir:lib/api_sample_web/views/post_view.ex　※変更後
defmodule ApiSampleWeb.PostView do
  …
  def render("index.json", %{post: post}) do
    render_many(post, PostView, "post.json")
    # ^---%{data:} removed
  end

  def render("show.json", %{post: post}) do
    render_one(post, PostView, "post.json")
    # ^---%{data:} removed
  end
  …
```

## コントローラからビューに渡すキーを変更したいとき

コードをキレイにしたいという観点で、リクエストJSONのキーだけで無く、コントローラからビューに渡すキーを変更したい場合は、render()呼び出しの第2引数のキーを変更し、受け側のビューのrender()も変更します

ついでに各種ローカル変数も変更しています（これは必須ではありませんが、変えておいた方が無難です）

```elixir:lib/api_sample_web/controllers/post_controller.ex
defmodule ApiSampleWeb.PostController do
  …
  def index(conn, _params) do
    hoge = Api.list_post()
    # ^---modify here
    render(conn, "index.json", hoge: hoge)
    # modify here------^--------^-----^
  end

  def create(conn, %{"hoge" => hoge_params}) do
    # modify here------^--------^
    with {:ok, %Post{} = hoge} <- Api.create_post(hoge_params) do
      # modify here-------^------------------------^
      conn
      |> put_status(:created)
      |> put_resp_header("location", Routes.post_path(conn, :show, hoge))
      # modify here-------------------------------------------------^
      |> render("show.json", hoge: hoge)
      # modify here-----------^-----^
    end
  end

  def show(conn, %{"id" => id}) do
    post = Api.get_post!(id)
    render(conn, "show.json", hoge: hoge)
    # modify here--------------^-----^
  end

  def update(conn, %{"id" => id, "hoge" => hoge_params}) do
    # modify here------------------^--------^
    hoge = Api.get_post!(id)
    # ^---modify here

    with {:ok, %Post{} = post} <- Api.update_post(hoge, hoge_params) do
      # modify here-------^------------------------^-----^
      render(conn, "show.json", hoge: hoge)
      # modify here--------------^-----^
    end
  end

  def delete(conn, %{"id" => id}) do
    post = Api.get_post!(id)
    # ^---modify here

    with {:ok, %Post{}} <- Api.delete_post(post) do
      # modify here-------------------------^
      send_resp(conn, :no_content, "")
    end
  end
```

```elixir:lib/api_sample_web/views/post_view.ex
defmodule ApiSampleWeb.PostView do
  …
  def render("index.json", %{hoge: hoge}) do
    # modify here-------------^-----^
    %{data: render_many(hoge, PostView, "post.json")}
    # modify here--------^
  end

  def render("show.json", %{hoge: hoge}) do
    # modify here-------------^-----^
    %{data: render_one(hoge, PostView, "post.json")}
    # modify here-------^
  end
  …
```

なお、DBスキーマや、それに紐付く構造体（%Post{}）、およびモジュール名まで変更する場合は、上記の続きを「気合い」で書き換えていくことも可能ですが、自動生成し直した方が速いと思われます

## レスポンスをJSONでは無くテキストにしたい

render()の第一引数を ``index.json`` から ``index.txt`` に変更すれば、JSONからテキストに変更できます

ちなみに、あたかもファイル名のように見えますが、 ``index.json`` や ``index.txt`` というファイルが存在する訳ではありません

```elixir:lib/api_sample_web/views/post_view.ex
defmodule ApiSampleWeb.PostView do
  …
  def render("index.txt", %{hoge: hoge}) do
    # modify here----^
    "hoge, foo"  # <--modify here
  end

  def render("show.txt", %{hoge: hoge}) do
    # modify here---^
    "hogehoge"  # <--modify here
  end
  …
```

# 終わり

既存APIのリクエストJSON、レスポンスJSONをリネームする方法について解説しました

APIを自動生成する際にミスってしまったときや、自動生成したものに機能追加を行った後にリネームしたい、なんてときには、ご参考ください

# 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:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください！:tada: