---
title: Build and test a blazing fast JSON API with Phoenix, an Elixir framework
teaser: Learn how to build a fast, low-latency API with Phoenix and Postgres.
tags: web,elixir,phoenix,testing
author: Paul Smith
published_on: 2015-03-20
---

Note: This guide was written for Phoenix 0.10. Parts of it may no longer work if
you are using a newer version. If you are using a newer version of Phoenix,
check out the [updated blog post].

[updated blog post]: https://thoughtbot.com/blog/building-a-phoenix-json-api

Let's build a JSON API that serves a list of contacts. We'll be writing it
using Elixir and Phoenix 0.10. [Phoenix](http://www.phoenixframework.org) is a
framework written in Elixir that aims to make writing fast, low latency web
applications as enjoyable as possible. This will not go through installing
Elixir or Phoenix. See the
[Phoenix Guides](http://www.phoenixframework.org/v0.10.0/docs/getting-started)
to get started.

### Why Elixir and Phoenix?

Erlang is a Ferrari wrapped in the sheet metal of an old beater. It has
immense power, but to many people, it looks ugly. It has been used by
WhatsApp to handle billions of connections, but many people struggle with
the unfamiliar syntax and lack of tooling. [Elixir](http://elixir-lang.org)
fixes that. It is built on top of Erlang, but has a beautiful and enjoyable
syntax, with tooling like
[mix](http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html)
to help build, test and work with applications efficiently.

Phoenix builds on top of Elixir to create very low latency web applications, in
an environment that is still enjoyable. Blazing fast applications and an
enjoyable development environments are no longer mutually exclusive. Elixir
and Phoenix give you both. Response times in Phoenix are often measured in
*microseconds* instead of milliseconds.

Now that we've discussed why we might want to build something in with this
framework, let's build something!

### Writing the test

See [getting started](http://www.phoenixframework.org/v0.10.0/docs)
on the Phoenix website to see how to create a new app called `HelloPhoenix`.
We'll be using Phoenix 0.10.0 for this exercise.

Now that you have your Phoenix app setup let's start by writing a test.
Let's create a file at `test/controllers/contact_controller_test.exs`

```elixir
defmodule HelloPhoenix.ContactControllerTest do
  use ExUnit.Case, async: false
  use Plug.Test
  alias HelloPhoenix.Contact
  alias HelloPhoenix.Repo
  alias Ecto.Adapters.SQL

  setup do
    SQL.begin_test_transaction(Repo)

    on_exit fn ->
      SQL.rollback_test_transaction(Repo)
    end
  end

  test "/index returns a list of contacts" do
    contacts_as_json =
      %Contact{name: "Gumbo", phone: "(801) 555-5555"}
      |> Repo.insert
      |> List.wrap
      |> Poison.encode!

    response = conn(:get, "/api/contacts") |> send_request

    assert response.status == 200
    assert response.resp_body == contacts_as_json
  end

  defp send_request(conn) do
    conn
    |> put_private(:plug_skip_csrf_protection, true)
    |> HelloPhoenix.Endpoint.call([])
  end
end
```

We write a `setup` function to wrap our Ecto calls in a transaction that will
ensure that our database is always empty when we start our tests.

The test itself does what you would expect. `use Plug.Test` gives us access
to the `conn/2` function for creating test connections. In our test we
insert a new Contact, wrap it in a list and then encode it. After that we
create a new connection and send the request. We assert that the response
was successful and that the body contains a list of contacts encoded as
JSON.

Run `mix test` and we'll see the error `HelloPhoenix.Contact.__struct__/0 is
undefined, cannot expand struct HelloPhoenix.Contact`. This means we haven't
yet created our model. Let's use [Ecto](https://github.com/elixir-lang/ecto) for
hooking up to a Postgres database.

### Creating our databases

Ecto uses a repository for saving and retrieving data from a database.
Phoenix already comes with a repo set up and a default configuration. Make
sure your Postgres username and password are correct in `config/dev.exs` and
`config/test.exs`.

Let's see what new `mix` tasks we get from Ecto by running `mix -h | grep ecto`.

You'll see a number of tasks you can use. For now let's create the dev and
test databases. After that we can add our first model.

    # This will create your dev database
    $ mix ecto.create
    # This will create your test database
    $ env MIX_ENV=test mix ecto.create

### Adding the Contact model

Let's add a schema for `Contact` at `web/models/contact.ex`.

```elixir
defmodule HelloPhoenix.Contact do
  use Ecto.Model

  schema "contacts" do
    field :name
    field :phone

    timestamps
  end
end
```

Next we'll create a migration with `mix ecto.gen.migration create_contacts`. In
the newly generated migration, write this:

```elixir
defmodule HelloPhoenix.Repo.Migrations.CreateContacts do
  use Ecto.Migration

  def change do
    create table(:contacts) do
      add :name
      add :phone

      timestamps
    end
  end
end
```

The default column type for Ecto migrations is `:string`. To see what else
you can do, check out the
[Ecto.Migration docs](http://hexdocs.pm/ecto/Ecto.Migration.html).

Now run `mix ecto.migrate` to create the new table, and once more for test
`MIX_ENV=test mix ecto.migrate`.

### Adding the routes and controller

Let's get to our API endpoint with a route that will look like `/api/contacts`.

```elixir
# In our web/router.ex
defmodule HelloPhoenix.Router do
  use Phoenix.Router

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

  scope "/api", HelloPhoenix do
    pipe_through :api

    resources "/contacts", ContactController
  end
end
```

If you're coming from Rails, you'll note that `/api/contacts.json` will
result in a 404 not found. You are expected set the appropriate request
header. In a pinch you can do `/api/contacts?format=json`, but this is not
recommended. The trailing format param was not added for performance reasons
and because HTTP headers already enable this functionality.

Now, if we run `mix test` we see that we still need a `ContactController`.

    ** (UndefinedFunctionError) undefined function: HelloPhoenix.ContactController.init/1 (module HelloPhoenix.ContactController is not available)

Let's create our controller at `web/controllers/contact_controller.ex`

```elixir
defmodule HelloPhoenix.ContactController do
  use HelloPhoenix.Web, :controller
  alias HelloPhoenix.Repo
  alias HelloPhoenix.Contact

  plug :action

  def index(conn, _params) do
    contacts = Repo.all(Contact)
    render conn, contacts: contacts
  end
end
```

First we make sure to get all the Contacts with `Repo.all(Contact)`. Then we
render JSON with `Phoenix.Controller.render/2`. The function is automatically
imported when we call `use HelloPhoenix.Web, :controller`. Check out
`web/web.ex` to see what else is imported.

If we run `mix test` our tests won't pass quite yet.

    ** (UndefinedFunctionError) undefined function: HelloPhoenix.ContactView.render/2 (module HelloPhoenix.ContactView is not available)

We need a view to render our JSON.

### Rendering our JSON with a view

Views handle how to output our JSON. Right now it's pretty simple, but in
the future, this is where we could change what we send based on user's
permissions for example.

Let's create a file in `web/views/contact_view.ex`

```elixir
defmodule HelloPhoenix.ContactView do
  use HelloPhoenix.Web, :view

  def render("index.json", %{contacts: contacts}) do
    contacts
  end
end
```

This will use pattern matching to set and then return `contacts`. Phoenix
will automatically encode the array of contacts to JSON. You can use this
view function to customize how the JSON is presented, but we'll cover that in a
later post.

At this point when you run `mix test` all tests should pass.

### Cleanup

Let's check out `HelloPhoenix.Web` in `web/web.ex` to cleanup our app a bit
more. If we open that file up we see that the controller function already
has an alias for `HelloPhoenix.Repo`.

```elixir
  def controller do
    quote do
      # Auto generated - This imports all the macros and functions that a controller needs.
      use Phoenix.Controller

      # Auto inserted - The app was generated with an alias to Repo as a convenience.
      alias HelloPhoenix.Repo

      # This imports the router helpers so you can generate paths like
      # `api_contacts_path(conn)`
      import HelloPhoenix.Router.Helpers
    end
  end
```

This means that in your controller you can remove your alias for
`HelloPhoenix.Repo`.

Let's use
[ExUnit.CaseTemplate](http://elixir-lang.org/docs/stable/ex_unit/ExUnit.CaseTemplate.html)
to clean up our tests a bit. In `test/test_helper.exs`

```elixir
# Add this above `ExUnit.start`
defmodule HelloPhoenix.Case do
  use ExUnit.CaseTemplate
  alias Ecto.Adapters.SQL
  alias HelloPhoenix.Repo

  setup do
    SQL.begin_test_transaction(Repo)

    on_exit fn ->
      SQL.rollback_test_transaction(Repo)
    end
  end

  using do
    quote do
      alias HelloPhoenix.Repo
      alias HelloPhoenix.Contact
      use Plug.Test

      # Remember to change this from `defp` to `def` or it can't be used in your
      # tests.
      def send_request(conn) do
        conn
        |> put_private(:plug_skip_csrf_protection, true)
        |> HelloPhoenix.Endpoint.call([])
      end
    end
  end
end
```

Adding code to `using` will make those functions and aliases available in
every test. This makes it so that we can remove `send_request/1` and the other
alias from our test and replace it with `use HelloPhoenix.Case`

```elixir
defmodule HelloPhoenix.ContactControllerTest do
  use HelloPhoenix.Case, async: false
  # We removed the other aliases since they're already included in
  # `HelloPhoenix.Case`. We also removed the `setup` macro.

  test "/index returns a list of contacts" do
    contacts_as_json =
      %Contact{name: "Gumbo", phone: "(801) 555-5555"}
      |> Repo.insert
      |> List.wrap
      |> Poison.encode!

    response = conn(:get, "/api/contacts") |> send_request

    assert response.status == 200
    assert response.resp_body == contacts_as_json
  end

  # We also removed the function definition for `send_request/1`
end
```

### That's a wrap

Now you've seen how to create and test a Phoenix JSON API. We've also
learned how to cleanup our files and make it easier to use our modules in
other controllers and tests in the future by using `HelloPhoenix.Web` and
`ExUnit.CaseTemplate`. You can now deploy this app to Heroku with the [Elixir
buildpack](https://github.com/HashNuke/heroku-buildpack-elixir).
