Getting Started

Installation

In this section, we will install all the dependencies for the Phoenix framework including installation of Elixir and Erlang. We will cover installation instructions for macOS and Ubuntu/Debian here. However, Phoenix can run in several flavors of Linux as well as in Microsoft Windows. For installation instructions related to other platforms, check out the official installation page for Elixir and Phoenix.

Homebrew for macOS

We will be using the Homebrew package manager for installing all the Phoenix dependencies for macOS. So if you don’t have Homebrew installed, go to https://brew.sh and copy paste the install script on your terminal to install it. While Homebrew is not a prerequisite for Phoenix or Elixir, it helps us to install everything we need from the comfort of the command line.

Installing Elixir and Erlang

on macOS
→ brew install elixir

The above command installs the latest stable version of Elixir along with Erlang. At the time of writing, this installs Elixir 1.4 which is what we need.

On Ubuntu and Debian

We need to install Erlang and Elixir individually.

Download the Erlang package and install it.

→ wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
→ sudo dpkg -i erlang-solutions_1.0_all.deb
→ sudo apt-get update
→ sudo apt-get install esl-erlang

Now you can install Elixir using:

→ sudo apt-get install elixir

Installing Postgresql

Phoenix projects by default are configured to work with the Postgresql database, though, any database that is supported by the Ecto library will work. We can also use Phoenix without any database if we need to. In this book, we will use the Postgresql database as it enjoys good support in the Elixir community. In addition to being well supported it also has features not provided by other databases that we will require later.

on macOS
→ brew install postgresql

Once installed, Brew will print post installation notes as shown below:

...
To have launchd start postgresql now and restart at login:
  brew services start postgresql
Or, if you don't want/need a background service you can just run:
  pg_ctl -D /usr/local/var/postgres start

Be sure to run either of the commands shown above as per your need. Normally, I run brew services start postgresql so that I can forget about starting the Postgres server every time I restart my system.

Brew installs Postgresql and creates a default super admin. The name of this super admin is the same as the name of your currently logged-in system account name while the password is left empty. We could just go with that. However, for every Phoenix project that we create, we need to modify the configuration file to give the right credentials for the Postgres database.

Phoenix projects by default expect the Postgres admin username to be postgres with the password postgres. We can save a few key strokes for every new project by creating a Postgres user as expected by Phoenix. Login to Postgres shell by typing psql -d postgres on your terminal.

On the Postgres console, type the following commands to create a super user by name postgres with the password postgres.

postgres=# CREATE USER postgres;
postgres=# ALTER USER postgres PASSWORD 'postgres';
postgres=# ALTER USER postgres WITH SUPERUSER;
postgres=# \q

On Ubuntu and Debian

Install postgresql and postgresql-contrib through apt-get as shown below:

→ sudo apt-get install postgresql postgresql-contrib

Ubuntu installation of Postgres creates a default super admin in the name of postgres but doesn’t set the password. So you might want to set the password to postgres on your local machine to match the default settings for Phoenix projects. Login to Postgres shell by typing sudo -u postgres psql postgres on your terminal.

postgres=# ALTER USER postgres PASSWORD 'postgres';
postgres=# \q

Nodejs

Phoenix projects by default are setup to use Brunch — a nodejs tool for the management of asset files. In the development environment, all static files like images, fonts, CSS and Javascript files get processed by Brunch before Phoenix serves them. In the production environment we can use the precompiled files from our development machine without having to use Brunch on the production server.

Even in the development environment Brunch is not a hard dependency — meaning if we don’t want Brunch we can simply remove a simple configuration line in our project and still have Phoenix continue to work. Or we could replace Brunch with other tools like Webpack if we prefer. We will install Nodejs as we will use Brunch for managing the asset files in the development environment.

on macOS
→ brew install node
On Ubuntu and Debian
→ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
→ sudo apt-get install -y nodejs

inotify-tools (only for linux)

In the development environment, Phoenix provides live reloading feature that automatically refreshes our browser tabs whenever we change the source code in our project. On linux machines, this feature is dependent on inotify-tools while it’s not needed on macOS or windows.

