Sharing Open Source Node.js Libraries with Node Package Manager (npm)
Software
Wednesday, 14 July 2010 09:39

My last post was about a library I wrote for node.js for connecting to Amazon's Product Advertising API (called node-apac). Once I had written the barebones version, I wanted to share it to others that might be looking for the same thing and so my search began to find out the best way to share open source software for node.js. I was looking for something akin to Perl's CPAN.

Kiwi

The first thing I came across was kiwi. Kiwi works almost identically to CPAN, which is what I was looking for. In the end, Kiwi turned out to not be the best option, so I'm not going to describe everything in detail. But here's the idea of how it works. There is a web-accessible repository of libraries that is accessible via a command line interface. Using the command line interface, you can both submit packages to the repository and install packages from the repository.

Node Package Manager (npm)

So, what's wrong with kiwi? Not that much really, except that it's not the "popular" one and, with package managers, that is crucial. I asked around in the #Node.js IRC channel and was informed that Node Package Manager (npm) is the latest and greatest and has the most momentum and community support. At least for the foreseeable future, npm is what you want to use. Conceptually there's not very much different from Kiwi. There is still an online repository that you access via a command line utility. There is one thing I found significant however. With Kiwi, you have to use kiwi in your code to require libraries. For example, to use the express.js web development framework, after installing via kiwi, you would do this in your code:

var kiwi = require('kiwi'),
    express = kiwi.require('express');

You can see here that you become dependent on kiwi by using it. npm is much better at staying out of the way. npm installs packages so that they can be found with a normal require() call. So, you can switch from using npm to installing packages by some other means without having to change your code.

Brief Tutorial on npm

What I'd like to do at this point is to give you a jump start with npm. After finishing this article you should be able to use npm to publish a library you have written or use npm to install a package you need. However, I should state first that node.js and npm are not well supported on platforms other than linux. I'm pretty sure support is planned, but for the time being, you might just find yourself up against a lot of frustration.

Getting npm

To get started, we have to work out some dependencies first. Basically all you need is node.js. You also need v8, but installing node.js will take care of that for you. So start by going to the node.js download page and follow the installation instructions there. Next, go to npm's github page and, once again, follow the installation instructions there.

Adding code to the repository

Adding a library of your own is very easy, I promise. All you have to do is a minor code change (a handful of lines), create a file called package.json (around 20 lines for a simple project), create an npm account and publish your code to npm. Let's take a closer look at each step using node-apac as an example.

In node-apac, there are two "classes", each in a separate file, that I'd like to make available to anyone that uses the package. However, using npm, as far as I know, you can only specify one file as the main file for exporting things. So, I had to make a third file that imports the other two and exports everything I want all at once. Here's what it looks like:

exports.OperationHelper =
    require('./OperationHelper').OperationHelper;
exports.RequestSignatureHelper =
    require('./RequestSignatureHelper').RequestSignatureHelper;

That's pretty simple, right? Then, when using the library, if someone wants to use the OperationalHelper, they would do something like this:

 

var opHlpr = require('apac').OperationalHelper;

Again, pretty simple stuff here. Now let's look at the package.json file. This is the file that explains all about your package. In this file you can give it a name, description, tell it where to find that "main" JS file where you export everything and a bunch of other fancy stuff like dependencies. Let's take a brief look at node-apac's package.json:

{ "name" : "apac"
, "description" : "Amazon Product Advertising API Client for Node"
, "version" : "0.0.1"
, "author" : "Dustin McQuay "
, "repository" :
  { "type" : "git"
  , "url" : "git://github.com/dmcquay/node-apac.git"
  }
, "bugs" : { "web" : "http://github.com/dmcquay/node-apac/issues" }
, "os" : [ "linux", "darwin", "freebsd" ]
, "directories" : { "lib" : "./lib/" }
, "main" : "./lib/apac"
, "engines" : { "node" : ">=0.1.97" }
, "dependencies" : { "libxmljs" : "*" },
, "licenses" :
  [ { "type" : "MIT"
    , "url" : "http://github.com/dmcquay/node-apac/raw/master/LICENSE"
    }   
  ]
}

This is a fairly minimal package.json file. You can read more about the format of this file at http://github.com/isaacs/npm/blob/master/doc/json.md. I'm going to skip over the obvious stuff. First let's look at version. This is an important field to npm. It is used for dependency resolution and must follow a specific format. You should read more at semver.org, but the minimal to understand for now is that it must be three integers separated by periods. The next interesting field is "main". This is where to find the main JS file that we wrote earlier. I have placed mine in a directory called lib as you can see. The name of the file is apac.js, but you leave off the ".js" here for some reason. This library is written for node, so I include that as in engine and it depends on libxmljs so I include that in the dependencies. The "*" means that any version of the library will do.

Everything is pretty well set up now so we're ready to create an npm account and publish our code. Creating an npm account is done on the command line using the "npm adduser" command. With npm, commands are issued in the "npm [subcommand]" format, and in this case, adduser is the subcommand. To learn more about the adduser subcommand, just type "npm help adduser" or similar for any other subcommand. To create our user, it will look something like this:

npm adduser <username> <password> <email>

Ya, this is a little bit lame. Your password is going to end up in your history file and another plain text config file and I'm guessing it is sent over the network in plain text each time you authenticate. So don't use your online banking password for this.

We're almost done! Now all you have to do is issue the "npm publish" command. You need to be in the root of your project, where the package.json file should also be located. Then just type "npm publish". Yay, you did it!

 

If you got lost anywhere, you might find it useful to take a look at the source for node-apac for a complete, working example.

Installing a Package

Now that we've added node-apac to the registry, we can now install it using the npm install command:

npm install node-apac

That doesn't work though! Ya, I know. It will for most packages, but if you make a new package like this that is a pre 1.0.0 release, it won't know which version of the package to install. The reason is that it is going to look for the latest version tagged with @stable, but there is none since we have not tagged any versions as stable yet. Furthermore, I don't think you can tag a pre 1.0.0 release as stable. All we have to do to get around this is to specify that we want to install the version tagged as @latest, which is always the most recent version. Let's try again.

npm install node-apac@latest

blog comments powered by Disqus