Last year we had the opportunity to develop an enterprise-class platform using the Elixir language. Our client was interested in the speed and flexibility. Requests for a software written in Elixir are rare. Nevertheless, we didn’t hesitate to pick up the gauntlet. Apart from the language requirement, we were left with a choice of tools and frameworks. With that in mind, we started research on the most popular ways of developing web applications using Elixir.
Thanks to the vibrant community, we had a strong start. Everywhere you go, you will see mention of the Phoenix Framework. Phoenix is a package of libraries like Cowboy or Ecto, wrapped into a tight ecosystem with the addition of some helpers. It allows developers to bootstrap a simple website in almost no time. We were positively surprised by how good the interaction within this framework is. With one command you get full CRUD with templates and simple controllers. Need a REST-like CRUD? No problem, one command and you have one with JSON replies instead of HTML. Apart from that, it’s blazing fast. While developing a Phoenix app, I sometimes forget that it’s still running on my laptop. It has a live reload mechanism enabled by default for the dev environment, so whenever you modify a file that’s on the watchlist, you’ll be presented with instant changes in the browser.
There’s a long list of things that are great about running a server on Elixir with Phoenix Framework. Still, it’s relatively easy to find similar couples in other languages like Java and Spring Boot, Python and Django, or PHP and Symfony/Laravel. One of the highlights of the Phoenix Framework is the Phoenix Liveview – an extension that enables a website to work like a single-page application with just a little effort.
Some of you may recall this illustration by William Erhel. In both scenarios, there’s something wrong. Either it’s a creepy backend, or it’s a barely maintainable frontend. But why not have positive sides of those two? With Phoenix Framework and LiveView extension, it’s totally doable.
The Front-end part
Although the basic Phoenix Framework setup is adequate or a very robust back-end, we still lacked a modern solution for the front-end. After some digging, we found a tool called “Phoenix LiveView”. We decided to give it a try instead of using some well-known frameworks like Angular or React. LiveView is a library that compromises server-rendered templates with client-sided live updates. It’s still under substantial development with a minor release almost every week. Fortunately, the specifics of the agreement with our client allowed us to use this experimental library.
When we were starting the project, LiveView was something fresh. The latest version at that point was 0.3.x. It was different from the current version (0.14.x), there were some issues with it, but that didn’t scare us. At one point we were close to ditching it, but after updating to the latest version, we’ve found out that it works better, so we kept it. The devs are very responsive. Usually, you can get your issue on GitHub resolved in around 10 minutes (if it’s a small thing). Of course, this won’t make it to a release in such a short period, but you can always fork the repo and temporarily use the commit when it was fixed.
Thanks to LiveView, we were able to make views responsive to the events coming from the server without a full-refresh, just like when using Angular or similar. Since Elixir is running on the Erlang VM it makes it possible to use WebSocket connections without problems, what’s more, apparently Phoenix can handle around 2M of those at once. When the back-end system was designed to process data continually it’s very nice to have a solution where you can just subscribe to a topic and update the view based on the received message.
Using LiveView allows you not only to save on JS code, so the website loads faster, but also to simplify the back-end logic. You don’t have to write individual controllers for every “dynamic” part of a website to return some JSON, nor to render the whole template in the browser, as is the case with some popular JS framework. You can have both done on the server, and thanks to how the EEx templates are implemented, you can actually send to the client only things that have changed.
The above figure illustrates the “Ajax” way of doing things. As we can see, this approach results in large amounts of code in the browser, larger HTTP payloads, and also more code on the server. It also somehow forces the developer to use some all-in-one solution (yes, because we’re lazy) which includes a lot of boilerplate and functionalities we’ll never use.
This one shows the LiveView approach. Compared to the previous illustration, we can see that boilerplate is gone, and stuff sent to the client is a lot smaller, which relieves the browser from doing hard work.
The lifecycle of a LiveView-enabled view is pretty simple. First, the server responds to a HTTP request with a static HTML, and after that, the browser tries to set up a WebSocket connection. After it has been established, it calls the same function and renders the template again although this time the template is being replaced in-place and it’s stateful, so whenever there are changes to the data, it’s automatically updating. This works both ways, so we can also send events to the server, which can react accordingly. We love the “let it crash” approach of Elixir/Erlang. It has been used also in this case. If we encounter a server error while being on such a view it will try to gracefully reconnect the socket with decreasing frequency on failed attempts and on success will reset state to its initial version.
Thanks to this lifecycle, it’s straightforward to present a user with nice loading animation if we need to perform time consuming tasks. This creates a feeling of a very responsive website. On the other hand it’s also easy to do things twice. As I’ve mentioned earlier, the first – static – render and the second – stateful – render are calling the same function to get the view, so without having special conditions inside it we may do things twice, and if those operations need some time to complete then the user will have to wait without seeing any change and can even leave the website thinking it’s broken.
You may be curious how all of this is working on the front-end side. With the WebSocket connection, we are able to push data from the server to the client instead of doing a pull based approach (like long polling). This way, when the state of a view changes on the server it renders a new template, extracts changed parts and sends them to the client. Every time the server sends only dynamic parts of the template and only the ones that have changed, which makes WebSocket messages as small as possible. Whole template (including static parts) is sent only on the initial static HTML render. Next, JS merges those and computes HTML. New HTML is being passed through the morphdom library – it compares current DOM with the one that would be the result of applying changes and then runs the lowest possible number of transformations on the real node to make it look like the target one. That way we get optimised not only for network traffic but also browser resources. Simple yet powerful. Below you can find an example of that functionality in action.
LiveView ships with a bunch of useful JS that helps with the interaction between UI and the server. For example, to react on a click event one could just add a phx-click attribute to some button and then on the server implement handle_event method for it. You can do the same with the keyboard and form events, which makes things like a real-time server-side form validation a breeze – no more duplicated validation on FE and BE.Although this set of built-in functionalities is compelling, sometimes it’s just odd to use a backend for all frontend-sided bells and whistles, even if it could handle it without any hiccups. Let’s take for example, relative dates. We’d have to send every second data from the server just to update a html with a new relative value of a timestamp that won’t change. Instead of doing that, Phoenix LiveView offers a feature called “hooks”. Those hooks are basically small JS “widgets”, you can think of them as React components. They have some lifecycle methods like mounted, updated, destroyed, and more. Using those you can build pretty much anything, from a simple hook that displays the relative date, through the map widget up to the standalone SPA (but why would you do that). In our project we’ve been using vanilla JS, but one can easily imagine how powerful hooks could be when properly integrated with React or Vue. Being able to properly leverage the usage of WebSockets-based events and browser-side hooks gives the developer a powerful tool for building highly-responsive applications with loads of interactive elements.
In the end, we kept LiveView in our app due to its versatility. Even in the early stages of this tool it turned out to be a perfect solution for a modern web application. We have built stuff like lazy loading, pagination or “Load more” functionality with nearly no JS code, and replaced all XHR calls to the backend with their WebSocket equivalents. After almost a year of development, it’s hard for us to even think of removing this library from our app.
If you are looking for something new to try in your web application, I honestly recommend adding Phoenix LiveView to your Elixir project and forgetting about writing extensive internal REST APIs.