NodeJS C++ Addon - Examples

Here you will find examples using C++ functions via NodeJS addon (NAPI), one uses the Win32 API to display a dialog window, another to sum two numbers together and the final method executes a javascript call back function with parameters.

NodeJS Addon Examples

Introduction

I have listed examples for building an NAPI addons this addon uses the Win32 API to display a GUI dialog Message. Another example which adds two floating point numbers together, and a method that calls a javascript call back function with a parameter defined pass for the script to process.

addon project structure

I have created to new source files to hold these methods, named myAddon.h and myAddon.cpp. These files are listed under the cppsrc folder created when preparing the addon. They are also added to the binding.gpy file under sources array. And the main.cpp has changed to call the myAddon Init function is inside the NAPI InitAll function.

NodeJS Addon Examples - Source Code
main.cpp
#include <napi.h>
#include "myAddon.h"

Napi::Object InitAll(Napi::Env env, Napi::Object exports){

    return myAddon::Init(env, exports);
}

NODE_API_MODULE(exampleAddon, InitAll)
myAddon.h

#include <napi.h>
#include <string>

namespace myAddon{

    //  Initialize Link Function
    Napi::Object Init(Napi::Env env, Napi::Object exports);

    //  C++ Function to show a Windows API Dialog Box Message
    void ShowMessage(std::string msg);

    //  C++ Method of adding two numbers
    float Sum(float a, float b); 

    //  Show Message Wrapper Function
    Napi::String ShowMessageWrapper(const Napi::CallbackInfo& info);

    //  function to sum two numbers, passed from javascript
    Napi::Number SumWrapper(const Napi::CallbackInfo& info);

    //  Callback Function
    Napi::String executeCallbackWrapper(const Napi::CallbackInfo& info);
}
myAddon.cpp
#include "myAddon.h"
#include <windows.h>
#include <sstream>


//  C++ Function
void myAddon::ShowMessage(std::string msg){
    // Win32 API Specific function...
    MessageBoxA(NULL, msg.c_str(), "Demo Windows Function", MB_OK);
}

//  C++ Method of adding two numbers
float myAddon::Sum(float a, float b){
    return a + b;
}

// Show Message - Wrapper Function
Napi::String myAddon::ShowMessageWrapper(const Napi::CallbackInfo& info)
{
    Napi::Env env = info.Env();

    //  Parameter Type Checking
    if(!info[0].IsString()){
        Napi::TypeError::New(env, "String Expected").ThrowAsJavaScriptException();
    }

    //  Get First Parameter as string
    Napi::String msg = info[0].As<Napi::String>();

    //  Execute C++ function
    ShowMessage(msg);

    //  Return Empty String
    return Napi::String::New(env, "");
}

//  Sum C++ - wrapper function
Napi::Number myAddon::SumWrapper(const Napi::CallbackInfo& info){
    
    //  Get Environment
    Napi::Env env = info.Env();

    //  Parameter Type Checking
    if(info.Length() != 2 || !info[0].IsNumber() || !info[1].IsNumber()){
        Napi::TypeError::New(env, "Numbers Expected").ThrowAsJavaScriptException();
    }

    //  Get Parameters a,b as numbers
    Napi::Number a = info[0].As<Napi::Number>();
    Napi::Number b = info[1].As<Napi::Number>();

    //  Execute Sum C++ function
    float result = myAddon::Sum(a,b);

    //  Return results of sum
    return Napi::Number::New(env, result);
}


Napi::Object myAddon::Init(Napi::Env env, Napi::Object exports)
{
    //  Export new function called "showMessage", this is callable from javascript
    exports.Set("showMessage", Napi::Function::New(env, myAddon::ShowMessageWrapper));

    //  Export new function called "sum", this is callable from javascript
    exports.Set("sum", Napi::Function::New(env, myAddon::SumWrapper));

    //  Export new function called "stringProcess", this is callable from javascript
    //  and has a callback function as parameter
    exports.Set("stringProcess", Napi::Function::New(env, myAddon::executeCallbackWrapper));

    return exports;
}

//  Callback Function Example
Napi::String myAddon::executeCallbackWrapper(const Napi::CallbackInfo& info)
{
    //  Get Environment
    Napi::Env env = info.Env();

    std::stringstream ss;
    
    //  Parameter Type Checking
    if(info.Length() != 2 || !info[0].IsString() || !info[1].IsFunction()){
        Napi::TypeError::New(env, "String Expected, and expected callback function").ThrowAsJavaScriptException();
    }

    std::string str = info[0].ToString().Utf8Value();

    ss << "Processed : " << str;

    //  Cast First parameter as function, this is the callback function in javascript
    Napi::Function cb = info[1].As<Napi::Function>();

    //  Execute callback function now, passing the new string as a parameter
    cb.Call(env.Global(), {Napi::String::New(env, ss.str())});

    return Napi::String::New(info.Env(), "Success");
}
binding.gyp

This file is in the Nodejs project directory where the package.json file is located.

{
"targets": [{
    "target_name": "myAddon",
    "cflags!": [ "-fno-exceptions" ],
    "cflags_cc!": [ "-fno-exceptions" ],
    "sources": [
        "cppsrc/main.cpp",
        "cppsrc/myAddon.h",
        "cppsrc/myAddon.cpp"
    ],
    'include_dirs': [
        "<!@(node -p \"require('node-addon-api').include\")"
    ],
    'libraries': [],
    'dependencies': [
        "<!(node -p \"require('node-addon-api').gyp\")"
    ],
    'defines': [ 'NAPI_DISABLE_CPP_EXCEPTIONS' ]
}]
}

index.js

This file is in the Nodejs project directory where the package.json file is located.

const myAddon = require('./build/Release/myAddon.node');

module.exports = myAddon;

//  Call showMessage C++ function
myAddon.showMessage("Display this message");

//  Call sum C++ function, display the results
console.log(myAddon.sum(0.5, 0.75));


//  Example of parameter function callback
console.log(myAddon.stringProcess("Callback", function(data){
    console.log(data);
}));