Node.js apps: what’s the difference between installing npm packages locally and globally?

You don’t need to spend too much time with Google to realise that there’s some amount of confusion about the purpose of global packages. Local packages people seem to grasp easily. Global packages… well, confusion. And if you’re one of those confuse people, don’t feel alone: I was confused too.

Ironic, considering I’m working on the npm support in Node Tools for Visual Studio. But, up until recently, global packages really weren’t that important in the implementation. We gave you a way to install and uninstall them in the alpha, but that was about it. Not much further thought given really:

Managing global packages in NTVS's npm Package Management dialog.

More recently I’ve been improving the way you interact with npm packages, and global packages are back in the frame. And the question is, what are they for? When (and how) should you use them? And when shouldn’t you?

Let’s take a step back. You might already be familiar with the outline structure of a node.js app:

Structure of a node.js app

On the left we’ve got the Explorer tree, and I’ve highlighted the root ExpressApp2 folder of my app (I was just using this for testing). The ExpressApp2 folder you can see above that is the Visual Studio solution folder.

On the right you can see the contents of my Node.js app, which is pretty sparse, along with the Visual Studio ExpressApp2.njsproj project file.

We also have the node_modules folder, where locally installed npm packages are stored. You can see that I’ve got angular, express, jade, and stylus installed locally.

If I want to use one of these modules in my code, I’d write something like:

var express = require('express');

No surprises so far. But what if express wasn’t there, but was instead installed globally? Could I still use it?

Well, yes, and no.

You see the global package installation folder (for such it is) is not just a big bucket of miscellaneous stuff that’s visible and accessible to all your node applications. There’s no way you can say in package.json, “please get this from (or install it to) the globally installed packages”. And if you try to require it in your code, well, that won’t work either.

And this makes sense, right? I mean you could have anything in your global packages so you probably don’t want all of them visible in all your node.js apps. The potential for problems would be significant.

So, back on point, what are global packages for?

Global packages are for anything that you need to access from the shell. By contrast local packages are for using within your apps.

So you’ll often find yourself installing frameworky or infrastructurey things in your global packages. Installed globally they’ll be on your PATH so you can use them anywhere.

A good example is Yeoman and its generators. You’ll use yo from the command line to scaffold out projects in a variety of places, so both Yeoman and the generators you want to use must be globally accessible.

Going back to express, this is an example of a package that you’ll often want to use in your node apps, so you’d normally install it locally. Except that it also has a command line interface… so you’ll want to install it globally?

Well, you can actually do both, and this might be the easiest option.

But, as I said earlier, you can use that global install in your apps. Maybe you have a lot of apps and you want to use the same version of express in all of them. That way, when you upgrade express, you only have to upgrade it globally and all your apps will get the upgrade. Nice, right?

If you wanted to do this, you’d need to link express into your node apps. To try this out, execute the commands below from the shell in a convenient folder. Couple of points:

  • On Windows you’ll need to run the Command Prompt as administrator, or you won’t be able to create the link,
  • For the npm init command, which will scaffold out a basic package.json for you, just accept all the default suggestions.
mkdir linktest
cd linktest
npm init
npm install express -g
npm link express

Unfortunately, unlike the npm install command, there’s no way to save express into your package.json dependencies list. You’ll need to edit the file and add it yourself. However, you should find that your linktest folder now contains a node_modules folder. Within that, you’ll find a symbolic link to your global express install. You can obviously drill into that folder just as if it were really in the linktest folder.

Now, as I said, if you update your global install of express your linktest node app will also get the update.

Hopefully that clears up the differences between local and global packages.

TAKE-HOME MESSAGE: always try to use local packages unless there’s a very good reason not to! (I.e., you need to use the package from the command line.)

 

13 thoughts on “Node.js apps: what’s the difference between installing npm packages locally and globally?

  1. Pingback: แตกต่างอย่างไร? ในการติดตั้ง Node JS แบบ Global และ Local - Nextflow

  2. Sean

    I was getting confused about this just the other. It wasn’t really clear to me when I should be installing things globally and when I should install them locally for a specific project. Thanks for clearing that up.

    Reply
    1. bart Post author

      Yeah, it’s far from obvious. At the time I’d started working on NTVS the global/local package documentation was so sketchy I came away with the impression that global packages were implicitly installed for all node apps, which isn’t the case at all (although if they have shell commands on the PATH these obviously would be available).

      Reply
  3. Sanfords

    Seems like a good feature would be for a package to tell npm what kind of loading it prefers and do it.
    The npm link docs seem to prefer you to install locally and link globally, but the reverse could be done.
    I would think that if a local install is asked for and a global install exists with the same version being installed, it should automatically link the local one to save space.
    I think some deeper thought on this whole local/global thing could yield automatic linking as appropriate and fix the confusion.
    A very helpful article for sure!

    Reply
    1. bart Post author

      Thanks, glad you liked it! It definitely ought to be possible to install all packages – by which I mean package versions, since that’s what you’re really dealing with – globally and link from each node app that uses them. It might sidestep the MAXPATH problem on Windows too.

      Windows obviously has support for long paths – has done for years – and both node and npm handle long paths just fine on Windows. That said, things can get pretty ugly if you’re working with, say, a CI system such as TeamCity, and your clean step tries to remove a folder that has node_modules underneath it with very deep subdirectories, which happens often. I still haven’t found a great solution to this situation.

      I think fundamentally, whilst the nested directory tree is simple to work with and implement, it’s not actually a great solution for a system that’s supposed to be cross platform. This has been discussed at some length in the npm project but it seems unlikely it’s going to change.

      Reply
  4. sorcerer

    If some of the packages I want to install is local, e.g. Kerberos, and it is depending on mongodb. Then mongodb should be install locally. Correct?
    What if I have some packages that need to be install globally, e.g. Yeoman, I should install mongodb globally. Correct?
    Since I want to install both, what should I be doing?
    I can’t install mongodb globally and locally because mongodb will not syn between the local and global install.
    Please advise.

    Reply
    1. bart Post author

      Hi sorcerer, I’m slightly confused. Are you talking about installing mongodb (as in, the No SQL document database), or the mongodb driver for Node.js (https://mongodb.github.io/node-mongodb-native/)?

      If you mean the database, then that’s not an npm package. You need to download the appropriate version for your operating system from https://www.mongodb.com/download-center and install it on your system.

      Having done this you’ll then need to install the mongodb driver npm package: https://mongodb.github.io/node-mongodb-native/. I would recommend you install this locally in your node project.

      I don’t understand why you’d need to sync, or rather what you mean when you talk about, syncing between local global packages. I’m not sure why keeping these in sync would be necessary. If you want to guarantee the use of specific package versions with your project you should install those versions locally and reference them as dependencies, using the specific version numbers you need, in your package.json file.

      Hope that’s helpful.

      Reply
  5. Ernani

    Hi nice article by the way, asked this question in stack overflow and they think im asking a stupid question. When you install express wouldnt it be better to do a –save so it goes straight to your package.json instead of your local module folder alone? Or why dont people just use –save everytime they install a package of course considering it is a local package of course? When would you actually do a –save and when its not? Thank you

    Reply

Leave a Reply to bart Cancel reply

Your email address will not be published. Required fields are marked *