Deploying an Elixir + Phoenix + Elm Application with Nanobox


This article was written by Roberto Trevisan and originally published on Medium. It has been republished here with his permission. If you like it, be sure to visit the original and give it a few claps.


I recently found out about Nanobox and saw it as an excellent alternative to Heroku when deploying an Elixir, Phoenix, and Elm application.

The most important advantage of using Nanobox instead of Heroku (which I’m a huge fan) is that it allows for each Elixir node to talk to each other directly. Another advantage is that you can deploy to Cloud computing engine other than AWS such as Azure, Google, DigitalOcean among others using your own billing account.

So assuming Phoenix and Elixir are already installed, let’s start by creating a brand new Phoenix (version 1.3) app:

mix phx.new nanoapp

To save time, skip the “Fetch and install dependencies” for now.

$ cd nanoapp
$ mix deps.get

Setting Up Nanobox

Considering you already followed the procedure in https://docs.nanobox.io/install/, the first step is to create a boxfile.yml.

# boxfile.yml at the root of your project
run.config:
  # elixir runtime
  engine: elixir
  engine.config:
    runtime: elixir-1.5
    erlang_runtime: erlang-20

  # we need nodejs in development
  # ensure inotify exists for hot-code reloading
  dev_packages:
    — nodejs
    — inotify-tools

  # cache node_modules
  cache_dirs:
    — assets/node_modules
    — assets/elm/elm-stuff

  # add node_module bins to the $PATH
  extra_path_dirs:
    — assets/node_modules/.bin

  # enable the filesystem watcher
  fs_watch: true

  extra_steps:
    — cd assets && npm install
    — cd assets/elm && ../node_modules/.bin/elm-package install — yes

deploy.config:
  extra_steps:
    — mix phx.digest
  before_live:
    web.main:
      — mix ecto.create — quiet
      — mix ecto.migrate

# add postgres as a data component
data.db:
  image: nanobox/postgresql:9.6

web.main:
  start: node-start mix phx.server
  # Timex and TZData need to write into its priv/ folder
  writable_dirs:
    — priv

Before we start nanobox we need to add a few lines to assets/package.json.

{
  "repository": {},
  "license": "MIT",
  "scripts": {
    "deploy": "brunch build --production",
    "watch": "brunch watch --stdin"
  },
  "dependencies": { 
    "elm": "^0.18.0", /* <-- Add this line
    "phoenix": "file:../deps/phoenix",
    "phoenix_html": "file:../deps/phoenix_html"
  },
  "devDependencies": {
    "babel-brunch": "6.1.1",
    "brunch": "2.10.9",
    "clean-css-brunch": "2.10.0",
    "elm-brunch": "^0.10.0", /* <-- Add this line
    "uglify-js-brunch": "2.10.0"
  }
}

We are ready to take Nanobox for a spin: nanobox run.
Nanobox will start at the app/ folder which is sync to your projects root folder.

Installing the Rest of Elm

Elm is already installed in assets, but let’s make a simple hello world.

# in the assets/elm folder:
elm-package install elm-lang/html — yes

And the assets/elm/Main.elm file should look like this:

import Html exposing(text)

main =
 text “Hello World”

And now, and most importantly we need to edit brunch-config.json to add the elmBrunch configuration:

Update brunch-config.json

Add Elm to the watched folders:

watched: [
  …
  ‘elm’
],

Remove notifications to avoid Elm compilation messages errors in Docker:

notifications: false,

Configure the plug-in:

plugins: {
  elmBrunch: {
    elmFolder: ‘elm’,
    mainModules: [ ‘Main.elm’ ],
    outputFolder: ‘../js’,
    outputFile: ‘main.js’,
    executablePath: ‘../node_modules/.bin’,
  },
}

This will read the Elm code at assets/elm and output the compiled main.js to assets/js.

Tying It All Together

We need to make Phoenix talk to Nanobox database, so in config/dev.exs we should change the database configuration:

# Configure your database
config :nanoapp, Nanoapp.Repo,
  adapter: Ecto.Adapters.Postgres,
  username: System.get_env(“DATA_DB_USER”),
  password: System.get_env(“DATA_DB_PASS”),
  hostname: System.get_env(“DATA_DB_HOST”),
  database: “gonano”,
  pool_size: 10

You will want to change it in the prod.ex as well so deployment works.

Now we need to integrate Phoenix and Elm starting with adding the following lines to the assets/js/app.js

import Elm from ‘./main’;

const elmDiv = document.querySelector(‘#elm_target’);

if (elmDiv) {
  Elm.Main.embed(elmDiv);
}

And we need to replace the layout at li/nanoapp_web/template/layout/app.html.eex and leave the body tag as:

<body>
  <div class=”container”>
    <p class=”alert alert-info” role=”alert”><%= get_flash(@conn, :info) %></p>
    <p class=”alert alert-danger” role=”alert”><%= get_flash(@conn, :error) %></p>
    <div id=”elm_target”></div>
  </div> <! — /container →
  <script src=”https://code.highcharts.com/highcharts.js"></script>
  <script src=”<%= static_path(@conn, “/js/app.js”) %>”></script>
</body>

Now, we can exit the VM and make a dry-run: nanobox deploy dry-run.

That is it. Go to IP address Nanobox provided and you should see your Elm app backed by Elixir, Phoenix and Nanobox.

To put your app into the wild follow the deployment workflow.


Don't forget to visit the original article and give it some claps!


tubedude/elixir-nanoapp on Github
Elixir + Phoenix + Elm app to run on Nanobox.

Phoenix
Productive. Reliable. Fast. A productive web framework that does not compromise speed and maintainability.

Elm
Generate JavaScript with great performance and no runtime exceptions.

Posted in Elixir, Phoenix, Elm