Install inotify-tools using the command shown below:

→ sudo apt-get install inotify-tools

Hex Package Manager

Hex is the package manager for Elixir libraries. It allows us to upload/download Elixir libraries from the https://hex.pm website.

→ mix local.hex

Rebar - Erlang Build Tool

rebar and rebar3 are Erlang build tools for compiling and testing Erlang applications. Since our Phoenix project will depend on some of the Erlang libraries, we need Rebar to be installed on our machine.

→ mix local.rebar

mix is a command line binary that gets installed as part of the Elixir language installation. Since this is a sub-command of mix, a binary installed in our system as part of installing Elixir, the above command to install hex and rebar is same for all OS.

Phoenix Project Generator

And finally to install the Phoenix project generator, run the following command:

→ mix archive.install https://github.com/phoenixframework/archives/raw/master/phx_new.ez

It’s important to note that the above command installs the Phoenix project generator and not Phoenix itself. After installing the Phoenix generator, all that we get is a new sub command to mix, to create a new Phoenix project. The Phoenix framework itself gets installed within our project directory under the deps folder when we run the mix deps.get command which we will see shortly.

Validating the Installation

Run the following commands on the terminal to confirm you have installed the correct version of Elixir, Phoenix the required dependencies.

Elixir
→ elixir -v
Erlang/OTP 19 [erts-8.2] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.4.0

Elixir version should be 1.4.0 or higher.

Phoenix
→ mix help | grep 'phx.new'

mix phx.new           # Creates a new Phoenix v1.3.0 application

Phoenix version should be 1.3.0 or higher.

PostgreSQL
→ psql --version

psql (PostgreSQL) 9.6.1

PostgreSQL version should be 9.4 or higher.

Nodejs
→ node -v

v7.2.1

Node version should be 5.0.0 or higher.

Code Editor

For a good developer experience, we will need a code editor that supports syntax highlighting and auto code completion for Elixir. There are several text editors that support the Elixir language. My personal choice is the Atom editor.

If you are a die-hard Emac or Vim fan, you might want to check out Spacemacs. For the rest of us, Atom works as a good editor for working with Elixir projects.

If you go with Spacemacs, do install the excellent Alchemist package for Elixir. It provides syntax highlighting, code completion and several other features that help your development workflow with Elixir projects. If you are on Atom, you can install language-elixir and atom-elixir packages to do similar stuff in Atom. The Atom packages can be installed from the terminal with a single command.

→ apm install language-elixir atom-elixir

If you are not a fan of either of these editors, pick up one that suits your taste, but make sure that it at least supports syntax highlighting for Elixir.

Create a New Phoenix project

In this section, we will quickly run through the steps required to create a new Phoenix project and add a new page to it. This exercise is to quickly glance at the various moving parts of Phoenix and not to master them. So don’t worry if you find it difficult to understand the code or if all this seems too fast paced. We will spend adequate time in the rest of the book to build understanding.

Let’s create a new Phoenix project using the mix generator command phx.new. It is the new sub command to mix binary that gets added when we installed the Phoenix generator in section 1.1.8.

Open up your terminal and run the following code.

→ mix phx.new learn_phoenix

This command creates a new folder learn_phoenix in your present working directory. In my case, my pwd is ~/pio and the new Phoenix project is initialized in ~/pio/learn_phoenix folder.

You will be prompted to either download the dependencies or skip them as below.

Fetch and install dependencies? [Yn]

Choose "Y" to download the dependencies. The installer will then proceed to download all required Elixir libraries from https://hex.pm (the official package manager site for Elixir projects). It will also download nodejs packages from https://npm.org, specifically Brunch and its friends which are used for assets management in Phoenix.

* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build

We are all set! Run your Phoenix application:

    $ cd learn_phoenix
    $ mix phx.server

You can also run your app inside IEx (Interactive Elixir) as:

    $ iex -S mix phx.server

Before moving on, configure your database in config/dev.exs and run:

    $ mix ecto.create

