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. All content copyright James Fisher 2019. This post is not associated with my employer.