Phoenix is a Web Framework written in Elixir that works similarly to Rails but is faster and more scalable than Rails. This tutorial will show you how to create a simple blog site using Phoenix and some Elixir code. You can find more about Phoenix at www.phoenixframework.org but don’t spend too much time on reading because the correct way to learn Phoenix is by writing applications!

Before installing Phoenix you will need to install some additional packages to create a proper development environment and make your life easier:

$ mix local.hex $ ps ax | grep -i postgr 1 2 $ mix local . hex $ ps ax | grep - i postgr

The first command installs Hex, which is the package manager of Elixir. The second one confirms that PostgreSQL is both installed and up and running; if not, please install it and start the PostgreSQL server process. You can verify that you can successfully connect to the running PostgreSQL server process as follows:

$ sudo -u postgres psql psql (9.5.3) Type "help" for help. postgres=# 1 2 3 4 $ sudo - u postgres psql psql ( 9.5.3 ) Type "help" for help . postgres = #

If PostgreSQL is not running for some reason, you will get an error message similar to the following:

$ psql psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432"? 1 2 3 $ psql psql : could not connect to server : No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.5432" ?

Then, you can use mix to install Phoenix:

$ mix archive.install https://github.com/phoenixframework/ archives/raw/master/phoenix_new.ez 1 $ mix archive . install https : //github.com/phoenixframework/ archives/raw/master/phoenix_new.ez

You can find the version of Phoenix you’re using as follows:

$ mix phoenix.new -v Phoenix v1.2.1 1 2 $ mix phoenix . new - v Phoenix v1 . 2.1

Although it is not absolutely necessary to install and use Node.js for your Phoenix projects, Node.js can be very helpful and is used by Phoenix, so visit http://nodejs.org and install Node.js before continuing with the tutorial. You can find your Node.js version as follows:

$ node --version v4.2.6 1 2 $ node -- version v4 . 2.6

Enough with the dependencies, it is now time to start using Phoenix to create a simple website.

Phoenix project

This project will be the equivalent of the elementary “Hello World!” program that everyone tries out when starting to learn to code. First, you will have to create a new Elixir project using mix as follows:

$ mix phoenix.new hw ... Fetch and install dependencies? [Yn] Y ... $ cd hw $ mix ecto.create $ mix phoenix.server 1 2 3 4 5 6 7 $ mix phoenix . new hw . . . Fetch and install dependencies ? [ Yn ] Y . . . $ cd hw $ mix ecto . create $ mix phoenix . server

The first command creates a new Phoenix project at the current directory, named hw. After executing mix ecto.create you might get some error messages that have to do with the database connection; in that case, edit ./config/dev.exs and make sure that you put the connect username, password and database information near the end of the file. However, for this simple project, this is not necessary.

The mix phoenix.server command starts Phoenix router, which you can also start with the interactive Elixir shell:

$ iex -S mix phoenix.server 1 $ iex - S mix phoenix . server

The previous command starts an HTTP server that usually listens to port number 4000 and is useful for testing your application. The good thing is that it also displays information about user requests:

$ mix phoenix.server [info] GET / [debug] Processing by Hw.PageController.index/2 Parameters: %{} Pipelines: [:browser] [info] Sent 200 in 24ms 1 2 3 4 5 6 $ mix phoenix . server [ info ] GET / [ debug ] Processing by Hw . PageController . index / 2 Parameters : % { } Pipelines : [ : browser ] [ info ] Sent 200 in 24ms

So, if you visit http://localhost:4000/ you will see Phoenix default web page (See below).

The HTML code for displaying Phoenix default webpage is hard coded inside the ./web/templates/page/index.html.eex file. Should you wish to display something different, you should carefully change the contents of this file.

At this point, you usually define your routes, which you can think of as the paths supported by your web application, and write the necessary code so that each URL displays the right output. Because this is a simple project, it will support just one path, which will only display a simple message. So, the contents of router.ex need not be changed.

After learning the basics, it is about time to develop something practical using Phoenix.

Blog it

You will now learn how to create a blog site using Phoenix. In order to create a Phoenix project named “blog”, you will need to execute the following commands

$ mix phoenix.new blog ... Fetch and install dependencies? [Yn] Y ... $ cd blog $ mix do deps.get, compile $ mix local.hex 1 2 3 4 5 6 7 $ mix phoenix . new blog . . . Fetch and install dependencies ? [ Yn ] Y . . . $ cd blog $ mix do deps . get , compile $ mix local . hex

Once again, you will need to edit ./config/dev.exs and insert the correct PostgreSQL information, which will be the subject of the next section.

Talking to a database

Phoenix uses PostgreSQL by default; although it is possible to use another database server if required, it is wise to stay with PostgreSQL because Phoenix has better support for PostgreSQL than for alternatives.

