Node.js addon hello world

We’re going to make a native extension called addon. Here’s main.js, which uses this extension:

// main.js
const addon = require('./addon');
console.log(addon.hello());

And here it is in action:

$ node main.js
world

Now, you could implement addon in JavaScript, like this:

// addon.js
module.exports.hello = () => 'world';

But instead, we’re going to implement addon in C++ as a Node.js addon! Instead of a file called addon.js, we’ll be making addon.node. The extension .node tells Node.js that it’s a native module, not a JavaScript module.

These .node files can be built with a tool called node-gyp:

$ npm install --save-dev node-gyp  # install for this project

To use it, first create the following binding.gyp file:

# binding.gyp
{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "addon_src.cc" ]
    }
  ]
}

Then run npx node-gyp configure, which uses the above binding.gyp file. Our binding.gyp says we’re going to build addon.node from the source file addon_src.cc. Make that next:

// addon_src.cc
#include <node.h>

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  v8::MaybeLocal<v8::String> str = String::NewFromUtf8(isolate, "world", NewStringType::kNormal);
  v8::Local<v8::String> checkedString = str.ToLocalChecked();
  v8::ReturnValue<v8::Value> retVal = args.GetReturnValue();
  retVal.Set(checkedString);
}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

With everything in place, we can build our addon.node file with npx node-gyp build:

$ npx node-gyp build

If you get build errors here, it’s likely because the V8 API has changed. The above example works for Node 12.x. Consult the addons docs for a latest working example. Due to V8 API instability, Node.js provides “Native Abstractions for Node.js”, a bunch of macros which are hopefully more stable. I’ll do a future post on a NAN hello world.

If you don’t get build errors, you should now have a file at build/Release/addon.node. Copy it to the local directory, and run our main script:

$ npx node-gyp build
$ cp build/Release/addon.node .
$ node main.js  # uses addon.node!
world

(Copying the addon.node file to the local directory is a bit ugly. A popular alternative is the bindings npm package, which has logic to require from all common locations that node-gyp outputs to.)

Tagged #programming.

Similar posts

More by Jim

Want to build a fantastic product using LLMs? I work at Granola where we're building the future IDE for knowledge work. Come and work with us! Read more or get in touch!

This page copyright James Fisher 2019. Content is not associated with my employer. Found an error? Edit this page.