๐Ÿค– Beginners guide to writing NodeJS Addons using C++ and N-API (node-addon-api)

Atul
Atul
Jun 15, 2018 ยท 9 min read

Lets Code: Boilerplate setup

mkdir test-addon
cd test-addon
git init
npm init
npm install node-gyp --save-dev
npm install node-addon-api
npm run build> test-addon@1.0.0 build /Users/atulr/Projects/Hobby/test-addon
> node-gyp rebuild
SOLINK_MODULE(target) Release/nothing.node
CXX(target) Release/obj.target/testaddon/cppsrc/main.o
SOLINK_MODULE(target) Release/testaddon.node
//index.jsconst testAddon = require('./build/Release/testaddon.node');
console.log('addon',testAddon);
module.exports = testAddon;
node index.js
addon {}
/**
* This code defines the entry-point for the Node addon, it tells Node where to go
* once the library has been loaded into active memory. The first argument must
* match the "target" in our *binding.gyp*. Using NODE_GYP_MODULE_NAME ensures
* that the argument will be correct, as long as the module is built with
* node-gyp (which is the usual way of building modules). The second argument
* points to the function to invoke. The function must not be namespaced.
*/
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

Exporting a Hello World C++ function using N-API

std::string hello(){
return "Hello World";
}
#include <napi.h>namespace functionexample {
std::string hello();
Napi::String HelloWrapped(const Napi::CallbackInfo& info);
Napi::Object Init(Napi::Env env, Napi::Object exports);
}
#include "functionexample.h"std::string functionexample::hello(){
return "Hello World";
}
Napi::String functionexample::HelloWrapped(const Napi::CallbackInfo& info)
{
Napi::Env env = info.Env();
Napi::String returnValue = Napi::String::New(env, functionexample::hello());

return returnValue;
}
Napi::Object functionexample::Init(Napi::Env env, Napi::Object exports)
{
exports.Set(
"hello", Napi::Function::New(env, functionexample::HelloWrapped)
);

return exports;
}
node index.js
addon { hello: [Function] }
Hello World

How about functions with input parameters ?

int add(int a, int b){
return a + b;
}
node index.jsaddon { hello: [Function], add: [Function] }
hello Hello World
add 15

Exporting a Hello World C++ Class using N-API

Napi::Function func = DefineClass(env, "ClassExample", 
{
InstanceMethod("add", &ClassExample::Add),
InstanceMethod("getValue", &ClassExample::GetValue),
});
constructor = Napi::Persistent(func);
env is the environment that represent an independent instance of the JavaScript runtime,
Napi::Value ClassExample::Add(const Napi::CallbackInfo& info) 
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);

if (info.Length() != 1 || !info[0].IsNumber()) {
Napi::TypeError::New(env, "Numberexpected").ThrowAsJavaScriptException();
}
Napi::Number toAdd = info[0].As<Napi::Number>();
double answer = this->actualClass_->add(toAdd.DoubleValue());
return Napi::Number::New(info.Env(), answer);
}
node index.jsaddon { hello: [Function],
add: [Function],
ClassExample: [Function: ClassExample] }
hello Hello World
add 15
Testing class initial value : 4.3
After adding 3.3 : 7.6

Sending complex js objects to the C++ world

const prevInstance = new testAddon.ClassExample(4.3);
console.log('Initial value : ', prevInstance.getValue());
console.log('After adding 3.3 : ', prevInstance.add(3.3));
const newFromExisting = new testAddon.ClassExample(prevInstance);console.log('Testing class initial value for derived instance');
console.log(newFromExisting.getValue());
Napi::Object object_parent = info[0].As<Napi::Object>();ClassExample* example_parent = Napi::ObjectWrap<ClassExample>::Unwrap(object_parent);ActualClass* parent_actual_class_instance = example_parent->GetInternalInstance();this->actualClass_ = new ActualClass(parent_actual_class_instance->getValue());return;
node index.jsInitial value :  4.3
After adding 3.3 : 7.6
Testing class initial value for derived instance
7.6

Atul

Written by

Atul

https://blog.atulr.com : I have moved from medium to my personal blog here. Follow me on twitter : https://twitter.com/masteratul94

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and weโ€™ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium โ€” and support writers while youโ€™re at it. Just $5/month. Upgrade