Using NPM with Heroku Node.js

The Node Knockout is this weekend right now and I've been trying to teach myself Node and get ready in a variety of ways. One of the most important (and least clear) aspects of preparation was figuring out how to properly vendor the latest Node libraries for use in Heroku. I've got NPM up and running locally, but as of its latest release it has no built-in support for vendoring. Here's how I managed.

Updated Method

Since this post, the maintainers of NPM have released an updated version with a new command: bundle. Here's how to use it (make sure you have NPM >= 0.1.27 for this to work).

First, create a package.json file in your project's root directory, and populate it with dependencies like so:

{
  "name":"yourproject",
  "version":"0.0.1",
  "dependencies":{
    "express":"1.0.0rc2",
    "ejs":""
  }
}

If you don't need a specific version, just specify a blank string. Now that you've created a package.json, you need to run bundle:

npm bundle ./vendor

This will bundle the dependencies specified into the vendor directory of your project. Now you're almost done! The last step is to add the vendor directory to your load paths:

require.paths.unshift('./vendor')

That's it! You can now require the dependencies you've specified like you would if NPM were installed normally (i.e. without special functions or directory specifiers). Happy Node-ing!

Old Method

Warning: I am not an experienced Node developer and I may be doing this completely wrong. However, I am happy to publish this anyway with the hope that more learned Javascripters will correct my naive ways.

NPM's Command Options

NPM provides a few helpful command options that let you specify the directory of installation when you install a library. By using the --root and --binroot options you can use a folder inside your local project instead of the default NPM root. For example, in my project I created a vendor folder and then, to install Express, specified:

npm install express --binroot ./vendor --root ./vendor

Now while this seems like it might do exactly what we want, it's not quite perfect. Rather than install everything in vendor, it installs the important stuff in vendor/.npm/.cache. This isn't the end of the world but can make for some pretty ugly require statements.

The Require Blues

So now you've got your packages all nice and installed inside your project directory (probably a lot of support files that you don't need, as well, but the Node slugs on Heroku tend to be small anyway so no biggie). Now you need to include them in your application. To do this, I wrote a quick function, vrequire that adds the necessary load path and then requires the library all at once:

vrequire = function(lib) { 
  require.paths.unshift("vendor/.npm/" + lib + "/active/package/lib");
  return require(lib); 
}

Now if I call vrequire("express") it will load up from my vendored library. If I deploy my app to Heroku, it's able to find everything it needs and (cross your fingers) launch and work correctly!

Hopefully some people find this little guide useful, and I look forward to someone pointing me to the "real" way to do this stuff! If you want to poke around the exploratory code I've been creating to play with Node, you can find it on GitHub (but don't expect much!).