Is the Phoenix Framework the killer app of Elixir?

18.04.2022Raffael Schneider
Cloud Elixir Realtime Distributed Systems concurrent Otp Erlang Beam functional-paradigm actor-model b-nova techup Stay Tuned

For me personally, the year 2022 is all about distributed systems. For this reason, in a new TechUp series, I will deal with distributed systems in general, as well as their conception with Elixir and its ecosystem. First, we will focus on Elixir in order to get a practical grasp of the basic concepts of distributed systems using a programming language. After that, we will look at theoretical concepts as well as more advanced topics around distributed systems. This will be divided into the following two TechUp series:

  • Elixir Series
  • Distributed System Series

This is part 3 of the Elixir Series. If you’re not already coming from there, I highly recommend reading Part 2 first. For those of you who are just getting into Elixir and Distributed Systems with this TechUp, here’s a quick rundown on Elixir:

Elixir is a relatively new programming language from 2014, which, in addition to the functional paradigm, also uses Erlang’s “Open Telecom Platform” as a runtime target. The OTP is a perfect runtime for distributed systems and provides all the necessary components to program such a system. Nowadays, we are trying to accomplish similar concepts through container orchestration with Kubernetes.

Today’s TechUp is about the Phoenix Framework. A web framework that is fondly referred to as the “killer app” of Elixir. Let’s get to know Phoenix together and try to better understand its features. 😄

The Phoenix Framework

Programming languages are often characterised by their ecosystem, as well as their abundance of libraries and frameworks. This is no different with Elixir, where the Phoenix Framework is a big gun in terms of web frameworks. Phoenix is not only the main framework of Elixir par excellence, but also provides a multitude of features that greatly simplify web application development with Phoenix and at the same time enable a more robust architecture.

Phoenix is an MVC web framework that runs entirely on the BEAM, the virtual machine at the heart of OTP. There is no official company behind the software, but it is mainly employees and people around the consulting company DockYard who are driving the development of the framework. The current release version of the framework - at the time of writing - is version 1.6.0, which was published at the end of August 2021.

MVC stands for Model-View-Controller and is a software design pattern that should be familiar to every b-nova employee…

Asynchronous and real-time through the proven PubSub model

Phoenix uses the process-based Actor Model, with separate entities called Channels. A channel is also message-based and communicates via a topic with the PubSub server, which Phoenix ships with. A PubSub instance automatically synchronises with instances on other nodes. An asynchronous connection is established via a WebSocket between a channel and a potential client, typically a web browser, on which a session is currently running. This ensures asynchronous communication.

A word about WebSockets

Just like HTTP, a WebSocket is a communication protocol via TCP. However, since it is not HTTP, it is subject to a different standard, namely RFC 6455. It is worth mentioning that full duplex (also known as counter operation) is possible between both clients. More precisely, this means that data can be exchanged simultaneously and that a new connection does not have to be established as in the request/response model of HTTP.

WebSockets are used in applications where a real-time, highly up-to-date data stream, i.e. real-time web, is necessary. This is relevant, for example, for trading platforms where price fluctuations must be displayed in real time. Of course, WebSockets are used in chat platforms, streaming or typically also in gaming servers. WebSocket is supported as default by all common browsers.

Let’s do a quick example with a client request and a server response. The client request could look like this:

1
2
3
4
5
6
7
8
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

The associated server response would return a handshake to the client for the upgrade to the websocket protocol, after which the websocket-based duplex connections would be created.

1
2
3
4
5
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

The following data exchange would then immediately take place on layer 7, just as with HTTP, and would require TCP on layer 4. Websocket is therefore an interesting alternative to the classic HTTP, which allows duplex connections to be set up in real time, which are considered open until they are terminated by one of the two ends.

Frontend and backend in one thanks to unique LiveView

If we take a closer look at the communication via the WebSocket, we see that the HTML in the frontend is also updated via events against the backend. This process of HTML diffs against events is called LiveView in Phoenix and is an in-house feature that brings certain advantages, especially in a decentralised structure via channels.

PETAL instead of PWA

The Phoenix Framework and its LiveView functionality has also inspired other web frameworks to produce an equivalent solution. For example, Laravel LiveWire or Rails Hotwire. The approach is the same; instead of classic JSON, HTML is used between frontend and backend. This made it possible to remove abstraction layers that were previously taken for granted.

In addition to the transport layer, other abstraction layers are also obsolete in the Phoenix environment, as it is no longer necessary to follow the tried and tested principles of a conventional single-page application. With Phoenix in combination with LiveView, you can also build interactive web applications, which makes many sub-areas obsolete. The following components would certainly be worth mentioning:

  • JSON serialisation from server to client
  • JSON serialisation from client to server
  • REST controller(s)
  • JavaScript routing
  • JavaScript data store solutions
  • GraphQL subcomponents such as GraphQL type system, resolvers, queries or mutations