For now, it will suffice to know that the mix phx.new command has downloaded several Elixir libraries in your project’s deps folder and also downloaded several nodejs packages in assets/node_modules.

Note
It’s important to note that all dependencies are installed in deps folder inside our project folder including phoenix i.e., the Phoenix framework is installed locally per project and not system-wide.

However, the Phoenix new project generator that we installed in the previous section is a system-wide installation that gives us the command mix phx.new.

 new project bb07d

The contents of the assets/node_modules directory is too big to show here in full and not very relevant to what we are learing.

Now run mix phx.server from inside the project directory.

~/pio/learn_phoenix
→ mix phx.server

This will take some time as Elixir compiles all the Elixir source code from our project and its dependencies into .beam files. Once compiled, you will see the following message on your terminal.

 new project 9fa71

There are several error messages and then a line that reads

[info] Running LearnPhoenixWeb.Endpoint with Cowboy using http://0.0.0.0:4000

Let’s ignore those errors for now and open our browser to http://localhost:4000. We will be greeted with Phoenix welcome page:

 new project 5a103

Now back to the errors that we saw in the terminal. Our application is trying to connect to a Postgres database which doesn’t exist.

Let’s fix this error. Hit CTRL+c twice in the terminal running Phoenix server to stop it. Open config/dev.exs file in your editor and check if the Postgres login credential given are correct and Phoenix has the privilege to create a new Postgres database.

config/dev.exs
config :learn_phoenix, LearnPhoenix.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: "postgres",
  password: "postgres",
  database: "learn_phoenix_dev",
  hostname: "localhost",
  pool_size: 10

If our local PostgreSQL username is postgres and password is supersecret, we need to change the database configuration in config/dev.exs file (shown above) to reflect the same.

config/dev.exs
# Change this only if your credentials are different
...
  username: "postgres",
  password: "supersecret",
...

However, if you followed the instructions to create Postgres superuser as previously explained in section 1.1.3, you don’t have to make any change in the config/dev.exs file.

After the database connection details have been configured correctly, we can now run mix ecto.create to create a database for our Phoenix app and then run mix phx.server. This time we won’t see those db connection errors as our Phoenix application is now able to connect to the database.

~/pio/learn_phoenix
→ mix ecto.create
→ mix phx.server

Phoenix File Structure

Our learn_phoenix directory looks like this:

 structure aec72

Assets and Priv

The top level directory assets contains css, js, images, fonts and any other static files within respective sub folders. While the assets directory contains the original source code for our assets, the priv directory contains the compiled version of the same asset files. The priv directory is also the place where translation files and database migration files get stored. We will dig into these files more in the coming chapters.

What is priv?

"priv" is like "OTP" where its name made sense at the beginning but today it has grown beyond that. All it matters now is that we put in the "priv" directory any artifact that you need in production alongside your code.

— José Valim

Config

The config directory contains files that store various configurations for our project like the database credentials, port number of the web server etc. Phoenix projects by default are configured to support three different environments — dev, test, prod.

Configuration files for each of these environments are present in the config directory as shown below:

 structure 87e23

Test

The test directory contains the various test code for our projects. We will create files in this directory when we start with Test Driven Development.

Lib

The lib folder contains our project’s main code.

 structure 8ee1f

We will spend most of our time within the lib directory. All code that is specific to our project belongs here. This directory contains two subdirectories: first with the same name as our project and the second with a web suffix to our project name i.e., lib/learn_phoenix and lib/learn_phoenix_web

The learn_phoenix_web directory is the home of the web interface specific code for our project. That is, the learn_phoenix_web directory contains code for starting web server, defining routes, controllers, HTML templates etc.

All non-web related code go into the lib/learn_phoenix directory. This includes the context and schema files for our project. We will look into these in detail in Chapter 3.

Note

Module names are nested in Phoenix just like the folder structure. From now on, we will refer to long module names such as LearnPhoenixWeb.PageController by their base name i.e, PageController in this case, unless there is any ambiguity in understanding similarly ending module names. We will continue retaining the full name in code examples.

Control Flow in Phoenix

