DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

vKxni
vKxni

Posted on

REST APi in Phoenix

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
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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.

defmodule MyAppWeb.RandomController do
  use MyAppWeb, :controller

  def index(conn, _params) do
    send_resp(conn, 200, "hello world")
  end
end
Enter fullscreen mode Exit fullscreen mode

Save this file and now try to start your server.

$ mix phx.server
Enter fullscreen mode Exit fullscreen mode

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:

defmodule MyApp.Random do
  def random_number do
   Enum.random(100..10000)
  end
end
Enter fullscreen mode Exit fullscreen mode

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

defmodule MyAppWeb.RandomController do
  use MyAppWeb, :controller

  import MyApp.Random

  def index(conn, _params) do
    send_resp(conn, 200, random_number())
  end
end
Enter fullscreen mode Exit fullscreen mode

Save the file and restart your server

$ mix phx.server
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

Your mix.exs should now look like this

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
Enter fullscreen mode Exit fullscreen mode

Once done, run the following command

$ mix deps.get
Enter fullscreen mode Exit fullscreen mode

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

 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]
  )
Enter fullscreen mode Exit fullscreen mode

Your endpoint.exs should now look like this:

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
Enter fullscreen mode Exit fullscreen mode

If something looks a bit different, don't worry, just add the new PLUG right below the session_options.

And you are done! Congratulations.

Top comments (0)

Layoffs: It’s Okay to Not Be Okay