The Phoenix Framework can be used to implement various frontend solutions. In the community, a toolset called PETAL is preferably used. PETAL is an acronym and stands for the following software components:

  • Phoenix
  • Elixir
  • TailwindCSS
  • Alpine.js
  • LiveView

Besides Elixir, Phoenix and its own LiveView functionality, TailwindCSS is often used for styling and Alpine.js for frontend logic. Yes, now it can be argued that JavaScript continues to be used in the frontend and thus no added value is achieved. It is clear that the Phoenix Framework cannot completely replace JavaScript, because certain components such as a pop-up menu still have to be managed with JavaScript. In return, however, the whole logic of data modelling from the backend to the client falls away. The use of Alpine.js is ideally limited to purely front-end interactive elements.

Alpine.js was developed specifically for this use case and goes back to Laravel LiveWire developer Caleb Porzio. The tool contains only the most necessary with a limited set of elements, namely 15 attributes, 6 properties and 2 methods.

TailwindCSS is a utility-first CSS framework that allows you to set the classes and attributes needed for the desired element directly in the HTML. In combination with the rest of the stack, the result is an overall picture where everything can be built in HTML. This is the perfect starting point for a pure backend-focused web framework like Phoenix.

Ecto

Ecto is the Object Relational Mapper, ORM for short, for Elixir and also the standard ORM for the Phoenix framework.

Ecto supports the following databases:

  • Database

    • PostgreSQL
    • MySQL
    • MSSQL
    • SQLite3
    • ETS
  • Ecto Adapters

    • Ecto.Adapters.Postgres
    • Ecto.Adapters.MyXQL
    • Ecto.Adapters.Tds
    • Ecto.Adapters.SQLite3
    • Etso

ETS stands for Erlang Term Storage and is a built-in storage for the Erlang Runtime that allows very large datasets to be persisted in the Runtime at runtime. This results in an optimal method to use distributed data simultaneously.

Installation of Phoenix

We can easily have Phoenix installed in the latest version 1.6.6 (at the time of writing the TechUp) using Hex.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
❯ mix archive.install hex phx_new
Resolving Hex dependencies...
Dependency resolution completed:
New:
phx_new 1.6.5
* Getting phx_new (Hex package)
  All dependencies are up to date
  Compiling 11 files (.ex)
  Generated phx_new app
  Generated archive "phx_new-1.6.5.ez" with MIX_ENV=prod
  Are you sure you want to install "phx_new-1.6.5.ez"? [Yn] Y
* creating /Users/rschneider/.mix/archives/phx_new-1.6.5

Phoenix offers a quickstart with phx.new, with which we can easily set up a Phoenix template project. This takes up to a minute the first time it is set up, because certain dependencies are still being pulled. We simply call our Phoenix app webservice and set up the project with mix phx.new webservice as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
❯ mix phx.new webservice --live --no-ecto
* creating webservice/config/config.exs
...
* creating webservice/priv/static/favicon.ico

Fetch and install dependencies? [Yn] Y
* running mix deps.get
* running mix deps.compile

We are almost there! The following steps are missing:

    $ cd webservice

Start your Phoenix app with:

    $ mix phx.server

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

    $ iex -S mix phx.server

The --no-ecto\ flag simply denotes the use of Phoenix without ORM with Ecto, eliminating the need to set up a database before trying Phoenix. Now we can start our REPL as usual:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
❯ iex -S mix phx.server
Erlang/OTP 24 [erts-12.1.5] [source] [64-bit] [smp:10:10] [ds:10:10] [async-threads:1] [dtrace]

Compiling 13 files (.ex)
Generated webservice app
[info] Running WebserviceWeb.Endpoint with cowboy 2.9.0 at 127.0.0.1:4000 (http)
[debug] Downloading esbuild from https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.0.tgz
[info] Access WebserviceWeb.Endpoint at http://localhost:4000
Interactive Elixir (1.13.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>

Under localhost:4000 we can then call our web framework.

As you can see, Phoenix already provides a start page and displays a very helpful dashboard under http://localhost:4000/dashboard/home.

Templates

EEx stands for Embedded Elixir and refers to Elixir’s in-house template engine. The official documentation describes the most important features of the engine.

EEx is also the standard template engine of Phoenix and comparable to the Ruby equivalent ERB. By default, templates live in a lib/module_name/templates/\ directory and are named after the view to be rendered. For example, a template for a test page might live under a lib/hello_web/templates/page/test.html.eex directory and the test.html.eex would look like this:

1
2
3
<div class="jumbotron">
  <p><%= handler_info @conn %></p>
</div>

The example comes directly from the Phoenix-specific documentation about templates.

Next step: A Twitter clone as POC

In the next episode of the Elixir Series, we will write a Twitter clone in Elixir and have this application hosted on a platform designed for Elixir.

Phoenix Framework website

Phoenix Documentation

Elixir Website

Petal - What it is and why you might like it