Building APIs with Phoenix Framework
By: Diogo Souza
Tags: Phoenix Framework, API, Elixir, Phoenix, Api Development

There’s this feeling for many early Elixir developers to avoid getting their hands dirty when it comes to adopting Phoenix for their projects. Unsurprisingly, the number of developers that not only accept Phoenix but also advocate it is rising fast.

But this isn’t for nothing. Phoenix is flexible, robust, and leaves you free to decide in different ways how your APIs will be built.

But, Why Phoenix?

Among many reasons, the main ones that keep Phoenix at the top of the web frameworks battle include:

It was born under the Elixir’s philosophy: MVC-bounded, straightforward, single requests per context, etc.

It’s very complete: it comes with default ORM, router mechanism, testing tools, and an awesome dashboard to show metrics over your app’s health (more on that later).

It is well documented and has a powerful community counting 15.9k stars on GitHub at the moment of this writing.

Whenever you get an error for an API call, for example, Phoenix wants to assure that you’ll receive as much information as necessary to understand the origin of the problem, the code line, the stack trace, the stack of calls, and the available options to fix that.

Error tracking with Phoenix.

Other than that, although Phoenix was primarily designed to embrace the development of monolithic applications, it suits microservices truly well.

No matter the size of your app, you can easily create it, expose the routes, and place it under a clustered-based cloud service, and Phoenix will take care of becoming aware of the new conditions.

What We’re Going to Build Here?

So, we’ve already seen that Phoenix is a great choice for developing your APIs in Elixir. But how?

Let’s explore here the steps to generate a web API to manage a beer catalog.

The goal is for you to understand the aspects that surround a Phoenix API structure, by scaffolding it. This will demonstrate how powerful Phoenix (along with its tools) is.

At the end of the tutorial, that should be the GET /beers endpoint result:

Fetching the list of beers from the Phoenix API.

Setup

First, let’s break down what you’ll need to create this API example. We need to have Elixir properly installed on your machine.

Follow up the official guidelines to get that done. It is as simple as running the command below:

brew install elixir

For this tutorial, we’re making use of version “Elixir 1.11.2 (compiled with Erlang/OTP 23)”. If you don’t have any knowledge of Elixir, I’d recommend going for a previous read over the official Elixir’s Learning Paths.

Also, we’re going to make use of MySQL as the app database. Make sure to install the MySQL Community Server and a GUI Tool to manage it (I particularly like MySQL Workbench the most).

Phoenix uses PostgreSQL as the default database for any new application. Feel free to use it if you’re more comfortable with it. However, for the sake of simplicity, we’re going to stick to MySQL.

You’ll also need an IDE. VS Code is one of the favorites when it comes to Elixir development, and it provides a great extension called vscode-elixir to help you out with your code completion, syntax coloring, etc.

Creating the Project

Now that we have everything installed, let’s create our Phoenix web project. Select a folder on your machine, and run the following command:

mix archive.install hex phx_new

The mix command became automatically available once you’ve installed Elixir. The mix is a build tool to help with creating, managing the dependencies, compiling, and testing Elixir projects.

The command above will install the Phoenix application project to the current directory. Then, run the following command to create the Phoenix project:

mix phx.new beer_catalog

Let’s call it beer_catalog. Once the command finishes, you’ll notice a new folder within the project called lib/beer_catalog. That’s the application main folder.

At the end of the previous command, you’d be prompted to inform if you’d like to fetch and install all the dependencies upfront. Select yes, and the following command will be executed:

mix deps.get

Database Configs

Now, we need to change the default PostgreSQL database to MySQL. For this, we need to first change the Postgres dependency located within the root’s mix.exs file to the following:

# Specifies your project dependencies.

#

# Type `mix help deps` for examples and options.

defp deps do

[

... {:myxql, ">= 0.0.0"}, ...

]

end

Elixir uses the Ecto project as the default ORM for its projects. It provides you with a bunch of repository wrappers, changesets, schema generations, among other features to facilitate the process of dealing with the database.

To accomplish this, Ecto expects to receive the adapter it’ll use for each specific database. In other words, it is the driver Elixir needs to understand how to communicate with that database.

For MySQL, for example, we use MyXQL.

Once we’re done with the settings, we need to run the following command to explicitly ask Elixir to create the provided database into the physical MySQL server:

mix ecto.create

This command will also validate the connection URL and credentials. If anything’s wrong, Pheonix will print the log errors to the console.

When the command finishes, you may see the generated database displayed within Workbench:

Newly created beer_catalog_dev database.

Ecto also uses suffixes to understand which environment is running at a time:

-dev is for the local development executions

-test is for the test executions

No suffix is when you’re dealing with production.

To execute the project you simply need to run the mix phx.server command. Phoenix is going to consider everything that’s not up-to-date. So, make sure to hit “Yes” for whichever dependencies it asks for an update, like rebar3, for example.

When you hit http://localhost:4000/ at the browser, you may see the following screen:

Phoenix Welcome Page.

Beer Schema

Let’s move on to the schema setup. In our case, only one will be needed: the Beer schema. A simple schema can be auto-generated in Phoenix as follows:

mix phx.gen.schema Beer beers brand:string style:string origin:string quantity:integer

This command will consider each one of the subsequent attributes (and their types) to generate the model and the migration script that further persists it to the database.

However, when designing the models of your APIs, make sure to keep everything as clean and organized as possible.

Phoenix provides the concept of Contexts, which define a group of modules that are related in terms of functionalities.

For example, if you think about an entity called User, it usually is not represented alone. A user belongs to one or more accounts. With that in mind, rather than going straight to the User schema creation, Phoenix asks you to stop for a while and think about your models upfront.

Instead of a single User interface, you’d be better off with Account > User grouped interfaces. Right?

For our Beer API, let’s consider that Beer belongs to a Catalog. With that said, we’re going to change the command to the following:

mix phx.gen.context Catalog Beer beers brand:string style:string origin:string quantity:integer

This way, a whole new world of files will be auto-created in an organized way, like:

- lib/beer_catalog/catalog/beer.ex: This is the beer schema definition, and this is the auto-generated code:

defmodule BeerCatalog.Catalog.Beer do

use Ecto.Schema

import Ecto.Changeset schema "beers" do

field :brand, :string

field :origin, :string

field :quantity, :integer

field :style, :string timestamps()

end

def changeset(beer, attrs) do

beer

|> cast(attrs, [:brand, :style, :origin, :quantity])

|> validate_required([:brand, :style, :origin, :quantity])

end

end @doc falsedef changeset(beer, attrs) dobeer|> cast(attrs, [:brand, :style, :origin, :quantity])|> validate_required([:brand, :style, :origin, :quantity])endend

Can you spot the chain of calls at the top of the code listing? Wait, it gets better…

- lib/beer_catalog/catalog.ex: If you open this generated file, you may see a bunch of pre-generated CRUD operations handling the Beer domain. In other words, your basic CRUD is done!

You can customize them as you please, but it’s halfway through. Cool, isn’t it?

The command has also generated the database migration files under the priv/repo/migrations/XXX_create_beers.exs file, as well as some CRUD tests for the schema and catalog’s queries.

Migrating and Seeding the Database

Before we can run the CRUD API, we need to migrate the changes to the physical database. For this, run the following command:

mix ecto.migrate

When it finishes, get back to the Workbench and check out the newly created tables:

Migrated beers table.

Ecto includes two columns for each table to keep tracking when new data is inserted or updated. It also tracks the history of the migrations through the schema_migrations table.

If you’d like to start the API with some data within the database, we can do that through the seeding feature. It allows you to insert data before starting up the application.

For this, go to priv/repo/seeds.exs file and add the following content:

BeerCatalog.Catalog.Repo.insert!(%BeerCatalog.Catalog.Beer{

brand: "Milwaukee's Best Light",

style: "American-Style Light Lager",

origin: "USA",

quantity: 1200

}) BeerCatalog.Catalog.Repo.insert!(%BeerCatalog.Catalog.Beer{

brand: "Dos Equis",

style: "Lager Especial",

origin: "Mexico",

quantity: 430

}) BeerCatalog.Catalog.Repo.insert!(%BeerCatalog.Catalog.Beer{

brand: "Maibock",

style: "Bavarian lager",

origin: "Germany",

quantity: 4000

})

Then, run the command below to perform the seeding:

mix run priv/repo/seeds.exs

Once the command’s done, you may see the data directly into your table:

Seeded data from beer table.

Creating the API Controller

Now, we need to generate the controller structure to interface the database and the REST endpoints.

For this, simply run the following command:

mix phx.gen.json Catalog Beer beers brand:string style:string origin:string quantity:integer — no-context — no-schema

This command generates JSON-based REST resources to handle CRUD operations through dedicated endpoints. The flags are necessary because we’ve already created the schema and context before.

You can spot the generated file under the lib/beer_catalog_web/controllers/beer_controller.ex file. Go ahead and navigate through it.

Finally, we need to map the newly created resource to the default routing system which still does not recognize it.

Open the lib/beer_catalog_web/router.ex file and, under the :api scope setting, add the following line:

resources “/beers”, BeerController, except: [:new, :edit]

It’ll map all the HTTP CRUD operations to the respective functions. That’s it! We’re all set!

Testing

As we said, some integrated tests were already created by Phoenix. To run all the tests and validate if your whole API configuration is ok, execute the following command:

mix test

If everything went ok, move on and test your endpoints via Postman. Make sure to check the tables to see if the operations are being properly persisted.

Conclusion

As a bonus, Phoenix also provides a metrics dashboard called LiveDashboard, by hitting http://localhost:4000/dashboard/home at the console:

Phoenix LiveDashboard preview.

This is a great way to monitor the health of your Phoenix applications, the number of processes, memory usage, framework versions, etc.

Phoenix has proven to be flexible enough to give you freedom in the way you craft your applications. The auto-generation tools are perfect to boost your productivity while giving some nice hints for those who are starting now.

I hope this article helped you to pave your first steps through this amazing framework, and demonstrate its main features. Good luck!