For the purposes of this tutorial the name of the database will be “Technotif” and the name of the user that will be used will be “Technotifuser” with a password of “aPassword” – you will learn more about the tables that are going to be created in a while.

The next thing you should do is execute the following commands from the PostgreSQL shell:

mtsouk=# CREATE USER Technotifuser WITH PASSWORD 'aPassword'; CREATE ROLE mtsouk=# CREATE DATABASE Technotif; CREATE DATABASE mtsouk=# GRANT ALL PRIVILEGES ON DATABASE Technotif to Technotifuser; GRANT mtsouk=# ALTER USER Technotifuser CREATEDB; ALTER ROLE 1 2 3 4 5 6 7 8 mtsouk = # CREATE USER Technotifuser WITH PASSWORD 'aPassword'; CREATE ROLE mtsouk = # CREATE DATABASE Technotif; CREATE DATABASE mtsouk = # GRANT ALL PRIVILEGES ON DATABASE Technotif to Technotifuser; GRANT mtsouk = # ALTER USER Technotifuser CREATEDB; ALTER ROLE

Now put the correct information inside ./config/dev.exs.

That’s it for the database related things, so we can now continue building the project:

$ mix ecto.create && mix ecto.migrate $ npm install $ mix phoenix.server 1 2 3 $ mix ecto . create && mix ecto . migrate $ npm install $ mix phoenix . server

The npm install command installs Node.js dependencies, whereas the mix ecto.create && mix ecto.migrate commands create and migrate your database. This time the mix phoenix.server command should generate no database related errors.

The next command will execute a Phoenix generator that will create some things for us:

$ mix phoenix.gen.html Post posts title:string body:text ... Add the resource to your browser scope in web/router.ex: resources "/posts", PostController Remember to update your repository by running migrations: $ mix ecto.migrate 1 2 3 4 5 $ mix phoenix . gen . html Post posts title : string body : text . . . Add the resource to your browser scope in web / router . ex : resources "/posts" , PostController Remember to update your repository by running migrations : $ mix ecto . migrate

What we did here is declaring the name of a web page in both singular (Post) and plural (posts) as well as the fields of it (title, body) along with their types (string, text).

As the output of the previous command suggests, it is time to add a new route.

Routing is the process that Phoenix has to do so that each HTTP request will be served by the appropriate Elixir code. You will need as many routes as the number of static web pages your project has. If you have dynamic pages, then you will need fewer routes. The project file with the routing information for the blog project is ./web/router.ex. At this point, you will need to add just one route:

$ diff router.ex router.ex.orig 20d19 < resources "/posts", PostController 1 2 3 $ diff router . ex router . ex . orig 20d19 < resources "/posts" , PostController

After this, you will need to execute the following command for changes to take effect in the PostgreSQL part:

$ mix ecto.migrate 1 $ mix ecto . migrate

The following command shows the routing list of our project:

$ mix phoenix.routes Compiling 9 files (.ex) Generated blog app page_path GET / Blog.PageController :index post_path GET /posts Blog.PostController :index post_path GET /posts/:id/edit Blog.PostController :edit post_path GET /posts/new Blog.PostController :new post_path GET /posts/:id Blog.PostController :show post_path POST /posts Blog.PostController :create post_path PATCH /posts/:id Blog.PostController :update PUT /posts/:id Blog.PostController :update post_path DELETE /posts/:id Blog.PostController :delete 1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ mix phoenix . routes Compiling 9 files ( . ex ) Generated blog app page_path GET / Blog . PageController : index post_path GET / posts Blog . PostController : index post_path GET / posts / : id / edit Blog . PostController : edit post_path GET / posts / new Blog . PostController : new post_path GET / posts / : id Blog . PostController : show post_path POST / posts Blog . PostController : create post_path PATCH / posts / : id Blog . PostController : update PUT / posts / : id Blog . PostController : update post_path DELETE / posts / : id Blog . PostController : delete

You now have a fully working blog site and you are allowed to stop here.

But why not go further? The following section will briefly show how to add support for comments.

Adding comments

Making it possible to add comments to your blog posts is not as difficult as it might sound. First you will need to execute two commands:

