Introduction to The Phoenix Framework & How It Works in Action

Phoenix Framework

Today I’d like to give you a brief introduction to the Phoenix framework and demonstrate it in action. Phoenix is a relatively new server-side framework (compared to Rails or Django) written in Elixir. It boasts great performance and fault-tolerance so it is getting quite popular these days.

In this article, we will talk about the basic components of Phoenix and create a very simple web application so that you can get a sense of how Phoenix applications look like.

The source code for this article can be found on GitHub.

So, Phoenix?

Yeah, Phoenix. It is a modern server-side web framework written in Elixir (which, in turn, runs on an Erlang virtual machine) built with Model-View-Controller pattern in mind. Many of its concepts are very similar to the ones presented in Rails or Django, so if you are familiar with any one of those, you’ll get the basics of Phoenix very fast. “Why do we need yet another MVC framework?”, you might ask. Well, because Phoenix offers both productivity, stability and performance at the same time. Some developers blame Rails to be slow: that’s not the case with Phoenix which can serve countless number of clients with ease while providing nice fault tolerance. This is possible thanks to Elixir’s magic that has features like concurrency, monitors and supervisor trees out of the box. Therefore, if you’ve never tried using Phoenix, I really recommend doing so.

Unfortunately, Phoenix also has some downsides as well. One problem is that Phoenix’s community is rather small (though, very friendly!) and, consequently, package ecosystem is somewhat poor at the moment. This is because Phoenix is not as mature and popular as Rails/Django yet. There are quite a few Phoenix resources on the net and (probably even worse) there are not many third-party libraries (called packages) available. For example, if you need to implement authentication in Rails, there are a lot of solutions out there. Moreover, each of them has its own philosophy so you may either pick a full-fledged solution (like Devise) or something barebones (like AuthLogic). With Phoenix you have about three solid options but unfortunately at least one of them is not that actively maintained and has quite a lot of issues. Still, I have to say that the community tends to grow so new cool solutions will inevitably appear.

Another thing that may potentially become a problem for beginner developers is the need to learn Elixir language itself that does have a bunch of rather complex points. For example, this is a functional language with immutable data and, of course, without any classes, inheritance and that stuff. As long as many developers start their journey by learning some OOP language, getting the grasp of functional programming may take some time. Also, learning OTP basics can be quite painful, though it does not require you to be a computer genius. On the other hand, studying a new programming paradigm is really useful and can change your attitude towards the development process quite significantly.

All right, enough with introductions — let’s quickly discuss the major components of a Phoenix application!

Major Components

Each Phoenix application typically has the following parts:

  • Endpoint which is the alpha and omega of any request lifecycle. It provides special plugs to apply to request and handles it until the router kicks in.
  • Router forwards the request to the proper controller and action based on the defined rules. Also, it provides special helpers to generate route paths and also performs some additional work.
  • Controllers are composed of actions that handle requests, prepare the data to pass into the views, invoke the proper views or perform redirects. All in all, you can say that controllers are thin layers between models and views.
  • Views act as presentation layers and render the corresponding templates. Note that in Phoenix, views are modules that define helper functions and decorate data; they are not files with some HTML or JSON (like in Rails, for example).
  • Templates are the files with the actual contents that is included in the response. For example, a template may contain HTML with dynamically substituted data (which is prepared by the controller).
  • Channels are needed to manage sockets which allow to establish realtime communication between clients and a server using persistent connections.
  • PubSub is needed for the channels to function properly. For instance, it allows clients to subscribe to various topics.

That’s pretty much it about major Phoenix components. As you see, most of them are quite common and you have probably met them before (if you have prior experience with web frameworks, of course). Now let’s proceed to the next section and try to create a very simple application in order to see Phoenix in action!

Installing Phoenix

Installing Phoenix is not a complex task. You’ll need to perform the following steps:

  • Install Erlang 18 or later (as Elixir runs on Erlang VM). All major operating systems are supported so you should not have any difficulties
  • Install Elixir 1.4 or higher. All major operating systems are supported as well.
  • Install Hex package manager by running mix local.hex command in your terminal (make sure that Erlang and Elixir’s executables are present in the PATH)
  • Install Phoenix itself by running mix archive.install

This is it! Phoenix, however, does have some optional dependencies that you will probably want to install as well:

  • NodeJS. Well, actually, the Node Package Manager, but as long as NPM requires Node, you’ll have to install them both. NPM, in turn, is required by (that compiles assets like JS or CSS) to download its dependencies.
  • Database management system: PostgreSQLMySQLMicrosoft SQL, or MongoDB. Of course, you may create an application without a database at all, but in this demo we’ll try integrate our app with Postgres, so install it to follow along. Phoenix also supports SQLite3 but this solution is suitable only for the development environment.
    Once you have everything installed on your PC, proceed to the next section where we are going to bootstrap our first Phoenix application.

Our First Phoenix App

