※ Phoenix 1.0.3
やること
- 
mix phoenix.gen.htmlする
- RouterにAPIリクエストを受けるためのpipelineを設定する
- ControllerでContent Negotiationを指定する
- 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にコメント書きされているのでなんとなくわかる。
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で指定する。
# (該当箇所のみ抜粋)
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を修正する。
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'