Subscribe to access all episodes. View plans →

#75: Upgrading to Phoenix 1.4

Published November 19, 2018

Elixir 1.7

Phoenix 1.4

Chris McCord’s Upgrade Guide

Source code on GitHub

With the release of Phoenix 1.4 I wanted to look at easily we can update an existing Phoenix 1.3 app to use 1.4 - and that’s exactly what we’ll be doing in this episode. We’ll be using the Upgrade Guide put together by Chris McCord to update our application.

Let’s get started. First let’s open our project’s Mixfile and we’ll update our version of Phoenix to 1.4. Then we’ll need to update cowboy to plug_cowboy version 2.0. And we’ll also add plug to our dependency list. We’ll also need to update the version of phoenix_ecto to 4.0. Finally we’ll need to include the ecto_sql dependency.

mix.exs...defp deps do[  {:phoenix, "~> 1.4.0"},  {:phoenix_pubsub, "~> 1.0"},  {:ecto_sql, "~> 3.0"},  {:phoenix_ecto, "~> 4.0"},  {:postgrex, ">= 0.0.0"},  {:phoenix_html, "~> 2.10"},  {:phoenix_live_reload, "~> 1.0", only: :dev},  {:gettext, "~> 0.11"},  {:number, "~> 0.5.6"},  {:plug_cowboy, "~> 2.0"},  {:plug, "~> 1.7"}]end...

Now with our new dependencies, we’ll go to the command line to download them with mix deps.get. Then we’ll go to our assets directory and update those as well.

$ cd assets
$ npm install

Then let’s try starting our application.

$ mix phx.server

And we get an error - “The module Poison is not loaded and could not be found”. This is because Phoenix now uses the jason package instead of poison. You can still use poison by including the package to your list of dependencies. But instead, let’s update our app to use the jason.

We’ll go back to our Mixfile and add the jason package.

mix.exs...defp deps do[  ...  {:jason, "~> 1.0"},  ...]end...

Then we’ll open our config.exs and we’ll add a new config specifying Jason our json_library.

config/config.exs...config :phoenix, :json_library, Jason...

Then we’ll open our endpoint.ex module and we’ll use the new json_libary() we just set.

Then we’ll also need update our UserSocket with our transport info. We’ll set websocket: true and then longpoll: false.

lib/teacher_web/endpoint.ex...socket "/socket", TeacherWeb.UserSocket,  websocket: true,  longpoll: false...plug Plug.Parsers,  parsers: [:urlencoded, :multipart, :json],  pass: ["*/*"],  json_decoder: Phoenix.json_library()...

We’re specifying the transport information here because the transport macro has been deprecated.

So let’s open our user_socket.ex module and we’ll remove our call that uses transport.

lib/teacher_web/channels/user_socket.ex...# transport :websocket, Phoenix.Transports.WebSocket# transport :longpoll, Phoenix.Transports.LongPoll...

With that, let’s go back to the command line and download our new jason dependency.

Then we can start up our server.

$ mix deps.get
$ mix phx.server

And great everything starts up. So let’s go to the browser - and our app is working.

The changes we’ve made so far are the only required ones to get our app running with Phoenix 1.4. Let’s now go ahead and implement some of the optional updates.

Phoenix 1.4 includes formatter integration for test DSLs and routing so let’s go back to our project and create a .formatter.exs, then I’ll paste in the needed configuration.

.formatter.exs[  import_deps: [:phoenix],  inputs: ["*.{ex,exs}", "{config,lib,priv,test}/**/*.{ex,exs}"]]

Now one of the bigger changes is the new Routes alias. Let’s open our teacher_web.ex module and in our controller block, we’ll remove our import TeacherWeb.Router.Helpers.

Then we’ll alias our TeacherWeb.Router.Helpers as Routes. We’ll also need to do the same in our view block.

lib/teacher_web.exdef controller do  ...  alias TeacherWeb.Router.Helpers, as: Routes  ...enddef view do  ...  alias TeacherWeb.Router.Helpers, as: Routes  ...end