So, after having installed everything, run the following command:
mix phoenix_sample
It is going to create a new application Named phoenixsample and prepare its skeleton for us. Note that the app’s name must be written in lowercase. Code generator is going to ask you whether you’d like to install all the dependencies, so type Y and press Enter.
Our new project is going to be created with a Postgres support by default but if you’d like to stick with, say, MySQL, simply provide the –database mysql option when running the script. In order to be able to boot the project, we have to provide some configuration for our DBMS. Open the config/dev.exs file, scroll to the very bottom and find the following piece of code:

config :phoenix_sample, PhoenixSample.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "user",
  password: "secret",
  database: "phoenix_sample_dev",
  hostname: "localhost",
  pool_size: 10

Tweak all these parameters as needed and then run:

mix ecto.create

This is going to create a new database for you. After that start the server by running:

mix phx.server

Next, navigate to http://localhost:4000 and make sure that Phoenix welcoming page is being displayed.

Creating a Simple Page

Controller, View, and Template
Now that we have bootstrapped our application, let’s add some components to it. First of all, let’s create a new controller with a single index action that is going to invoke the corresponding view and render a given template.

Controllers should be placed to the lib/phoenix_sample_web/controllers folder, so create a new albums_controller.ex file inside:

defmodule PhoenixSampleWeb.AlbumsController do
  use PhoenixSampleWeb, :controller

  def index(conn, _params) do
    render conn, "index.html"

That’s an Elixir module with an index/2 function. This function, in turns renders an index.html template that will be created in a moment. conn here is a special parameter that contains information about the request. _params contains request parameters.

Next, we’ll require a view. It should be placed under the lib/phoenix_sample_web/views folder, so create a new albums_view.ex file there:

defmodule PhoenixSampleWeb.AlbumsView do
  use PhoenixSampleWeb, :view

Note that the first parts of the view’s name and controller’s name must match.
Lastly, create an index.html.eex template inside the lib/phoenix_sample_web/templates/albumsfolder:


The contents of this template will be interpolated into a generic layout which can be found in the lib/phoenix_sample_web/templates/layout folder. This layout defines the basic structure of the HTML page and has all the necessary tags like htmlbody, and others.

The last thing we have to do in order to see our first page in action is create a new route. All routes can be found in the lib/phoenix_sample_web/router.ex file.

defmodule PhoenixSampleWeb.Router do
  use PhoenixSampleWeb, :router

  pipeline :browser do
    plug :accepts, ["html"]
    plug :fetch_session
    plug :fetch_flash
    plug :protect_from_forgery
    plug :put_secure_browser_headers

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

  scope "/", PhoenixSampleWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index

There is a lot going on here and we won’t discuss everything in-depth (as it’ll take a lot of time), but let me note some main things:

  • pipeline :browser defines a set of behaviour and transformations that should be applied to a request. Inside, there are set of plugs that are executed in a given order and perform some operation to the request. This pipeline is then referenced with a pipe_through :browser line of code.
  • scope “/”, PhoenixSampleWeb do acts like a namespace. You may create a new scope, for example /api. In this case, all routers will be nested under the /api namespace and you’ll have something like /api/users or /api/comments/new.
  • get “/”, PageController, :index is the route created by default. It means that whenever a GET request arrives to the root URL, it should be forwarded to the PageController, index action.

Now let’s create a new route right above the get “/”, PageController, :index line:

 # ... 

  scope "/", PhoenixSampleWeb do
    pipe_through :browser # Use the default browser stack

    get "/albums", AlbumsController, :index # <---
    get "/", PageController, :index

That’s it! You may now visit the http://localhost:4000/albums path and make sure the page is displayed properly. In the terminal, you should see an output similar to this one:

[info] GET /albums
[debug] Processing with PhoenixSampleWeb.AlbumsController.index/2
  Parameters: %{}
  Pipelines: [:browser]
[info] Sent 200 in 0┬╡s

Also note that Phoenix has auto-reloading feature enabled in development. So, if you make some changes to a template, for example, the web page will be reloaded for you automatically and reflect these changes.

Persisting Data
Next, I would like to introduce an ability to persist data into the previously created database. And in order to do this, we need a new table. Of course, you can go ahead and create it manually using PGAdmin or the PSQL command line tool, but that wouldn’t be very convenient. On top of that, if you are going to deploy your project to a hosting provider, you’ll have to create the same table again. To overcome this problem, a concept of migrations was introduced in modern frameworks. Migrations are files with instructions explaining what operations should be applied to the database. With migrations you can, for example, easily create, modify and delete tables using a single command. On top of that, migrations can be rolled back to cancel an applied operation.

Let’s generate a new migration and the corresponding schema using the following command:

mix phx.gen.schema Album albums name:string singer:string track_count:integer

I have specified the following options:

I have specified the following options:

  • Album is the name of the schema (we’ll talk about schemas in a moment)
  • albums is the name of the table that we will create
  • name:string and singer:string means that I’d like to add name and singer fields with a type of string
  • track_count:integer – create a track_count field with a type of integer

After this command finishes its job, a new migration priv\repo\migrations\20180313160658_create_albums.exs will be created. The numbers in the filename will be different for you — that’s a timestamp specifying when exactly this migration was generated.

Take a look at the newly created file:

