DEV Community

Cover image for 🍱Versioning an API with Phoenix and Elixir
Tarcísio Giroldo
Tarcísio Giroldo

Posted on • Edited on

🍱Versioning an API with Phoenix and Elixir

So hey you, it looks like you are interested in Elixir and Phoenix.💧

I've been learning Elixir over the past six months and this a fast tutorial of how you can version your API just using Elixir and Phoenix.🦅

I will not provide any reason for you do that in your API, right now I do have intention to work with Elixir and Phoenix but I'm just studying because of my interest in functional programming, I'm writing it because I want to practice my English and this is my first article.

Creating the project for our use case

mix phx.new hello_version --no-live --no-dashboard --no-html
Enter fullscreen mode Exit fullscreen mode

Quick explanation about the command: we are passing the flags: --no-live because we are not using LiveView, we don't need the dashboard for monitoring so --no-dashboard and least but not last: --no-html

Running the project

So if you run your project with:

cd hello_version && iex -S mix phx.server
Enter fullscreen mode Exit fullscreen mode

If I'm not wrong (and I hope so) you will see this strange screen:

Image description

That's why we setted for no dashboard, no html and no liveview, if you check your router.ex you will see it is way more clear than a standard router.ex from the command mix phx.new new_project

Generating our controllers and views

To generate our controllers and views, we just need one command:

mix phx.gen.json Content Quotation quotations quote:text author:string book:string
Enter fullscreen mode Exit fullscreen mode

Another quick explanation: using mix phx.gen.json we are generating only the json response for our controllers and views without render or generate HTML and LiveView

After that you will see this in your terminal:

Image description

Just add to our router.ex inside your api scope:

  scope "/api", HelloVersionWeb do
    pipe_through :api

    resources "/quotations", QuotationController, except: [:new, :edit]
  end
Enter fullscreen mode Exit fullscreen mode

After that run:

mix ecto.migrate
Enter fullscreen mode Exit fullscreen mode

And if you do all correctly you should be able to see this screen after the 2 steps earlier

Image description

We will also need create some quotations, so let's use HTTPie in terminal for that

Image description

But you can create with Postman, Insomnia, whatever you want.

Take a deep breath

Ok, I don't know if I'm going to fast or if it's good but let's go to what interest

So hipothetically you are testing the users are not able to use the DELETE verb in your endpoint, so you want to test it with a few users.
You had to change your router.ex to:

  scope "/api", HelloVersionWeb do
    pipe_through :api

    resources "/quotations", QuotationController, except: [:new, :edit, :delete]
  end
Enter fullscreen mode Exit fullscreen mode

Look closely and you will see and you type mix phx.routes is not anymore listed on the routes.

So you want to apply to the test version let's call it t (I'm not a mathematician).

So for versioning you will need to declare your scope api as the :api

Then you will have to create a new scope, so putting all together it will be:

  scope "/api", HelloVersionWeb, as: :api do
    pipe_through :api

    resources "/quotations", QuotationController, except: [:new, :edit, :delete]

    scope "/t", Api.T, as: :t do
      resources "/quotations", QuotationController, except: [:new, :edit]
    end
  end
Enter fullscreen mode Exit fullscreen mode

I will also remove the delete method created by the mix phx.gen.json command

defmodule HelloVersionWeb.QuotationController do
  use HelloVersionWeb, :controller

  alias HelloVersion.Content
  alias HelloVersion.Content.Quotation

  action_fallback HelloVersionWeb.FallbackController

  def index(conn, _params) do
    quotations = Content.list_quotations()
    render(conn, :index, quotations: quotations)
  end

  def create(conn, %{"quotation" => quotation_params}) do
    with {:ok, %Quotation{} = quotation} <- Content.create_quotation(quotation_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", ~p"/api/quotations/#{quotation}")
      |> render(:show, quotation: quotation)
    end
  end

  def show(conn, %{"id" => id}) do
    quotation = Content.get_quotation!(id)
    render(conn, :show, quotation: quotation)
  end

  def update(conn, %{"id" => id, "quotation" => quotation_params}) do
    quotation = Content.get_quotation!(id)

    with {:ok, %Quotation{} = quotation} <- Content.update_quotation(quotation, quotation_params) do
      render(conn, :show, quotation: quotation)
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Ok now we will need a new directory called api inside the controllers folder and a t folder inside the api folder
Like this:

Image description

The QuotationController inside the Api/T folder

defmodule HelloVersionWeb.Api.T.QuotationController do
  use HelloVersionWeb, :controller

  alias HelloVersion.Content
  alias HelloVersion.Content.Quotation

  action_fallback HelloVersionWeb.FallbackController

  def index(conn, _params) do
    quotations = Content.list_quotations()
    render(conn, :index, quotations: quotations)
  end

  def create(conn, %{"quotation" => quotation_params}) do
    with {:ok, %Quotation{} = quotation} <- Content.create_quotation(quotation_params) do
      conn
      |> put_status(:created)
      |> put_resp_header("location", ~p"/api/quotations/#{quotation}")
      |> render(:show, quotation: quotation)
    end
  end

  def show(conn, %{"id" => id}) do
    quotation = Content.get_quotation!(id)
    render(conn, :show, quotation: quotation)
  end

  def update(conn, %{"id" => id, "quotation" => quotation_params}) do
    quotation = Content.get_quotation!(id)

    with {:ok, %Quotation{} = quotation} <- Content.update_quotation(quotation, quotation_params) do
      render(conn, :show, quotation: quotation)
    end
  end

  def delete(conn, %{"id" => id}) do
    quotation = Content.get_quotation!(id)

    with {:ok, %Quotation{}} <- Content.delete_quotation(quotation) do
      send_resp(conn, :no_content, "")
    end
  end
end

Enter fullscreen mode Exit fullscreen mode

So for a better study I added a little more than one quote, so I have these 4 quotes:

Image description

So after that if want to delete, I'm not being able to use anymore the endpoint and receiving an error message which can be personalized in the fallback controller.

So that's it people from functional and Elixir, thank you for your time. See ya 👋

Top comments (0)