REST APi in Phoenix
Tags: phoenix, elixir, api, otp
Published: 2022-08-16 09:35:00

Ever wondered how to write a small REST APi for your frontend or for public usage? - here is how.

Requirements: 
- Elixir v13.3.2 + 
- Phoenix v1.6.10+
- Basic Knowledge of Elixir

```
$ elixir -v
Elixir 1.13.2 (compiled with Erlang/OTP 24)

$ mix phx.new --version
Phoenix installer v1.6.10
```

## Getting started

Lets start with creating our Project with the following command
```
$ mix phx.new myApp --no-html --no-assets --no-mailer --no-dashboard --no-ecto 
```
> This will remove all the useless boilerplate for us - since APis do not need any HTML or CSS. 

Now lets path into your folder:
```
$ cd myApp
```

Lets open our router first `lib/myapp_web/router.ex`, there, add the following code 
`get "/random", RandomController, :index`

Your Router should now look like this: 
```elixir
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

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

  # localhost:4000/api/{route}
  scope "/api", MyAppWeb do
    pipe_through :api

    get "/random", RandomController, :index
  end
end
```

### Custom Controller

As defined in the router, we now need a Controller called `RandomController`.
For that, open the folder `controllers` (`lib/myapp_web/controllers`) and create a new file called `random_controller.ex`.

```elixir
defmodule MyAppWeb.RandomController do
  use MyAppWeb, :controller

  def index(conn, _params) do
    send_resp(conn, 200, "hello world")
  end
end
```

Save this file and now try to start your server. 
```
$ mix phx.server
```

Your server should be listening on `http://localhost:4000/api/random`. Try to enter that URL in your favourite browser. Your should now see `"hello world"` 😁.

### Custom Files

It's not a good practice to write functions directly into your controller - specially in a bigger project - so lets write a helper function that handles everything for us.

Create a folder called `helper` with the following path: `lib/myapp/helper`. 

Since this isn't web related, we will now use our main folder (not the `myapp_web` one). 

In that folder, create a file called `random.ex`. 

It should look like this:
```elixir
defmodule MyApp.Random do
  def random_number do
   Enum.random(100..10000)
  end
end
```

Awesome, now lets go back to our `random_controller.ex` and modify it a little bit.

First of all, import our new helper file with its function.

`import MyApp.Random`

Then replace our "hello world" response with the function name `random_number()`. 

Like this: 
`send_resp(conn, 200, random_number())`. 

Your `random_controller.ex` should now look like this 

```elixir
defmodule MyAppWeb.RandomController do
  use MyAppWeb, :controller

  import MyApp.Random

  def index(conn, _params) do
    send_resp(conn, 200, random_number())
  end
end
```

Save the file and restart your server 
```
$ mix phx.server
```

Go to the following URL: `http://localhost:4000/api/random`, you should now see a random number every time you refresh/enter the page. 🎉

## Handling CORS

This is a more in-depth topic - but it's necessary if you want other people to use your API or even call your own APi from another port/ip. 

We will use a library called `corsica` for this, lets install it by opening our `mix.exs` file in the root directory. 

Add the following line into your `deps` function
```
{:corsica, "~> 1.2"}
```

Your mix.exs should now look like this

```elixir
defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :myapp,
      version: "0.1.0",
      elixir: "~> 1.13",
      description: "An example Web APi",
      elixirc_paths: elixirc_paths(Mix.env()),
      compilers: Mix.compilers(),
      start_permanent: Mix.env() == :prod,
      aliases: aliases(),
      deps: deps(),
      author: "github.com/vKxni",
      docs: [
        main: "readme",
        extras: [
          "README.md"
        ]
      ]
    ]
  end

  def application do
    [
      mod: {MyApp.Application, []},
      extra_applications: [:logger, :runtime_tools]
    ]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Here !! :)
  defp deps do
    [
      # phoenix server
      {:phoenix, "~> 1.6.10"},
      {:telemetry_metrics, "~> 0.6"},
      {:telemetry_poller, "~> 1.0"},
      {:gettext, "~> 0.18"},
      {:plug_cowboy, "~> 2.5"},
      {:poison, "~> 5.0"},
      {:jason, "~> 1.3"},

      # add this line here
      {:corsica, "~> 1.2"} # <--
    ]
  end

  defp aliases do
    [
      setup: ["deps.get"]
    ]
  end
end
```

Once done, run the following command
```
$ mix deps.get
```
This might take some time depending on your connection. 

### Adding CORS to our Endpoint

Open the file called `endpoint.exs` in your `myapp_web` folder. 
(`lib/myapp_web/endpoint.exs`)

Now in your Endpoint, add the following lines
```elixir
 plug(Corsica,
    max_age: 600,
    origins: "*",
    allow_headers: ["accept", "content-type", "authorization"],
    allow_methods: ["GET", "POST"],
    allow_credentials: true,
    log: [rejected: :error, invalid: :warn, accepted: :debug]
  )
```
Your `endpoint.exs` should now look like this:
```elixir
defmodule MyAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :xadara

  @session_options [
    store: :cookie,
    key: "_myapp_key",
    signing_salt: "g10HT1jr"
  ]

  # our CORS config starts here
  plug(Corsica,
    max_age: 600,
    origins: "*",
    allow_headers: ["accept", "content-type", "authorization"],
    allow_methods: ["GET", "POST"],
    allow_credentials: true,
    log: [rejected: :error, invalid: :warn, accepted: :debug]
  )
  # CORS ends here

  plug Plug.Static,
    at: "/",
    from: :myapp,
    gzip: false,
    only: ~w(assets fonts images favicon.ico robots.txt)

  if code_reloading? do
    plug Phoenix.CodeReloader
  end

  plug Plug.RequestId
  plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint]

  plug Plug.Parsers,
    parsers: [:urlencoded, :multipart, :json],
    pass: ["*/*"],
    json_decoder: Phoenix.json_library()

  plug Plug.MethodOverride
  plug Plug.Head
  plug Plug.Session, @session_options
  plug MyAppWeb.Router # this here is important too
end
```
If something looks a bit different, don't worry, just add the new PLUG right below the `session_options`. 

And you are done! Congratulations.