defmodule PhoenixSample.Repo.Migrations.CreateAlbums do
  use Ecto.Migration

  def change do
    create table(:albums) do
      add :name, :string
      add :singer, :string
      add :track_count, :integer



You can easily understand what’s going on here. We are creating an albums table with three fields of the specified type. timestamps() here means that the insert_at and updated_at fields should also be added to the table. These fields are updated automatically when a record is created and updated.

Now apply the migration in the following way:

mix ecto.migrate

You’ll see an output similar to this one:

[info] == Running PhoenixSample.Repo.Migrations.CreateAlbums.change/0 forward
[info] create table albums
[info] == Migrated in 0.0s

It means that the table was created!

Now let’s say a couple of words about the schema that can be found in the lib\phoenix_sample\album.ex file. Basically, schemas are used to map the Elixir values to some external data source, and vice versa. Also, they can be used to establish relationships with other schemas (for example, an album may have many tracks). Also, schema defines data validations. Our schema has the following contents:

defmodule PhoenixSample.Album do
  use Ecto.Schema
  import Ecto.Changeset

  schema "albums" do
    field :name, :string
    field :singer, :string
    field :track_count, :integer


  @doc false
  def changeset(album, attrs) do
    |> cast(attrs, [:name, :singer, :track_count])
    |> validate_required([:name, :singer, :track_count])

schema “albums” defines structure of our data.

changeset is a special function that defines which transformations should be applied to the data before it is persisted. validate_required, as you have probably guessed, defines validation rules. Specifically, it says that all three fields must have some value, otherwise the record won’t be persisted. Of course, you may introduce other validation rules as needed.

That’s pretty much it! Let’s try to insert some value into the albums table.

Adding Some Data
To make things simple, we are not going to code a separate HTML form allowing to create albums. Instead, let’s run a special Elixir console where we can manipulate our data and perform other operations:

iex -S mix

Type the following code:

PhoenixSample.Repo.insert(%PhoenixSample.Album{name: "Reload", singer: "Metallica", track_count: 13})

This is going to fire an INSERT operation and create a new album with the provided attributes. If one of the attributes is not set, the transaction will be rolled back according to the validation rules specified in the previous section. If everything is okay, you’ll see the following output:

[debug] QUERY OK db=32.0ms
INSERT INTO "albums" ("name","singer","track_count","inserted_at","updated_at") VALUES ($1,$2,$3,$4,$5) RETURNING "id" ["Reload", "Metallica", 13, {{2018, 3, 13}, {16, 13, 44, 267000}}, {{2018, 3, 13}, {16, 13, 44, 269000}}]
   __meta__: #Ecto.Schema.Metadata<:loaded, "albums">,
   id: 1,
   inserted_at: ~N[2018-03-13 16:13:44.267000],
   name: "Reload",
   singer: "Metallica",
   track_count: 13,
   updated_at: ~N[2018-03-13 16:13:44.269000]

Note that the inserted_at and updated_at fields were populated automatically. On top of that, there is an id field which is assigned with a value of 1. This field is also added automatically, and it is marked as a primary key with autoincrement. Of course, it is possible to use some other field as a primary key.

Great! The data is persisted, so why don’t we tweak our controller action to display them?

Rendering Albums

Render to the AlbumsController and modify the albums_controller.ex file in the following way:

defmodule PhoenixSampleWeb.AlbumsController do
  alias PhoenixSample.{Repo, Album} # <--- 1
  use PhoenixSampleWeb, :controller
  import Ecto.Query # <--- 2

  def index(conn, _params) do
    render conn, "index.html",
    albums: Repo.all(from a in Album,
    select: %{:name =>, :singer => a.singer, :tracks => a.track_count}) # <--- 3

There are three key points here:

1. We are creating a new alias for our own convenience, otherwise we would have to write PhoenixSample.Repo and PhoenixSample.Album inside the index function.
2. We are importing Ecto.Query to be able to create complex queries
3. We are setting an albums variable that should be available inside the template. This variable contains all the albums fetched with the help of Repo.all. :select option explains which fields I’d like to fetch. Also, it specifies in what format should the result be returned.

Having this code in place, we may tweak the template. Let’s utilize the Elixir comprehension to traverse the @albums list and display the necessary data on each step:


<%= for album <- @albums do %>
  <p>Name: <%= %></p>
  <p>Singer: <%= album.singer %></p>
  <p>Tracks: <%= album.tracks %></p>
<% end %>

Boot the server again, navigate to the http://localhost:4000/albums page and make sure that the data are displayed properly!

Conclusion: –
That’s all for today, folks. Of course, that was a very high-level overview of the Phoenix framework and we have covered only some introductory topics. Still, I really hope that by now you have at least basic feel of the Phoenix framework.

Phoenix guides can be found at website. It has many examples and very detailed explanations of all basic components and some advanced stuff like testing and deployment process, so be sure to browse it.

Of course, crafting Phoenix applications requires solid knowledge of Elixir language. Eduonix is going to present an “Introduction to Elixir” course in the beginning of May covering basics and more advanced concepts of this language so stay tuned!



Please enter your comment!
Please enter your name here