Running scripts with npm

npm-script

Most people are aware that is is possible to define scripts in package.json which can be run with npm start or npm test, but npm scripts can do a lot more than simply start servers and run tests.

Here is a typical package.json configuration.

start, actually defaults to node server.js, so the above declaration is redundant. In order for the test command to work with mocha, I also need to include it in the devDependencies section (it works in the dependencies section also, but since it is not needed in production it is better to declare it here).

The reason the above test command, mocha --reporter spec test, works is because npm looks for binaries inside node_modules/.bin and when mocha was installed it installed mocha into this directory.

The code that describes what will be installed into the bin directory is defined in mocha's package.json and it looks like this:

As we can see in the above declaration, mocha has two binaries, mocha and _mocha.

Many packages have a bin section, declaring scripts that can be called from npm similar to mocha. To find out what binaries we have in our project we can run ls node_modules/.bin

Invoking Commands

Both start and test are special values and can be invoked directly.

All other values will have to be invoked by npm run. npm run is actually a shortcut of npm run-script.

The above code must be invoked with npm run watch-test, npm watch-test will fail.

Running Binaries Directly

All the above examples consists of running scripts that are declared in package.json but this is not required. Any of the commands in node_modules/.bin can be invoked with npm run. This means that I can invoke mocha by running npm run mocha directly instead of running it with mocha test.

Code Completion

With a lot of modules providing commands it can be difficult to remember what all of them are. Wouldn't it be nice if we could have some command completion to help us out? It turns out we can! npm follows the superb practice of providing its own command completion. By running the command npm completion we get a completion script that we can source to get completion for all the normal npm commands including completion for npm run. Awesome!

I usually put each of my completion script into their own file which I invoke from .bashrc.

Pretty cool!

Combining Commands

The above features gets us a long way but sometimes we want to do more than one thing at a time. It turns out that npm supports this too. npm runs the scripts by passing the line to sh. This allows us to combine commands just as we can do on the command line.

Piping

Lets say that I want to use browserify to pack my Javascript files into a bundle and then I want to minify the bundle with uglifyjs. I can do this by piping (|) the output from browserify into uglifyjs. Simple as pie!

Anding

Another use case for running commands is to run a command only if the previous command is successful. This is easily done with and (&&). Or (||), naturally, also works.

Here I run two scripts declared in my package.json in combination with the command build. Running scripts from other scripts is different from running binaries, they have to prefixed with npm run.

Concurrent

Sometimes it is also nice to be able to run multiple commands at the concurrently. This is easily done by using & to run them as background jobs.

The above scripts contain a few interesting things. First of all watch uses & to run three watch jobs concurrently. When the command is killed, by pressing Ctrl-C, all the jobs are killed, since they are all run with the same parent process.

watchify is a way to run browserify in watch mode. watch-server uses nodemon in the standard way and restarts the server whenever a relevant file has changed.

watch-less users nodemon in a less well-known way. It runs a script when any of the less-files changes and compiles them into CSS by running npm run build-less. Please note that the option --ext less is required for this to work. --exec is the option that allows nodemon to run external commands.

Complex Scripts

For more complex scripts I prefer to write them in Bash, but I usually include a declaration in package.json to run the command. Here, for example, is a small script that deploys the compiled assets to Heroku by adding them to a deploy branch and pushing that branch to Heroku.

Add the script to package.json so that it can be run with npm run deploy.

Conclusion

npm is a lot more than a package manager for Node. By configuring it properly I can handle most of my scripting needs.

Configuring start and test also sets me up for integration with SaaS providers such as Heroku and TravisCI. Another good reason to do it.

This Post Has 33 Comments

  1. Also, if you specify a “postinstall” script, this will be run automatically when you’ve run ‘npm install’.

    Useful for running bower install, for example.

    1. Thanks, I’m glad you liked it.
      I have updated as you suggested. Thanks!

  2. Great post with great examples. Very informative!

  3. @George, thanks for letting me know, I’m glad you liked it. :)

  4. Hello Anders,

    Is it possible to run scripts from a public url

    something like
    “start”: “node http://mysitedomain/index.js
    },

    The nodejs is installed in a different server. I need to run the indes.js from a different server

    Thanks in advance

    1. Hi shruthi,

      You can run an external script by curling it (assuming curl is installed) and piping it into node.
      Something like this:

      This should work as an npm script.

      This will only work for a standalone script since it cannot download any dependencies.

  5. Hello Anders,

    I installed curl using ” npm install curl” and then tried with
    “start”: “curl http://117.240.88.103/myfolder/index.js | node”
    },

    I am getting error-“Failed to exec start script”.

    Is there anything wrong here

    Thanks
    Shruthi

    1. @shrutni, you need to install curl as a system command.
      Depending on your operating system it may already be installed.
      Try running curl --version at the command line.

      If it works you will see something like

      Otherwise install with apt-get on Linux and brew on OSX.

      Anders

      1. This idea of running an external command with curl seems messy (and a bit scary). A npm package to make this easier and safer seems like it wouldn’t be a bad idea. Something like:

        “start”: “npm run-external http://1.2.3.4/myfolder/index.js

  6. Excellent article, thank you so much for taking the time to write this up. I won’t be using Grunt, Gulp, or any of that nonsense anytime soon!

    1. @Ed, I’m glad you liked it.

  7. Nice insight of how the script tag work with NPM and presented in an way easy to understand. Thanks.

    1. @Ashwini, I’m glad you liked it, thanks for letting me know.

  8. Awesome! I didn’t know a lot of this stuff was possible with NPM. You’ve just greatly simplified my understanding of how to deploy node apps! Great concise article. Thanks very much! :)

    1. Thanks! I’m glad you liked it.

  9. Very helpful article, thank you!

  10. Excellent article. This is better than using gulp or grunt. I should have seen this before :)

  11. great article. thanks.

  12. Great tutorial.

    I have problems to following the example in Code Complete.
    After I run $ . <(npm completion), the only thing I see through $ npm run TAB is listed directories.

    What did I miss?

    1. It seems that the completion only works with the scripts in package.json and not the bin
      files anymore. Try to add a few scripts and you will get completion.

  13. Haven’t seen good instructions like this for a while!!!

  14. Good job !! your article is so helpful to me.

    I have soem question about “Running Binaries Directly”
    When i run “npm run mocha” directly, it just produce ” missing script: ~~~~”
    does it work??

    1. You’re right, it doesn’t work anymore. I usually add ./node_modules/.bin to my PATH.

  15. Great article and very informative

  16. Still a very useful article, many thanks for sharing!

  17. It is because of articles like these that actually made me create a tool that works like npm-scripts (kind of), but tries to address its cons :D
    https://www.npmjs.com/package/makfy
    Still in its infancy, but I’m open to suggestions.

  18. I had hoped that the “bin” option, defined in a submodule, would make install a local command in my main module. Apparently, bin only works for modules that are installed globally. Is that true?

    1. Eric, I usually add ./node_modules/.bin to my PATH, then it works for locally installed modules too.

  19. Thanks! I’m learning how to use the “scripts” section of package.json as a new user to Node.js and now I understand why I can run “npm test” directly but not other scripts I add into package.json.

  20. Thanks a Lot……. i ws searching this frm so long

  21. This was an easy read for me because you didn’t assume any knowledge. Very helpful, thank you.

  22. Very very nice article!!

  23. After 8 hours of banging my head against a wall trying to figure out a webpack config, this wonderfully succinct page showing “andify” solved my problem in less than 10min. Thus proving once again, experience is knowing what to find when.

Leave a Reply

Close Menu