How it works: the Elm+Elixir Starter Kit

Alex Koppel bio photo By Alex Koppel

Last week I released an Elm+Elixir Starter Kit to make it easy for developers to kick off projects using Elm, Elixir, and OAuth login via Twitter. All the parts come preassembled — just clone, run a setup script, and start building!

This week, let’s take a look at how the template project works, digging a little into the most interesting and challenging parts.

How do we compile Elm to Javascript?

The Phoenix framework uses the Brunch asset pipeline for static assets, so it’s actually quite straightforward to get Elm compiling. As described in @diamondgfx’s useful blog post, you just need to add the brunch-elm packages to package.json and set up the Elm section of your brunch-config.js appropriately. (This is also how the app handles SASS.)

How do we set up Twitter login?

Modern-day OAuth involves three parties: your browser, the app’s server, and an OAuth provider like Twitter. You log into Twitter with your browser; Twitter then redirects you back to the app with a special code in the URL. The app server talks behind the scenes with Twitter to validate that code and get your account info. In the halcyon days of yore1 we had to set this up ourselves, but fortunately there are great libraries these days to make it easy.

In Elixir, the combination of Ueberauth and Guardian take care of OAuth2. You configure Ueberauth in your app’s config.exs:

After that, hitting /auth/twitter will redirect you to the login URL with all the parameters Twitter needs to identify the app. When the user returns, Ueberauth validates the response and gets the authenticated user info, which the the starter kit handles in the AuthController:

Once the app has a validated Twitter account, Guardian takes care of everything to do with your local user record. It provides a number of useful plugs to ensure that users are loaded or authenticated3, making use of the GuardianSerializer module to encode user information into and out of the session.

How do you get user info on the frontend?

Here’s where things get really interesting. How does Elm know whether a user is logged in?

Elixir has a templating language (EEx). Since the backend Phoenix app is the source of truth, we could simply pass that information into Elm on initialization:

Hard-coding the user data in the template would be straightforward and fast, but if that approach unsettles you a little you’re not alone. Either your app only knows how to get user data on load, requiring a refresh whenever it changes, or you have to have multiple mechanisms to get the user data, which adds all the risks of code duplication.

Instead the Elm+Elixir Starter Kit loads the user data via a JSON API. When you create an Elm app, you can specify the starting command, which the Starter Kit uses to immediately identify the user:

(Never seen Elm before? Here’s a tutorial that explains what this code does.)

The UserApi Elm module makes a request to Phoenix, which knows the user state. That response is incorporated into the model for the views to consume.

User API request

What about Docker?

The Starter Kit is set up with Docker so you can build an image once and use it for development, testing, and production. The Docker image contains Elixir, Elm, and all the packages we need to run and test the code. For local development, the app uses docker-compose to set up Postgres as well; to avoid checking any passwords or secrets into source control, it reads from a git-ignored development.env file (a template for that is provided).

What’s next?

I’ve been building an app on top of the Starter Kit and am learning a bunch. Some of what I’m building will make its way back to the Starter Kit; other pieces I’m looking forward to blogging about here.

If you’ve had a chance to play with the repo, I’d love to hear about your experienc what you’re doing with it, and certainly anything that got in your way.

Enjoy!

  1. The idea of halcyon days, as I learned recently in a class, comes from the story of a Alcyone, a Greek woman who was transformed into a bird. 

  2. Similar to Omniauth and Devise, for us Rubyists. 

  3. plugs function (roughly) like the before_actions us Ruby types are familiar with.