Feathers Buzzard: a short migration guide
Tips & pitfalls when upgrading to the latest Feathers and Vue
Note - updated this article on February 27, 2018: I’ve modified .babelrc so that Webpack’s “tree shaking” capabilities are utilized.
Feathers is a great Javascript/Node framework for quickly building REST or web socket based APIs. It’s like a “‘batteries included” version of Express, meaning you spend less time on the “plumbing” of your app.
Feathers helps you to build not only the server but also the client, and it makes it very easy to connect the two.
(but chances are that, if you read this article, you already know what Feathers is all about …)
Recently a new Feathers version (V3, code name Buzzard) was released. Upgrading is easy and is largely automated, however when migrating my Feathers/Vue based app, I ran into a few gotchas that I would like to share.
Background
The app that I wanted to migrate consisted of two parts: a backend (server) and a frontend (client). The backend of my app was plain vanilla Feathers, the frontend was based on Vue.js (with Vuex) and the Quasar framework.
Here’s how I migrated the app. You can see the results (after migration) here for the backend and here for the frontend.
Migrating the backend
I simply followed the upgrade guide, however I decided to first upgrade my node.js installation to a more recent version.
I had node 6.9.1 installed, and Feathers Buzzard does work with that, however a lot of the documentation and code samples now use async/await which isn’t supported by node 6.x. So, I decided to upgrade to node 8.9.4 (the current LTS version). This is easy using nvm:
nvm install 8.9.4
nvm reinstall-packages 6.9.1
The command “nvm reinstall-packages 6.9.1” migrates the globally installed npm packages from node 6.9.1 to 8.9.4 (this is a ‘copy’, not a ‘move’ — the packages are still available under 6.9.1).
Finally, edit your .bashrc
file to make node 8.9.4 the default. In my .basrc
it looks like this:
export NVM_DIR="$HOME/.nvm". "$NVM_DIR/nvm.sh"
nvm alias default v8.9.4
Upgrade the Feathers CLI as follows:
npm uninstall feathers-cli -g
npm install @feathersjs/cli -g
Now the actual backend migration. “cd” into the project directory of the backend (server) and execute the command:
feathers upgrade
This does all the hard work for you, automatically. Kudos to the Feathers team! After that you need to perform a few simple manual steps, all documented in the upgrade guide.
Migrating the frontend
Migrating the frontend was a little bit more work.
I started, again, by simply executing:
feathers upgrade
But now the fun begins, there were quite a lot of little issues. No doubt this is because the client app has a lot more tooling and ‘moving parts’ than the server app: Feathers client, Vue, Vuex, Quasar, Webpack, etc etc.
When I tried to run the app in dev mode using ‘npm run dev’ I saw errors displayed in the terminal and/or in your browser’s Javascript console (for instance if you use Chrome Dev Tools).
Rather than describing the individual errors, I will just explain which changes I made to get rid of them.
Fix: incorrect socketio-client import
In the file src/api/client.js, “feathers upgrade” changes the line import socketio from ‘feathers-socketio’
to the following:
import socketio from '@feathersjs/socketio'
I had to manually change this line to:
import socketio from '@feathersjs/socketio-client'
Note: this is only needed if you use socket.io as your API transport, if you just use REST then this is not an issue.
In the same file I had to manually remove the .configure(hooks())
line (feathers upgrade
will remove this line if it’s a separate call: client.configure(hooks())
, but not if it’s part of a “chain” of function calls).
Fix: missing dependencies in package.json
Also, when using socket.io as your API transport, you will need to add some additional dependencies, as follows:
npm install -save engine.io-client isarray socket.io-parser
I’m not sure about the reason for this, but it might be that the new version of npm that we’re using (npm v.5.6.0) does not automatically include these ‘transitive’ dependencies of the socket-io library.
Fix: Babel/Webpack issues
As pointed out in the Feathers documentation, I made the following change in the build/webpack.base.conf.js
file — I changed the line:
exclude: /node_modules/
to:
exclude: /node_modules(\/|\\)(?!(@feathersjs))/
I also commented out the whole eslint
block under module
/rules
, because apparently my code style (especially the Vue.js part) didn’t meet the default eslint
rules (resulting in a lot of errors).
Finally (and most importantly) I changed my .babelrc
file as follows:
{
"presets": [
[
"env",
{
"loose": true,
"modules": false,
"targets": {
"browsers": ["> 1%"]
},
"exclude": [
"transform-es2015-typeof-symbol"
]
}
]
],
"plugins": [
"syntax-dynamic-import",
"transform-decorators-legacy",
"transform-object-assign",
"transform-class-properties",
"transform-object-rest-spread"
],
"comments": false
}
The reasons for these changes are discussed in this Github issue.
Notes:
- Readers of a previous version of this article will notice that the .babelrc file has changed a lot, notably I’m now using the newest “babel-preset-env” plugin; this configuration ensures that Webpack’s “tree shaking” works correctly.
- If you want to use ES6 ‘async/await’ syntax on the client, then use the following .babelrc variant using the “fast-async” plugin (note however that this might make your app’s bundle size bigger, right now I’m sticking to using Promises rather than async/await on the client):
{
"presets": [
[
"env",
{
"loose": true,
"modules": false,
"targets": {
"browsers": ["> 1%"]
},
"exclude": [
"transform-async-to-generator",
"transform-regenerator",
"transform-es2015-typeof-symbol"
]
}
]
],
"plugins": [
"syntax-dynamic-import",
"transform-decorators-legacy",
"transform-object-assign",
"transform-class-properties",
"transform-object-rest-spread",
["fast-async", {
"compiler": {
"promises": true,
"generators": false,
"noRuntime": true,
"wrapAwait": true
},
"useRuntimeModule": false
}]
],
"comments": false
}
Fix: Quasar/Vue.js routing
The final fix was related to Vue.js and to Quasar (the Vue.js component library that I used).
I wanted to upgrade the app to the newest version of Vue.js (and vuex, and vue-router) and Quasar. To do this, I used the Quasar CLI to generate a new ‘default’ app:
quasar init myapp
and then copied the dependencies out of the new app’s package.json
to my app’s package file, and did an npm install
.
When trying to run the app (npm run dev
), I encountered a Vue router ‘component not found’ error (Chrome dev tools console tab). The solution (after a good deal of Googling) was to change the Quasar app initialisation in my main.js file from:
Quasar.start(() => {
/* eslint-disable no-new */
new Vue({
el: '#q-app',
store,
router,
render: h => h(require('./App'))
})
})
to:
Quasar.start(() => {
/* eslint-disable no-new */
new Vue({
el: '#q-app',
store,
router,
render: h => h(require('./App').default)
})
})
with the relevant change being the addition of “.default” to the ‘require’ clause:
h => h(require('./App').default)
This is probably related to the “breaking changes’ in vue-loader
described here.
Thanks for reading!
I hope the information in this article will be useful to anybody.
Main takeaway: migrating the server part was trivially easy, migrating the client was more complicated, due to the many ‘moving parts’ (Babel, Webpack, Vue, Quasar, Feathers Client).
Full code (i.e. the final results after the migration) are available here for the server and here for the client.
Thanks for reading, and if you like this article then don’t forget to clap!