We haven’t done anything with our new Phoenix project yet. Let’s try to understand how the welcome page works and then mimic the same to create a new page at http://localhost:4000/about.

My preferred way to read an MVC web app is to start at its router file. This approach makes it easy to understand the control flow of various paths.

The router file for our Phoenix project is at learn_phoenix_web/router.ex.

lib/learn_phoenix_web/router.ex
defmodule LearnPhoenixWeb.Router do
  ...
  scope "/", LearnPhoenixWeb do
    pipe_through :browser

    # => Focus just on this line below and ignore everything else
    get "/", PageController, :index (1)
  end
  ...
end
  1. Router definition for homepage /

If you are familiar with any MVC framework, you might have at least guessed what this line of code means:

get "/", PageController, :index

It basically says, for any request coming to / path (i.e., homepage), call the index function of the PageController. The controller functions that are mapped in the router for a specific path are also called controller actions.

Our PageController defined at learn_phoenix_web/controllers/page_controller.ex looks like this now:

defmodule LearnPhoenixWeb.PageController do
  use LearnPhoenixWeb, :controller

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

The index action receives the control from router and it renders the index.html file.

Again if you are coming from Rails or another MVC frameworks, you might guess that this file exists in the views folder. However, it turns out that our learn_phoenix_web/views folder does not have any html template files. Rather it contains a file called page_view.ex which defines PageView module.

Looking around in the other generated directories, it’s easy to find the index.html.eex. It lives inside the directory learn_phoenix_web/templates/page/. The contents of the file also confirms that it’s the same as the one that we see in the homepage.

Note
Naming conventions

The render function within the PageController knows where to find the template index.html in our project directory by following the Phoenix convention.

  1. The learn_phoenix_web.ex file present in our project folder at lib/ defines the template root folder as learn_phoenix_web/templates.

  2. When the render function in the controller is given a template name without any additional clue, Phoenix tries to find the appropriate View module by adding the View suffix to the current controller’s name.

    Our controller name in this case is PageController so Phoenix will look for the View module in the name PageView.

  3. The View modules know where to find the template files by looking into the subdirectory named after the View module inside the template’s root path.

    Since our View module name is PageView, Phoenix will look for templates in the folder learn_phoenix_web/templates/page/

At this point it might be difficult to understand the purpose of PageView module as it is almost empty.

lib/learn_phoenix_web/views/page_view.ex
defmodule LearnPhoenixWeb.PageView do
  use LearnPhoenixWeb, :view
  # nothing here
end

While it’s tempting to conclude that PageView does nothing, that is not correct. To understand it, let’s change the render function call in our PageController module as below:

defmodule LearnPhoenixWeb.PageController do
  use LearnPhoenixWeb, :controller

  def index(conn, _params) do
    render conn, "index_new.html" # Change the template name
  end
end

Now if we visit the homepage, we are obviously greeted with an error. This error is however helpful to understand what is happening under the hood.

 control 8beed

A layman’s translation of the error message is below:

  • I looked for a render/2 function in LearnPhoenixWeb.PageView but I couldn’t find it.

  • I then looked for a template index_new.html.eex inside learn_phoenix_web/templates/page and I couldn’t find it either.

  • So I’ve no other option except throwing this error.

Note

It’s common to see function names referred to in the format render/2 as shown in the error message above.

The number after / refers to the number of arguments that the function accepts, which is called the function’s arity.

Now instead of creating a new template index_new.html.eex in the template folder, let’s create a new function render/2 in our PageView module as below:

lib/learn_phoenix_web/views/page_view.ex
defmodule LearnPhoenixWeb.PageView do
  use LearnPhoenixWeb, :view

  def render("index_new.html", _) do
    {:safe, """
    <h1>Hello World!</h1>
    <p>This template is rendered from PageView</p>
    """}
  end
end

Now going back to our homepage, we should see the new message displayed as given in our PageView module.

 control dd9f7

We will now remove this render function from the PageView module and add just the html to a new index_new.html.eex file inside learn_phoenix_web/templates/page. Back to the browser, the result is the same.