$ mix phoenix.gen.model Comment comments name:string content:text post_id:references:posts * creating web/models/comment.ex * creating test/models/comment_test.exs * creating priv/repo/migrations/20160813080840_create_ comment.exs Remember to update your repository by running migrations: $ mix ecto.migrate $ mix ecto.migrate Compiling 1 file (.ex) Generated blog app 11:08:55.467 [info] == Running Blog.Repo.Migrations. CreateComment.change/0 forward 11:08:55.467 [info] create table comments 11:08:55.472 [info] create index comments_post_id_index 11:08:55.474 [info] == Migrated in 0.0s 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ mix phoenix . gen . model Comment comments name : string content : text post_id : references : posts * creating web / models / comment . ex * creating test / models / comment_test . exs * creating priv / repo / migrations / 20160813080840_create_ comment . exs Remember to update your repository by running migrations : $ mix ecto . migrate $ mix ecto . migrate Compiling 1 file ( . ex ) Generated blog app 11 : 08 : 55.467 [ info ] == Running Blog . Repo . Migrations . CreateComment . change / 0 forward 11 : 08 : 55.467 [ info ] create table comments 11 : 08 : 55.472 [ info ] create index comments_post_id _ index 11 : 08 : 55.474 [ info ] == Migrated in 0.0s

The post_id:references:posts part of the first mix command tells Phoenix how a comment should reference a blog post in the database. You will now need to edit ./web/ models/comment.ex and make the following changes:

$ diff comment.ex comment.ex.orig 7c7 < belongs_to :post, Blog.Post, foreign_key: :post_id --- > belongs_to :post, Blog.Post 12,14d11 < @required_fields ~w(name content post_id) < @optional_fields ~w() < 18,20c15,18 < def changeset(model, params \\ %{}) do < model < |> cast(params, @required_fields, @optional_fields) --- > def changeset(struct, params \\ %{}) do > struct > |> cast(params, [:name, :content]) > |> validate_required([:name, :content]) 23d20 < 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ diff comment . ex comment . ex . orig 7c7 < belongs_to : post , Blog . Post , foreign_key : : post_id -- - > belongs_to : post , Blog . Post 12 , 14d11 < @ required_fields ~ w ( name content post_id ) < @ optional_fields ~ w ( ) < 18 , 20c15 , 18 < def changeset ( model , params \ \ % { } ) do < model < | > cast ( params , @ required_fields , @ optional_fields ) -- - > def changeset ( struct , params \ \ % { } ) do > struct > | > cast ( params , [ : name , : content ] ) > | > validate_required ( [ : name , : content ] ) 23d20 <

Last, you will need to edit ./web/models/post.ex to let it know that it supports multiple comments:

$ diff post.ex post.ex.orig 3d2 < import Ecto.Query 8d6 < has_many :comments, Blog.Comment 13,15d10 < @required_fields ~w(title body) < @optional_fields ~w() < 19,28c14,17 < def changeset(model, params \\ %{}) do < model < |> cast(params, @required_fields, @optional_fields) < end < < def count_comments(query) do < from p in query, < group_by: p.id, < left_join: c in assoc(p, :comments), < select: {p, count(c.id)} --- > def changeset(struct, params \\ %{}) do > struct > |> cast(params, [:title, :body]) > |> validate_required([:title, :body]) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ diff post . ex post . ex . orig 3d2 < import Ecto . Query 8d6 < has_many : comments , Blog . Comment 13 , 15d10 < @ required_fields ~ w ( title body ) < @ optional_fields ~ w ( ) < 19 , 28c14 , 17 < def changeset ( model , params \ \ % { } ) do < model < | > cast ( params , @ required_fields , @ optional_fields ) < end < < def count_comments ( query ) do < from p in query , < group_by : p . id , < left_join : c in assoc ( p , : comments ) , < select : { p , count ( c . id ) } -- - > def changeset ( struct , params \ \ % { } ) do > struct > | > cast ( params , [ : title , : body ] ) > | > validate_required ( [ : title , : body ] )

Now execute mix ecto.migrate from the root directory of your Phoenix project. You will now need to change the routing table (./web/router.ex) and add an Elixir function (:add_a_ comment) in order to implement comments:

$ diff router.ex router.ex.orig 20,22d19 < resources "/posts", PostController do < post "/comment", PostController, :add_a_comment < end 1 2 3 4 5 $ diff router . ex router . ex . orig 20 , 22d19 < resources "/posts" , PostController do < post "/comment" , PostController , : add_a_comment < end

Now you should edit ./web/controllers/post_controller. Put simply, you create a new plug, you implement the add_a_comment function and you make a small change to the existing implementation of the show function. You will now need to create ./web/templates/post/ comment_form.html.eex, which will be the web page for writing comments. Then, you need to make a change to ./ web/templates/post/show.html.eex to turn on comments. Now, create ./web/templates/post/comments.html.eex, which will be used for displaying the comments and make it active inside ./web/templates/post/show.html.eex. Now that you are done with the development of the blog site, you can start using it. Image below shows the home page of the site as well as the web page for creating new blog posts.

Although the blog site is far from complete, it is working without requiring you to write too much Elixir code! Should you wish to improve it, you can add user support and the ability to add images to your blog posts. The last step would of course be to deploy your website to a web server for the world to use and enjoy, but the details of how you do this are beyond our brief for this tutorial.