Heroku | 10 Habits of a Happy Node Hacker
For most of the nearly twenty years since its inception, JavaScript lacked many of the niceties
that made other programming languages like Python and Ruby so attractive: command-line interfaces, a
REPL, a package manager, and an organized open-source community. Thanks in part to Node.js and npm,
today’s JavaScript landscape is dramatically improved. Web developers wield powerful
new tools, and are limited only by their imagination.
What follows is a list of tips and techniques to keep you and your node apps happy.
npm includes an init
command which walks you through the process of creating a
package.json file. Even if you’re intimately familiar with package.json and its properties, npm init
is a convenient way to get your new node app or
module started on the right track. It sets smart defaults for you, like
inferring the module name from the parent directory name, reading your author
info from ~/.npmrc
, and using your git settings to determine repository
.
mkdir my-node-app
cd my-node-app
npm init
It’s good to get in the habit of using --save
(or --save-dev
) every time you
install a module that’s local to your project. These flags add the given module to
your package.json’s dependencies
(or devDependencies
) list and use a
sensible default semver range.
npm install domready --save
Notice that npm now uses caret-style semver
ranges:
"dependencies": {
"domready": "^1.0.4"
}
Setting a value for scripts.start
in package.json allows you to start your app
on the command line with npm start
. This is a great convention to follow, as
it allows any node developer to clone your app and get it running easily without
any guesswork.
Bonus: If you define scripts.start
in your package.json file, you don’t need
a Procfile. A Procfile will be created automatically using npm start
as the
web
process.
Here’s an example start script:
"scripts": {
"start": "node index.js"
}
Just as anyone on your team should be able to run your app, they should also be
able to test it. The scripts.test
field in package.json is used to specify the
script to run your test suite. If you’re using something like mocha
to run
tests, be sure to include it in devDependencies
in package.json, and refer to
the binary that’s local to your project, rather than the mocha
you have
installed globally:
"scripts": {
"test": "mocha"
}
Many node apps use npm modules with C dependencies like bson
, ws
, and hiredis
that must be compiled for Heroku’s 64-bit Linux architecture. This
compilation step can be a time-consuming process. To keep builds as fast as possible, Heroku’s
node buildpack caches
dependencies
after they’re downloaded and compiled so they can be re-used on subsequent
deploys. This cache means reduced network traffic and fewer compiles.
Ignoring the node_modules
directory is also the npm recommended
practice for module authors. One less distinction between apps and modules!
echo node_modules >> .gitignore
From the npm config docs:
Any environment variables that start with
npm_config_
will be interpreted as
a configuration parameter. For example, puttingnpm_config_foo=bar
in your
environment will set thefoo
configuration parameter tobar
. Any environment
configurations that are not given a value will be given the value of true.
Config values are case-insensitive, soNPM_CONFIG_FOO=bar
will work the same.
As of recently, app environment is available in all Heroku
builds. This change gives
node users on Heroku control over their npm configuration without having to
make changes to application code. Habit #7 is a perfect example of this
approach.
The public npm registry has seen immense growth in
recent years, and with that has come occasional instability. As a result, many
node users are seeking alternatives to the public registry, either for the sake
of speed and stability in the development and build cycle, or as a means to host
private node modules.
A number or alternative npm registries have popped up in recent months.
Nodejitsu and
Gemfury offer paid private
registries, and there are some free alternatives such as Mozilla’s read-only
S3/CloudFront mirror and Maciej
Małecki’s European mirror.
Configuring your Heroku node app to use a custom registry is easy:
heroku config:set npm_config_registry=http://registry.npmjs.eu
If you’ve been programming long enough, you’ve probably been to dependency
hell. Fortunately Node.js and npm
have set a precedent for sane dependency management by embracing semver, the
Semantic Versioning Specification. Under this scheme,
version numbers and the way they change convey meaning about the underlying code
and what has been modified from one version to the next.
npm has a litte-known command called outdated
. Combined with npm update
, it’s
a great tool for figuring out which of your app’s dependencies have fallen behind and
need to be updated:
cd my-node-app
npm outdated
Package Current Wanted Latest Location
------- ------- ------ ------ --------
express 3.4.8 3.4.8 4.0.0-rc2 express
jade 1.1.5 1.1.5 1.3.0 jade
cors 2.1.1 2.1.1 2.2.0 cors
jade 0.26.3 0.26.3 1.3.0 mocha > jade
diff 1.0.7 1.0.7 1.0.8 mocha > diff
glob 3.2.3 3.2.3 3.2.9 mocha > glob
commander 2.0.0 2.0.0 2.1.0 mocha > commander
If you’re working on open-source node apps or modules, check out
david-dm, NodeICO, and shields.io, three great
services that provide graphical badges you can use to display live dependency info on your project’s README or website.
As the npm ecosystem continues to grow, so do the options for automating the
development and build process. Grunt is by far the most
popular build tool in the node world today, but new tools like
gulp.js and plain old npm
scripts are also attractive
options with lighter footprints.
When you deploy a node app to Heroku, the npm install --production
command is
run to ensure your app’s npm dependencies are downloaded and installed. But the
command does something else too: It runs any npm script
hooks that you’ve defined in your
package.json file, such as preinstall
and postinstall
. Here’s a sample:
{
"name": "my-node-app",
"version": "1.2.3",
"scripts": {
"preinstall": "echo here it comes!",
"postinstall": "echo there it goes!",
"start": "node index.js",
"test": "tap test/*.js"
}
}
These scripts can be inline bash commands or they can refer to command-line
executables.
You can also refer to other npm scripts from within a script:
{
"scripts": {
"postinstall": "npm run build && npm run rejoice",
"build": "grunt",
"rejoice": "echo yay!",
"start": "node index.js"
}
}
Harmony is the working name for ES6, the next edition of the ECMAScript language
specification commonly known as JavaScript. Harmony brings lots of exciting new
features to JavaScript, many of which are already available in newer versions of
node.
Harmony enables many new features like block
scoping,
generators,
proxies, weak
maps, etc.
To enable harmony features in your node app, specify a newer node engine like 0.11.x
and set
the --harmony
flag in your start script:
{
"scripts": {
"start": "node --harmony index.js"
},
"engines": {
"node": "0.11.x"
}
}
Client-side JavaScript has a spaghetti-code legacy, but the language itself is
not to blame. The lack of a legitimate dependency manager is what
kept us in the jQuery-plugin copy-pasta dark ages for so many years. Thanks to
npm, we’re entering the front-end renaissance: the npm registry is growing like
crazy, and the proliferation of modules designed to work in the browser is
staggering.
Browserify is an
amazing tool that makes node modules work in the browser. If you’re a front-end
developer, browserify could change your life. Maybe not today, and maybe not
tomorrow, but soon. To get started using browserify, check out the articles.
Whether you’ve been developing in node for a while or are just getting
started, we
hope you find these tips useful. If you’ve got some (healthy) node habits to
share, tweet about it with the
#node_habits
hashtag. Happy hacking!
By the way, we’re hiring node people!