Elixir /Phoenix— Lets code authentication. Todo application part 1.

With Guardian and Comeonin

Hello. My name is Stephan, and I’m a half Canadian, half Norwegian web developer who recently picked up Elixir, and the Phoenix Framework.

In this series I’ll show you how to make a complete user authentication with the help from Guardian and Comeonin, and let the users create their own todo lists. I am still a beginner, but I want to share my experience with other developers who might be wondering about the same things I was when I started out fresh.

First things first, I assume you already have Elixir and Phoenix installed. If not, please check out the Elixir and Phoenix installation guide.

Creating the project

Once you have everything up and running, lets proceed with creating our project. Elixir uses the mix tool for maintaining projects. We start off with writing mix phoenix.create followed by the name of the app in our console:

~$ mix phoenix.new todo
* creating todo/config/config.exs
* creating todo/config/dev.exs
Fetch and install dependencies? [Yn] y
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch build
* running mix deps.get
* running npm install && node node_modules/brunch/bin/brunch buildWe are all set! Run your Phoenix application:$ cd todo
$ mix phoenix.server
You can also run your app inside IEx (Interactive Elixir) as:$ iex -S mix phoenix.serverBefore moving on, configure your database in config/dev.exs and run:$ mix ecto.create

Perfect. Now lets cd into our project and run mix phoenix.server, let Elixir compile your project, and visit localhost:4000:

We should also create our database:

~/todo$ mix ecto.create
The database for Todo.Repo has been created

The model and schema

Perfect, the project is all set up, and it’s time to start creating some users. We’ll start off creating a model, so that we can define our users and put them into the database. Mix comes with a quick tool for creating the model itself, a migration and some tests.

~/todo$ mix phoenix.gen.model User users email:string password_hash:string
* creating priv/repo/migrations/20160528092654_create_user.exs
* creating web/models/user.ex
* creating test/models/user_test.exs
Remember to update your repository by running migrations:$ mix ecto.migrate

The first argument is the module name followed by its plural name
(used for the schema). / Phoenix Documentation

Before we run the migration, lets have a look at what we got. Open up user.ex. We’ll focus on the newly generated user schema for now. We need to add one more field to our schema:

# web/models/user.exdefmodule Todo.User do
use Todo.Web, :model
schema “users” do
field :email, :string
field :password, :string, virtual: true
field :password_hash, :string

We add a virtual field as an intermediate field before we hash our password in the password_hash field. The virtual field will not be set in the database. We dont need to do any changes to our migration file, so leave it as it is:

# priv/repo/migrations/2016052809654_create_user.exdefmodule Todo.Repo.Migrations.CreateUser do
use Ecto.Migration
def change do
create table(:users) do
add :email, :string
add :password_hash, :string

Now lets run the migration:

~/todo$ mix ecto.migrate
Compiled web/models/user.ex
Generated todo app
11:47:13.300 [info] == Running Todo.Repo.Migrations.CreateUser.change/0 forward11:47:13.300 [info] create table users11:47:13.335 [info] == Migrated in 0.3s

There is of course no changes to our webapp yet, however we now have a user table for our database. We can run our entire application in iEX by typing iex -S mix in our console. Lets try and insert some data into our user database:

~/todo$ iex -S mix
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
Interactive Elixir (1.2.3) — press Ctrl+C to exit (type h() ENTER for help)
iex> alias Todo.Repo
iex> alias Todo.User
iex> Repo.insert(%User{email: “foo@bar.com”, password_hash: “foobar123”})
[debug] INSERT INTO “users” (“inserted_at”, ....{:ok,%Todo.User{__meta__: #Ecto.Schema.Metadata<:loaded>, email: “foo@bar.com”, id: 2, inserted_at: #Ecto.DateTime<2016–05–28T09:52:28Z>, password_hash: “foobar123”, updated_at: #Ecto.DateTime<2016–05–28T09:52:28Z>}}

Sweet. Ecto injected our first user into the database. Ecto automatically creates the id field with our timestamps, so we dont have to worry about setting the id field in our schemas. You can go ahead and add a couple more users to the database. Let’s see if we can retrieve all our users:

iex(5)> Repo.all(User)
[debug] SELECT u0.”id”, u0.”email”, u0.”password_hash”, u0.”inserted_at”, u0.”updated_at” FROM “users” AS u0 [] OK query=10.6ms
[%Todo.User{__meta__: #Ecto.Schema.Metadata<:loaded>, email: “foo@bar.com”, id: 1, inserted_at: #Ecto.DateTime<2016–05–28T09:49:50Z>, password_hash: “foobar123”, updated_at: #Ecto.DateTime<2016–05–28T09:49:50Z>},
%Todo.User{__meta__: #Ecto.Schema.Metadata<:loaded>, email: “stephan@devalo.no”, id: 2, inserted_at: #Ecto.DateTime<2016–05–28T09:57:53Z>, password_hash: “password”, updated_at: #Ecto.DateTime<2016–05–28T09:57:53Z>}]

That is awesome. All our users are saved correctly in the database. We still dont have any way of creating new users through our web app, so we’ll do that next.

The Controller

Phoenix controllers act as intermediary modules. Their functions — called actions — are invoked from the router in response to HTTP requests.
Phoenix Documentation

In order to do anything with our users in our webapp, we need a user controller. The user controller contains our actions, and are invoked from our router. Lets start with the action :index. We want to collect our users from the database, and have them available in our view templates.
We start off by creating a new file — web/controllers/user_controller.ex and add the necessary index action, in the form of a function:

# web/controllers/user_controller.exdefmodule Todo.UserController do
use Todo.Web, :controller
alias Todo.User
def index(conn, _params) do
users = Repo.all(User)
render(conn, “index.html”, users: users)

The first line in our module imports some useful modules for us to use in our controller. We collect and store our users in the users variable. Since Elixir is immutable, users will never change unless we specifically reassign it. Next, we render index.html, and make users available in our view.

Router, Views and Templates

If we now go to localhost:4000/users we’ll get an error:

no route found for GET /users (Todo.Router)

We haven’t added our users to the router yet, so lets do that now. Replace your router with the following:

# web/router.exdefmodule Todo.Router do
use Todo.Web, :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 “/”, Todo do
pipe_through :browser # Use the default browser stack
get “/”, PageController, :index
resources “/users”, UserController

We added a new line resources “/users”, UserController to our routes. This will give us all the CRUD routes in one go. All your routes can be viewed with the special command mix phoenix.routes:

~/todo$ mix phoenix.routespage_path GET / Todo.PageController :index
user_path GET /users Todo.UserController :index
user_path GET /users/:id/edit Todo.UserController :edit
user_path GET /users/new Todo.UserController :new
user_path GET /users/:id Todo.UserController :show
user_path POST /users Todo.UserController :create
user_path PATCH /users/:id Todo.UserController :update
PUT /users/:id Todo.UserController :update
user_path DELETE /users/:id Todo.UserController :delete

If we try and look up localhost:4000/users at this time, we’ll get another error:

undefined function Todo.UserView.render/2 (module Todo.UserView is not available)

This error gives us a little hint of what we need to do next. We haven’t created the views yet! In Phoenix, views and templates go hand in hand. One won’t work without the other. We’ll first add a new file to the view folder:

# web/view/user_view.exdefmodule Todo.UserView do
use Todo.Web, :view

The view will start looking for the associating html template, and will raise an error if it cant find it. First, create a new folder web/templates/user and call the new file index.html.eex. The name of the file must be the same name as the corresponding action in the user controller.

EEx is the default template system in Phoenix, and it is quite similar to ERB in Ruby. It is actually part of Elixir itself, and Phoenix uses EEx templates to create files like the router and the main application view while generating a new application. / Phoenix Documentation

Yay! Our webapp doesn’t throw any errors anymore. It’s time to display our users directly from the database to our view template:

# web/templates/user/index.html
<table class=”table”>
<%= for user <- @users do %>
<td><%= user.email %> </td>
<% end %>

Since we rendered the index page with users: users in the controller action, we can access it in our templates with an “at” symbol.

If we now take a look at our webapp, we can see all our users listed neatly at localhost:4000/users:

Now let’s see how we can add more users to our list, without having to add them directly in iEX. as with the :index action, we also need a :new action. The :new action will render the form where we register new users:

# web/controllers/user_controller.ex
def new(conn, _params) do
changeset = User.changeset(%User{})
render(conn, “new.html”, changeset: changeset)

Changesets allow filtering, casting, validation and definition of constraints when manipulating models.

There is an example of working with changesets in the introductory documentation in the Ecto module. The functions change/2 and cast/4 are the usual entry points for creating changesets, while the remaining functions are useful for manipulating them. / Ecto Documentation

First, we store our user changeset into the empty variable changeset, we then render new.html and make the changeset available in our view templates. Last thing we need for us to actually see the new template, is to add the template in the same folder as the index template. As mentioned above, our new template will contain the form we need to create users:

<h1>New user</h1>
<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
<div class=”form-group”>
<%= text_input f, :email, placeholder: “Email”,
class: “form- control” %>
<div class=”form-group”>
<%= password_input f, :password, placeholder: “Password”,
class: “form-control” %>
<%= submit “Create new user”, class: “btn btn-primary” %>
<% end %>

form_for/4 receives the Ecto.Changeset and converts it to a form, which is passed to the function as the argument f. All the remaining functions in this module receive the form and automatically generate the input fields, often by extracting information from the given changeset. For example, if the user had a default value for age set, it will automatically show up as selected in the form
/ Phoenix Documentation

If we now take a look at localhost:4000/users/new, our form is complete:

However, if we try to create a new user, phoenix throws an error:

undefined function Todo.UserController.create/2

We haven’t created the :create action yet, which we are referring to in our form. We’ll put our :create action with the other functions in our user controller:

# web/controllers/user_controller.ex
def create(conn, %{"user" => user_params}) do
changeset = User.changeset(%User{}, user_params)
case Repo.insert(changeset) do
{:ok, user} ->
|> put_flash(:info, "User created!")
|> redirect(to: user_path(conn, :index))
{:error, changeset} ->
|> render("new.html", changeset: changeset)

We add our user changeset and our user parameters from the form into the variable changeset. If everything goes as planned, we add the add the user into the database, and redirect to the user index page, with a nice flash message telling us everything went well. If not, we re-render our new.html template.


Lets have a look at our changeset. The changeset lies in our user model, replace the changeset function with the following:

# web/model/user.exdefmodule Todo.User do
def changeset(model, params \\ :empty) do
|> cast(params, ~w(email), [])
|> validate_format(:email, ~r/@/)

The changeset allows us to put on validations before we store it in the database. Here, we ensure that email address contains an “at” sign. If we try to fill out an empty form, the validations will fail, and the :new action will be re-rendered. It would be nice if we could have some error messages in the form, telling us what went wrong. Lets look into that now.

# web/templates/user/new.html.eex<h1>New user</h1>
<%= form_for @changeset, user_path(@conn, :create), fn f -> %>
<%= if @changeset.action do %>
<div class=”alert alert-danger”>
<p>Oops, something went wrong</p>
<% end %>
<div class=”form-group”>
<%= text_input f, :email, placeholder: “Email”,
class: “form-control” %>
<%= error_tag f, :email %>
<div class=”form-group”>
<%= password_input f, :password, placeholder: “Password”,
class: “form-control” %>
<%= submit “Create new user”, class: “btn btn-primary” %>
<% end %>

First, if there are any any errors, we’ll display an error telling the user that something went wrong. Underneath the email field, we’ll add an error_tag, which will give the user a better description of what went wrong in this particular field. Lets try and send the empty form:

Sweet. Everything is working as expected. At this point, we are storing the users password in plain text, which one should NEVER do. In the next part of this series, we’ll look into hashing the password before we store it, creating the user show page, letting the user update its information, and logging on and off using Guardian.

That’s it for the first part of this guide on how to create a todo application in phoenix. You can find part 2 here

Until next time
Stephan Bakkelund Valois

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store