Phoenix JSON API: Getting started

Phoenix is great for JSON APIs.

Why use a full-featured framework like Phoenix for a JSON API?

Unlike Rails and various other frameworks it inspired, Phoenix carries very little performance overhead beyond what a simpler library would offer. Rails is considerably slower than Rack or Sinatra for a simple app. The same is true of Django compared to simpler libraries like Flask. In contrast, Phoenix apps can serve nearly the same number of requests on the same hardware as apps written with just Plug (which it uses internally).

Since Phoenix comes with a lot of useful things out of the box (routing, controllers, generators, etc, etc, etc) and the performance cost is negligible, most Elixir devs use it even when writing a pure JSON API with a separate web or mobile front-end.

Getting started

This lesson, we'll make the simplest possible API—a single endpoint that doesn't rely on any stored data. We'll make an endpoint /api/ that returns the result of a die roll. First, generate an app. Call it "firehose". If you haven't installed Elixir yet, see the episode on ASDF.

mix phx.new firehose

Let the installer automatically install all the dependencies, cd into the newly created firehose directory and then take a look at the lib/firehose_web/router.ex file. There's a block of code that defines a scope for an API. Uncomment that and add a route for /roll:

  scope "/api", FirehoseWeb do
    pipe_through :api

    get "/roll", RollController, :index  # this won't work until we write a controller!
  end

The line with pipe_through :api will call all of the plugs in the :api pipeline near the top of the file. Unlike the much longer browser pipeline that includes plugs to handle sessions, flash messages and a variety of security related concerns, the api pipeline only has a single plug:

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

The controller

If you start the sever with mix phx.server, save the file and visit localhost:4000/api/roll, you'll see an error since the route we defined above routes get requests for that route to the :index action of a RollController that doesn't exist! Create a new one at /lib/firehose_web/controllers/roll_controller.ex with the following code:

defmodule FirehoseWeb.RollController do
  use FirehoseWeb, :controller

  def index(conn, _params) do
    num = :rand.uniform(6)
    render(conn, "index.json", roll: num)
  end
end

The use FirehoseWeb, :controller line at the top pulls in functionality via a macro and makes the module a Phoenix controller.

The index/2 function takes a connection struct and parameters (which aren't used) and returns the result of rendering the "index.json" template with a randomly generated number between 1 and 6 assigned to the key :roll.

The view (or template)

Reloading the page at this point will show a different error. Now the controller is defined, but Phoenix expects every controller to have a corresponding view, so we'll have to make one. Call it /lib/firehose_web/views/role_view.ex and add the minimum code to make the module a Phoenix view:

defmodule FirehoseWeb.RollView do
  use FirehoseWeb, :view
end

At this point, we're close but the render function in the controller had "index.json" as its first argument and we don't have anything in our app to handle that yet. We could create a template named index.json.eex and put it in the templates directory (under firehose_web) and the page would load. However, with JSON responses, the standard path is to write render functions in the view directly instead of writing a template. Just return a map and Phoenix will automatically convert it into a JSON response!

We'll add two function heads to the new view—one for valid input and one for everything else:

defmodule FirehoseWeb.RollView do
  use FirehoseWeb, :view

  def render("index.json", %{roll: num}) when is_integer(num) do
    %{status: "ok", roll: num}
  end

  def render("index.json", _), do: %{status: "error"}
end

Done!

Go to localhost:4000/api/roll and you should see something like {"status": "ok", "roll": 4}. Hit refresh and see more random results between 1 and 6.

Part 2: Phoenix JSON API: Rendering many

Back to index

No Comments