Subscribe to access all episodes. View plans →
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.ex
def controller do
...
alias TeacherWeb.Router.Helpers, as: Routes
...
end
def 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.js
const 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.js
import 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.