Learn more about Russian war crimes in Ukraine.

NPM addon package hello world

In a previous post I showed how to build an addon.node file which can then be used as a Node.js module with require('./addon'). These C++ addons are frequently distributed via NPM. Let’s make an NPM package from some C++.

Typically, an npm package does not contain any .node files. Instead, it will somehow generate the appropriate .node files at install time, for the correct architecture, OS, Node.js version, et cetera. When you npm install a package, the package can specify arbitrary scripts to run at install time, like this:

  "name": "arithmetic",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "preinstall": "echo preinstalling",
    "install": "echo installing",
    "postinstall": "echo postinstalling"

If you npm install this package, it will print

$ npm install ../arithmetic

If you use npm, you probably know that “https://github.com/zloirock is looking for a good job”. You know this due to the noisy postinstall script on the core-js package which everything depends on.

The install script can do whatever it likes to generate the .node files. The install script in the node-sass npm package downloads the prebuilt files. But more typically, it will compile them from C++ source. This convention is strong enough that it’s built into npm. According to the docs,

If there is a binding.gyp file in the root of your package and you haven’t defined your own install or preinstall scripts, npm will default the install command to "node-gyp rebuild".

The package needs to ensure the node-gyp tool is available, typically by adding the node-gyp npm package as a dependency (not a dev-dependency!).

The node-gyp tool looks for a binding.gyp file. Here is ours:

# binding.gyp
  "targets": [
      "target_name": "arithmetic",
      "sources": [ "arithmetic.cc" ]

This will compile build/Release/arithmetic.node from our source file arithmetic.cc. Here it is, a module that defines a single function increment:

#include <assert.h>
#include <node_api.h>
#include <stdio.h>
napi_value Increment(napi_env env, napi_callback_info cb_info) {
  napi_status status;

  size_t argc = 1;
  napi_value args[1];
  status = napi_get_cb_info(env, cb_info, &argc, args, nullptr, nullptr);
  assert(status == napi_ok);

  double arg_value;
  status = napi_get_value_double(env, args[0], &arg_value);
  assert(status == napi_ok);

  napi_value return_value;
  status = napi_create_double(env, arg_value + 1.0, &return_value);
  assert(status == napi_ok);

  return return_value;

napi_value Init(napi_env env, napi_value exports) {
  napi_status status;
  napi_property_descriptor incrementDescriptor = { "increment", 0, Increment, 0, 0, 0, napi_default, 0 };
  status = napi_define_properties(env, exports, 1, &incrementDescriptor);
  assert(status == napi_ok);
  return exports;


(I’ll write a future post on how this napi_ API works, and the alternative C++ APIs for writing Node.js addons.)

For me, node-gyp places its output at build/Release/arithmetic.node. You could set "main": "build/Release/arithmetic.node" in your package. But more typically, you wrap your native addon with a JavaScript module. This can provide a more idiomatic JavaScript API. Here’s ours:

const nativeArithmetic = require('./build/Release/arithmetic.node');
exports.increment = function(n) {
    if (typeof n !== 'number') {
        throw new Error("Expected one numeric argument");
    return nativeArithmetic.increment(n);

Our wrapper module checks types before calling into the native module. (If the native module is given bad arguments, an assert fails, causing the process to abort! Nastier than a thrown exception!)

While node-gyp puts its output at build/Release/arithmetic.node in my configuration, apparently it can place its output in several possible locations. The bindings package helps here, and tries to require() the module from common output locations. We use it like this:

const nativeArithmetic = require('bindings')('arithmetic.node');

Finally, here is the package.json with the important properties:

  "name": "arithmetic",
  "version": "0.0.1",
  "main": "index.js",
  "scripts": {
    "install": "node-gyp rebuild"
  "dependencies": {
    "bindings": "^1.5.0",
    "node-gyp": "^7.1.2"

What can computers do? What are the limits of mathematics? And just how busy can a busy beaver be? This year, I’m writing Busy Beavers, a unique interactive book on computability theory. You and I will take a practical and modern approach to answering these questions — or at least learning why some questions are unanswerable!

It’s only $19, and you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

After months of secret toil, I and Andrew Carr released Everyday Data Science, a unique interactive online course! You’ll make the perfect glass of lemonade using Thompson sampling. You’ll lose weight with differential equations. And you might just qualify for the Olympics with a bit of statistics!

It’s $29, but you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

More by Jim

Tagged #programming, #npm, #node, #javascript. All content copyright James Fisher 2020. This post is not associated with my employer. Found an error? Edit this page.