My first Phoenix app With Coherence

An article, posted almost 6 years ago filed in coherence, elixer, erlang, vm, authentication, Phoenix, framework, fast, messaging & rails.

Some time ago I actually initiated my very first Phoenix app, but was a bit disappointed by the lack of a rich box of gems (like that of ruby’s) and/or I didn’t have the time to invest heavily in researching all the possibilities. One of my new year resolution was to actively pursue more knowledge, hence I’m giving it a second shot.

My first Phoenix app

Why Phoenix?

I’m a full-stack Rails developer, but I needed real time messaging. That is not something Rails is typically good at (although it works), but Elixir (with its Erlang base) is well known for, even in the ruby community. Phoenix wraps Elixir in a nice Rails-like package ready for web and API development.

Requirements

Preparation

  1. Install Elixir (macOS & homebrew: brew install elixir) and make sure you have node.js installed (for frontend compiling) (macOS & homebrew: brew install nodejs)
  2. Get the mix-packagemangager:

     $ mix local.hex
    
  3. Then install Phoenix:

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

Typically a Phoenix app uses a PostgreSQL database. We assume it being installed; alternatively you can opt to use MySQL by passing the --database mysql option with the new command

mix phoenix.new web

Simply run the following command to get the base (and see above, you might add —database mysql when you’re not a PostgreSQL’er)

$ mix phoenix.new web

Hit Y(es) when asked for installing de dependencies. In case you want to change de default database (a PostgreSQL database named {applicationname}_dev) change it in config/dev.exs. Then navigate to the project’s dir (cd web in this case) run to prepare the database:

mix ecto.create

Run the server (it’ll run on port 4000 by default).

mix phoenix.server

Adding authentication

When using rails I typically use the Devise-gem. When browsing for packages at the Awesome-Elixir-page I came across Coherence which felt kind of the same.

Hence I added {:coherence, "~> 0.3"} (a later version may be available, check the above links) to the deps block in the mix.exs file. deps is defined as a private function (defp deps do … end-block). It could’ve been defined as a non private function (def deps… (note without the p)).

Then add :coherence as the first in the array of applications to load within the application definition, mine now looks like this:

 applications: [:coherence, :phoenix, :phoenix_pubsub, :phoenix_html…]]

Then install it, which will set up some database migrations for a newly to create User model and more:

 mix coherence.install --full-confirmable

Then update the web/router.ex file to use the coherence routes and route-based authentication-filtering (or should I say piping):

Add:

# just after `  use MyProject.Web, :router`
use Coherence.Router         # Add this

# last line in the `pipeline :browser do … end`-block
plug Coherence.Authentication.Session

# Add the following three blocks after the `pipeline :browser`-block
pipeline :protected do
  plug :accepts, ["html"]
  plug :fetch_session
  plug :fetch_flash
  plug :protect_from_forgery
  plug :put_secure_browser_headers
  plug Coherence.Authentication.Session, protected: true
end

scope "/" do
  pipe_through :browser
  coherence_routes
end

scope "/" do
  pipe_through :protected
  coherence_routes :protected
end

Also don’t forget to run the migrations:

mix ecto.setup    

Now you’ll have a full user-table and you’re ready to register users!

Getting the user in the view

You want the application to reflect that the user is signed in (e.g. show a sign in link when not logged in, show a sign out link when logged in)

Add the sign in link and a sign out buton

Adding a link to sign in using the link-helper:

<%= if Coherence.current_user(@conn) do %>
  <li><%= button('Uitloggen', to: session_path(@conn, :delete, Coherence.current_user(@conn).id), method: :delete) %></li>
<% else %>
  <li><%= link('Register', to: registration_path(@conn, :new)) %></li>
  <li><%= link('Sign in', to: session_path(@conn, :new)) %></li>
<% end %>

Post a message

TrueProgrammers™ will probably hate it, but I’m going to simply do the Phoenix equivalent of rails scaffold (note the ‘repetition’ of posts in the line, this is the table-name):

$ mix phoenix.gen.html Post posts message:text user_id:references:users 

In contrast to a rails scaffold, you do have to ‘manually’ place the following line in the scope "/", Lokal do … end-block in router.ex.:

resources "/posts", PostController

And of course you need to migrate your database:

mix ecto.migrate

Fence it (bringing Coherence to Posts)

We don’t want just anyone to update posts, hence we add at the top of our PostsController:

plug Coherence.Authentication.Session, [login: true, protected: true] when (action != :index and action != :show)

The login: true, makes sure the user is redirected to the login page on all actions but :index and :show.

Attach user to the newly created post

Almost forgot about this one. You want to know which user posted what, hence we update post_controller.ex and update the create block:

def create(conn, %{"post" => post_params}) do
  user = Coherence.current_user(conn)
  changeset = Post.changeset(%Post{user: user}, post_params)
  ...
end

We’ve now created a new changeset including the new user params. In case you’re wondering, like I was, whether this doesn’t open up overriding the actual user attribution? Well, since the model (in post.ex) describes how to cast the parameters to a proper changeset, which doesn’t include a user, nor a user_id, it won’t pass any user changes :)

Step back: some observations

The Phoenix-framework is very much Rails like. But when searching for a proper authentication gem I didn’t find something that was as complete as e.g. Devise is. But all is under heavy development.

Also I missed kind of the ruby approach where functional programming is allowed but at the same time everything is an object as well. I really enjoy writing:

[1,2,3,4].include? 3

Instead, Elixir has only what you would call class-methods in ruby that allow you to search lists, which make it more verbose. You have to think about what type of data structure you’re dealing with. Of course, that’s kind of the nature of a pure functional programming language

I haven’t dealt with the primary reason I wanted to check Phoenix out for in the first place, the promised speed in dealing with messaging (because of Elixer running in the Erlang VM).

That’s it

That’s it for now! Next will be pushing it real time to the subscribed audience :)

Op de hoogte blijven?

Maandelijks maak ik een selectie artikelen en zorg ik voor wat extra context bij de meer technische stukken. Schrijf je hieronder in:

Mailfrequentie = 1x per maand. Je privacy wordt serieus genomen: de mailinglijst bestaat alleen op onze servers.