🍱Versioning an API with Phoenix and Elixir
Tags: elixir, phoenix, tutorial, beginners
Published: 2023-03-02 18:14:49

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

```elixir
mix phx.new hello_version --no-live --no-dashboard --no-html
```

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:

```bash
cd hello_version && iex -S mix phx.server
```
If I'm not wrong (and I hope so) you will see this strange screen:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2fl3bomqet4q8gh4212.png)

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:

```elixir
mix phx.gen.json Content Quotation quotations quote:text author:string book:string
```
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vc2v201yfj3ldquxmbzy.png)

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

```elixir
  scope "/api", HelloVersionWeb do
    pipe_through :api

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

After that run:

```
mix ecto.migrate
```

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


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/342somlz1fuga55g5zy2.png)

We will also need create some quotations, so let's use [HTTPie](https://httpie.io/) in terminal for that


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vjx8i6gi6c5hegqkyspv.png)

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:

```elixir
  scope "/api", HelloVersionWeb do
    pipe_through :api

    resources "/quotations", QuotationController, except: [:new, :edit, :delete]
  end
```
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:

```elixir
  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
```

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

```elixir
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
```

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swiq292oa9tcv3mdajxu.png)

The QuotationController inside the Api/T folder

```elixir
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

```

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xt9og6t38dzwpmg4t59y.png)

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 👋