So what is happening here? It’s Phoenix magic powered by Elixir’s metaprogramming feature.

When we call render(conn, "index_new.html") from our PageController we are calling a function defined in the Phoenix.Controller module. It then calls render("index_new.html", _) defined inside our PageView module. But we have just removed this function from the module. So it’s not there to be called and we expect an error.

Here is the nice little trick that Phoenix does. During compilation process, it looks for any template files in the template folder ending in .eex suffix. It then takes out the contents of each of these files and creates a render/2 function in the respective View modules similar to what we have created.

Why does Phoenix do this?

That little trick aside, why does Phoenix do this? Having HTML in a separate template file it is easy to manage but reading and rendering it for every page request involves a disk-read which is slow. Having the HTML code inside our function definition is ugly and not easy to maintain but a function call is faster than a disk read. Can we have the best of both worlds? The speed of a function call and at the same time the clarity and ease of a template file?

That’s pretty much the goal of this Phoenix trick. It allows you to write HTML in template files then converts it to a function during the compilation process. This also explains why we need the PageView module even though it looks empty; because it’s this module where the template eventually gets converted into a function.

To summarize, the sequence of events happening when you visit http://localhost:4000/ are:

  • The router checks if the incoming request path is for / and calls the index action in the PageController module.

  • The index action on the controller calls the Phoenix.Controller.render(conn, "index.html") function. Since the module Phoenix.Controller is imported in all controllers, we are calling the function render without the module name prefix. If you are new to Elixir and don’t know what import is, you will learn it in the next Chapter.

  • Phoenix.Controller.render/2 calls a similarly named render/2 function in PageView containing the compiled code from the template file index.html. The result of this function call is then displayed on the browser.

Now armed with this knowledge, let’s create a new page at /about. Based on knowledge gained so far, we know we need to do the following to complete our task:

  • Router

    • modify router to map the new path /about to a function in a PageController. Let’s call this function about

  • Controller

    • create a new function about/2 in our controller. It should call the render/2 function with the template name about.html

  • View

    • since PageView module already exists, we don’t have to create it again.

    • define a new template about.html.eex inside learn_phoenix_web/templates/page folder and add the HTML content.

Let’s modify the router as below to create a static page at http://localhost:4000/about.

lib/learn_phoenix_web/router.ex
scope "/", LearnPhoenixWeb do
  pipe_through :browser

  get "/", PageController, :index  # => Add this line below.
  get "/about", PageController, :about
end

Modify our PageController and add a new function as below:

lib/learn_phoenix_web/controllers/page_controller.ex
defmodule LearnPhoenixWeb.PageController do
  use LearnPhoenixWeb, :controller

  (...)

  def about(conn, _params) do
    render conn, "about.html"
  end
end

Inside our learn_phoenix_web/templates/page, create a new file about.html.eex and add some HTML data.

<!-- web/templates/page/about.html.eex -->
<h1>About</h1>
<p>Some content</p>

Now back to the terminal and run mix phx.server if it’s not already running. Let’s open http://localhost:4000/about in our browser to see the message we added in our template file.

 control 81c81

We have now learned how the request for a web page flows through various parts of our Phoenix app and how to create a new static page in our application.

Summary

Let’s recap on the key concepts that we learnt in this chapter.

We learnt to install the various dependencies of the Phoenix Framework for macOS & for Ubuntu/Debian OS. We also installed the Phoenix project generator which we use by issuing the command mix phx.new projectname

As with any other Elixir project, we learnt that the Phoenix framework is installed as one of the dependencies of our project inside the deps folder. Hence all Phoenix projects are self-contained with a version of Phoenix installed inside each project.

We also configured the database connection, got to know a few mix tasks and understood the file structure of Phoenix.

Then we moved on to understanding how a page request is served by Phoenix. We did this by looking at the various interconnected critical components of our project including the Router, Controller, View and Templates.

Along with this, we also understood the inherent naming conventions and how Phoenix automatically identifies and links Controllers, Views and Templates.

Finally, we flexed our muscle by creating a new static page and getting it served successfully.