Create a REST API with Phoenix and Ecto

Quick and dirty guide to building a REST API with Phoenix and Ecto. Let’s just get right to it shall we?

Database

To save time and energy installing Postgres, create a docker-compose.yml file that starts postgres. It may look like this:

version: '2'
services:
  postgres:
    image: postgres
    environment:
      POSTGRES_DB: "rest_api_dev"
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
    volumes:
      - ./postgres_data:/var/lib/postgres/data
    ports:
      - "5432:5432"

Let’s fire it up as a daemon:

$ docker-compose up -d

Check that it is up and running:

$ docker ps
CONTAINER ID   IMAGE      NAMES
e28642950ed9   postgres   restapi_postgres_1
$ docker logs restapi_postgres_1

Create and configure Phoenix application:

$ mix phoenix.new rest_api --database postgres --no-brunch --no-html
$ cd rest_api

Edit config/dev.exs and set username, password and database:

# Configure your database
config :rest_api, RestApi.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "rest_api_dev",
  hostname: "localhost",
  pool_size: 10

Let’s create a new model

$ mix phoenix.gen.model Post posts title:string content:string

It should output something similar:

* creating web/models/post.ex
* creating test/models/post_test.exs
* creating priv/repo/migrations/20170608173224_create_post.exs

To create the table in the database, execute the following command:

$ mix ecto.migrate

Now, let’s try inserting some data!

$ iex -S mix
iex(1)> alias RestApi.Repo
iex(2)> alias RestApi.Post
iex(3)> Repo.insert(%Post{ title: "Elixir", content: "Is awesome" })
iex(4)> Repo.insert(%Post{ title: "The world", content: "Is full of magic" })
iex(5)> Repo.all(Post)

Create REST Controller (JSON)

$ mix phoenix.gen.json Post posts --no-model

It should output something like this:

* creating web/controllers/post_controller.ex
* creating web/views/post_view.ex
* creating test/controllers/post_controller_test.exs
* creating web/views/changeset_view.ex

Add a mapping in web/router.ex that maps to PostController

defmodule RestApi.Router do
  use RestApi.Web, :router

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

  scope "/api", RestApi do
    pipe_through :api

    resources "/posts", PostController # NEW!!
  end
end

Update the render function for post.json in web/views/post_view.ex to include the title and content attributes:

  def render("post.json", %{post: post}) do
    %{id: post.id,
      title: post.title,     # NEW!
      content: post.content} # NEW!
  end

Start the server

$ mix phoenix.server

Let’s try fetching some posts:

$ curl http://localhost:4000/api/posts

You should get output similar to this:

{"data":[{"title":"Elixir","id":1,"content":"Is awesome"},{"title":"The world","id":2,"content":"Is full of magic"}]}

Awesome! :)