[Elixir] PhoenixでJSONを返すWeb APIを作る

  • 46
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

※ Phoenix 1.0.3

やること

  1. mix phoenix.gen.htmlする
  2. RouterにAPIリクエストを受けるためのpipelineを設定する
  3. ControllerでContent Negotiationを指定する
  4. ViewにJSONを返すよう記述する

1. 事前準備

以下を実行

mix phoenix.gen.html Note notes title:string description:text

mix phoenix.gen.jsonというタスクもあり、こちらはすぐにAPIを用意したいだけなら十分なコードが生成される。ただし管理に便利なWebUIは自動生成されないため、今回はmix phoenix.gen.htmlの方を使う。

2. Router

この辺はrouter.exにコメント書きされているのでなんとなくわかる。

web/router.ex
defmodule Demo.Router do
  use Demo.Web, :router

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

  pipeline :api do
    plug :accepts, ["json"]
  end

  scope "/", Smaug do
    pipe_through :browser

    get "/", PageController, :index
    resources "/notes", NoteController
  end

  scope "/api", Smaug do
    pipe_through :api

    resources "/notes", NoteController, only: [:index, :show]
  end
end

plug :accepts, ["json"]を指定することで、Accept: application/jsonのあるHTTP Requestのみを許可してくれる。

また、resources/3でAPIとして公開するメソッドを制限できる。

3. Controller

生成されたControllerには、Templateのファイル名がStringでベタ書きされている。これだとAccept: application/jsonを指定したHTTP Requestを飛ばしてもContent Negotiationが行われない。

そこで、以下のようにAtomで指定する。

web/controllers/note_controller.ex
#(該当箇所のみ抜粋)
def index(conn, _params) do
  notes = Repo.all(Note)
  # render(conn, "index.html", notes: notes)
  render(conn, :index, notes: notes)
end

4. View

mixが生成したViewだとJSONへのencodingが行われず、poisonがエラーを出す。そのため以下のようにViewを修正する。

web/views/note_view.ex
defmodule Demo.NoteView do
  use Demo.Web, :view

  def render("index.json", %{notes: notes}) do
    %{data: render_many(notes, Demo.NoteView, "note.json")}
  end

  def render("show.json", %{story: story}) do
    %{data: render_one(note, Demo.NoteView, "note.json")}
  end

  def render("note.json", %{note: note}) do
    %{id: note.id,
      title: note.title,
      description: note.description}
  end
end

確認

curl 'http://localhost:4000/api/notes' -v -X GET -H 'Accept: application/json'