So first, an admission: I've never really understood how Node.js works, especially its relationship to npm, globally installed packages, and honestly even where the hell it lived on my computer. My lack of understanding led to the point where I didn't even update to new Node.js versions after having done so in the past and watching all my projects break.

Well, it just seemed like it was time to bite the bullet and figure it all out. This post is in part a rehash of all the steps I took after scouring the Interwebs for the best info I could find to help others struggling to figure this out as well as for myself to remember how I got through the process.

And now a disclaimer and an ask: There are definitely multiple ways to do pretty much everything I laid out here, so I basically just figured out what was easiest for me and went with it. Also, I'm using a Mac so this post is tailored only for that. If you're reading this and have suggestions for better ways to get through this whole process or see anything I clearly got wrong, please comment and I'll make corrections.

Step 1: The Current Situation

I was currently running Node.js v8.11.2 and decided to make the jump to v8.11.4. A very tiny jump, but I figured I'd start there and make sure things still worked before I made the jump up to v10.x.x. I use nvm (Node Version Manager - click here for install guide) to install and manage my Node.js versions. It's easy to use for a lot of reasons, some of which I'll touch on later.

Step 2: Install Node.js

You can simply run nvm install node for the latest release, or specify a version number: nvm install 8.11.4. Once the install is done, you can run nvm use <version>, in my case nvm use 8.11.4 to use the newly installed version, and nvm alias <alias name> 8.11.4 to set that Node.js version's alias to whatever you want. I used "default", so my command was just nvm alias default 8.11.4. You can also double check that it's all good to go with node -v which gives you the currently running version of Node.js, and/or nvm ls to list all the versions of Node.js on your computer.

Step 3: Updating npm

I didn't previously understand the relationship between Node.js and npm, but learned that when you install any version of Node.js, that version will also install a specific version of npm. This link shows which npm versions are installed with each Node version. So when installing Node.js v8.11.4, it automatically installed npm v5.6.0. Using nvm, these npm installs are stored in the same folder as other globally installed packages (more on that later).

I wanted to then update my version of npm to the most current one, which in my case is v6.4.0. I probably didn't need to, but I really wanted to give the new npm audit a go as it's a new feature in npm v6 (and it is pretty cool). So in order to do that, I simply ran npm install -g [email protected] which installed the latest version of npm as a global package within the node_modules/ folder of the newly installed version of Node.js. For reference, on my machine, this folder is located, from the root directory, at /Users/dan/.nvm/versions/node/v8.11.4/lib/node_modules/.

Step 4: Global Packages

So one of the main things I never knew was that when you install an npm package globally, for example npm install -g create-react-app, that package is installed within the same node_modules/ folder as the version of npm tied to that version of Node.js (same folder path as in the previous paragraph). So when you update versions of Node.js, you "lose" those globally installed packages because the new Node.js version doesn't have access to them. So when I would install something like Webpack globally, I could use it in an existing project. But when I changed Node.js versions, I lost access to it and got a breaking error when I tried running that project again because of course it couldn't find Webpack to compile it. (Which for me is a pretty good argument against installing packages globally unless I absolutely have to).

So I made the decision to only install those packages that I knew I needed to use globally. In my case one of those is trash-cli, which gives me access to the macOS trash can from the command line instead of relying on the rm command where you delete stuff and pray you never need it again because it is gone forever. So a quick npm install -g trash-cli got the job done, and it was easy to create a temp file and/or folder and run the trash command to test. Everything good to go, so I installed a few more packages globally and was ready to keep going.

For future Node.js installs, global packages can easily be reinstalled as a part of the Node installed with the following command: nvm install node --reinstall-packages-from=node. This command specifically installs the latest Node.js version and reinstalls the global packages being used by the currently running Node.js version. You can also specify versions to install and reinstall from, for example: nvm install 6 --reinstall-packages-from=5. And even better, you can run nvm install node --reinstall-packages-from=node --latest-npm. In addition to installing the latest Node.js version and reinstalling global packages, this also installs the latest version of npm all in one line of code. So that's fun.

Step 5: Testing Projects

The next thing for me to do was to see if what I did actually worked. As I was using Node.js v8.11.2 and only updated to v8.11.4, I didn't know what to expect. Turns out projects I'd been working on in v8.11.2 worked without a hitch in v8.11.4. I didn't seem to need to run npm rebuild probably because it was such a minor version update there weren't any breaking changes. So then I reverted back to Node.js v6.10.0 and tried running the project again. No dice. Now running an npm rebuild got the project working in v6.10.0. Switching back to v8.11.4, and trying to run the project again, still broken. But another npm rebuild got it up and running again.

And this was one of the biggest "aha" moments for me. Up until now, I had had no idea why projects broke when updating Node.js versions. Basically, the node_modules/ created in a project when running an npm install builds those modules into the project based on the current version of Node.js being used.

Think about it like this: So if you wore a size 6 shoe, and bought a pair of size 6 shoes because obviously you'd want the shoes around your feet to fit, that would be all good. But then a year goes by and you now wear size 8 shoes (massive growth spurt). What happens if you try and put on the size 6 shoes? Nothing good, and it won't work. So you have to buy a new pair of shoes in size 8. In that example, think of the current Node.js version you're running as the size of your feet, version 6, the things everything else has to be able to be able to work on. The size 6 shoes are the jQuery package you npm installed. And your size 8 feet are the new Node.js version you installed, version 8 (pretend installed ==> grew into). So your size 6 jQuery shoes don't work on your size 8 Node.js v8 feet, and you have to npm rebuild your jQuery shoes by buying a new size 8 version. I hope that makes sense because typing it kinda made my brain scream.

So in short, when running an existing project for the first time after updating Node.js, a simple npm rebuild should do the trick.

Side note: I use Netlify to deploy a lot of my projects. For some, they will require me to include an .nvmrc file which includes the version of Node.js used in the project. This can easily be done in the command line with node -v > .nvmrc.

Step 6: Deleting Old Node.js Versions

My last step in the process was to get rid of the old versions of Node.js I had sitting around. This isn't necessary and for some people it's probably not the best thing to do, but I'm not in a situation where I have to worry about using a version of Node.js other than whatever I want to use. Using nvm, this is easily accomplished with nvm uninstall <version number>.

Note on a bug: I ran into an issue with one project in particular that uses the package node-sass. It turns out this package was still bound to a previous version of Node.js I'd been using. I'm not going to pretend I 100% understand everything that was going on with this other than to say that npm rebuild node-sass fixed the problem.

And that's it! Everything is working fine and after finally forcing myself to learn how to do this, it'll be easy for me to continue updating Node.js whenever I feel the need.

Quick Summary:

  1. Install nvm if don’t already have it
  2. Then:
    • nvm install node for the latest release or nvm install <version>
    • nvm use <version>
    • nvm alias <alias name> <version> to set an alias for that version
    • node -v or nvm ls to make sure things are set up correctly
    • npm install -g [email protected] to install the latest version of npm (and maybe override what was installed with the new Node.js version by default)
    • Install global packages with npm install -g <package-name>
    • (an alternative is to install everything at once with nvm install node --reinstall-packages-from=node --latest-npm)
  3. nvm uninstall <version> to remove old version(s) of Node.js
  4. And for individual projects, run npm rebuild in the project's root directory