Now with these changes, we’ll need to go through our app and update all instances where we used our old route helpers to use our new alias. Let’s start by opening the album’s index.html.eex template. Then we’ll prefix our route helper with Routes.

Template path: lib/teacher_web/templates/album/index.html.eex

<%= link "show", to: Routes.album_path(@conn, :show, album) %>

Then let’s open our app.html.eex template and we’ll update our static_path for our css and our javascript.

Template path: lib/teacher_web/templates/layout/app.html.eex

<link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>">
<script src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>

Let’s update one more route in our album’s show.html.eex

Template path: lib/teacher_web/templates/album/show.html.eex

<span><%= link "Back", to: Routes.album_path(@conn, :index) %></span>

Another major change that came with Phoenix 1.4, is a change from using brunch to webpack for our assets. So let’s update our app to use “webpack”.

First let’s first open our package.json. And we’ll need to update our deploy and watch scripts to use webpack. Then we’ll go ahead and remove our current devDependencies and I’ll go ahead and paste in the new ones for webpack.

assets/package.json..."scripts": {  "deploy": "webpack --mode production",  "watch": "webpack --mode development --watch-stdin"},..."devDependencies": {  "@babel/core": "^7.0.0",  "@babel/preset-env": "^7.0.0",  "babel-loader": "^8.0.0",  "copy-webpack-plugin": "^4.5.0",  "css-loader": "^0.28.10",  "mini-css-extract-plugin": "^0.4.0",  "optimize-css-assets-webpack-plugin": "^4.0.0",  "uglifyjs-webpack-plugin": "^1.2.4",  "webpack": "4.4.0",  "webpack-cli": "^2.0.10"}...

We’ll also want to remove our brunch-config.js. Then we’ll create a new file in our assets directory named .babelrc. And I’ll paste in the configuration we want here.

assets/.babelrc{  "presets": [    "@babel/preset-env"  ]}

Finally we need to create another file webpack.config.js. I’ll paste in the defaults here to get webpack up and running with Phoenix.

assets/webpack.config.jsconst path = require('path');const glob = require('glob');const MiniCssExtractPlugin = require('mini-css-extract-plugin');const UglifyJsPlugin = require('uglifyjs-webpack-plugin');const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');const CopyWebpackPlugin = require('copy-webpack-plugin');module.exports = (env, options) => ({  optimization: {    minimizer: [      new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: false }),      new OptimizeCSSAssetsPlugin({})    ]  },  entry: {      './js/app.js': ['./js/app.js'].concat(glob.sync('./vendor/**/*.js'))  },  output: {    filename: 'app.js',    path: path.resolve(__dirname, '../priv/static/js')  },  module: {    rules: [      {        test: /\.js$/,        exclude: /node_modules/,        use: {          loader: 'babel-loader'        }      },      {        test: /\.css$/,        use: [MiniCssExtractPlugin.loader, 'css-loader']      }    ]  },  plugins: [    new MiniCssExtractPlugin({ filename: '../css/app.css' }),    new CopyWebpackPlugin([{ from: 'static/', to: '../' }])  ]});

Now let’s open our dev.exs and we’ll remove config for brunch and update it for webpack.

config/dev.exs...config :teacher, TeacherWeb.Endpoint,  http: [port: 4000],  debug_errors: true,  code_reloader: true,  check_origin: false,  watchers: [    node: [      "node_modules/webpack/bin/webpack.js",      "--mode",      "development",      "--watch-stdin",      cd: Path.expand("../assets", __DIR__)    ]]...

The last change we need to make now is to open our app.js

And we now need to import our css from our app.js so let’s do that here:

assets/js/app.jsimport css from '../css/app.css';...

With these changes we can go back to the command line.

And run npm install

$ cd assets
$ npm install

Then we can start up our server

$ mix phx.server

And if we go to the browser - everything is working.

Our app is now updated to use Phoenix 1.4 with our new route helpers and with webpack.

Since our application isn’t using Presence, we didn’t cover it in this episode. If you need to update Presence in your app I’ve linked to Chris McCords Phoenix 1.4 update instructions that goes over that.