Native_add-ons_for_Node.js
UI Development, Uncategorized

N-API and its Benefits

 

Since the beginning I am working as Front End developer I love to work with JS and all UI technologies today how modern web technologies play a major role in web development. Everywhere is JS and Only JS !!. JS in Browser and Node Js in Server. I feel like UI devs are more powerful than other Backend devs in the web development world.  But with time I understood I was wrong, JS is running on every browser because of the inbuilt engine of each browser develop by other backend dev, like for example The V8 engine is written in C++ and used in Chrome and Nodejs. So it means because of c++ developers only we can write JS code they make our life easy. JS is nothing but a high-end language which understand by the C++ engine and executes the code and renders the output in the browser.

You guys must be thinking why I am discussing about JS and C/C++ here. You all already know that C/C++ faster than JS. But today I wanted to share with you my past experience where I choose C++ over JS in web development. Yeah I know what you all are thinking. How C++ using in web development. In modern web UI technologies all are based on JS/Vanilla js and Node Js. Don’t wry if you following me till now then today I will explain you everything step by step.

Last few Month I was trying to develop some web app for entertainment purposes how we can manage the system audio control from my app.

When I am started building my app I was thinking about what are the things I need to cover in this. Here is the list:

  • UI interface for My web App
  • selection of correct audio device if I have many devices connected
  • Handle System volume control.

UI interface for My App

I develop a small interface for my web App with some UI controls like volume increase, decrease and mute icon, etc.

Selection of exact audio device

Today we usually connected various good quality audio devices with the system to get quality sound, in case if your system connected with more than one audio device then my app suppose to pick the quality speaker. I achieve this by using the windows Navigator.mediaDevices API we will discuss this in my other blog. for more reference you can explore link.

Handle System volume control

Now the important part of my blog how we can control the system volume control through my app. Initially I thought is there any JS API is there which can communicate with our system audio interface. Answer is No currently we don’t have such API supported in browser. Then I think let explore some node module, definitely there is must be some node module which can fit according to my requirement. So I started looking for some pre node modules and their examples I found a few modules but they are only controlling the volume of only default speaker. But when my app selects some another audio device which does not default then these modules did not work as expected and there is more problem I was facing with node module that when I am trying to control the volume from my app it is working only when your audio selection is default but when I am trying to update the volume from the system then my app should update the volume also which node modules do not provide.
So finally I thought the pre node module is not the right solution for my situation. So I decided to write a new node module API that will communicate with system audio.

But I thought how can I communicate with system audio as everyone knows the system audio ecosystem varies from the Operating system. Windows have their own system audio interface and Mac have their own. So I decided to go with windows only as I don’t have a Mac machine at that time.

So I resolved the first challenge by picking the windows operating system but now I have another challenge is how can I communicate with the windows system audio interface. When I started looking for some solutions I got to know through JS we can’t achieve it completely because JS will not directly communicate with windows audio interface all are inbuilt libraries of the operating system(develop in c or c++). Then what next ?? How should I proceed with this thought always coming to my head?

Then I thought is there any way where I will communicate with an inbuilt audio interface using js only by having some middleware ?? Then I started looking for more solutions even I went through several blogs. Finally I came to one site nodejs yeah our own node module website. I got to know node js provide you one platform where you can write c++ code and you can use this c++ code to your client-side using node addon. I said wow how cool is this it means I can write any c++ code and I can use it on the client-side. One more advantage we can leverage other c++ library and use in the browser side using node addon.

Finally I decided this is what I am looking for I will communicate the windows system audio interface by some c++ code which I will write and later I will use this c++ code to client-side like my web app. Js will communicate with actual c++ code by using N-API Addon.

Before we begin with this tutorial, let me start with a little disclaimer. I am not a professional C and C++ programmer.

But I always wanted to use some of the public C++ libraries inside JavaScript code for performance.

 

For example look at the below screenshot, where we compare both javascript and c++ code.

So if anyone thinking JS is faster than C or C++ then Sorry I can’t help you.

What is a Native Addon?

Before we jump into building Native Addons, we need to know first what they are? A Native Addon in the context of the Node.js is a binary file that is compiled from a low-level language like C and C++. Hence like importing a JavaScript file using require, we would be importing a Native Addon.

Native Addon like any other .js file exposes its API on module.exports or export object. A collection of these files when wrapped inside a node module also called as Native Module.

Since we want to load and run this Native Addon in our JavaScript application, this Native Add-on must be compatible with our JavaScript environment and must expose a compatible API so that it can be consumed like a normal node module.

A Native Addon is normally written in C or C++ language and compile using a standard compiler to a Dynamically Linked Library (DLL). It is also called as a Shared Library or Shared Object (SO).

DLL can be loaded into a program dynamically, at runtime. This DLL contains the compiled native code of our C or C++ program and an API to communicate with this compiled code.

How Native Addons work?

Alright! So far you got a brief idea of what a Native Addon is but how do they really work? To understand this, we need to look at the Node.js architecture and how things are bundled together. as you know Node.js is a collection of many open-source libraries.

Node.js uses Google’s open-source V8 engine as the default JavaScript engine which executes the JavaScript code. The V8 engine is written in C++ language and you can find the source code of this project on GitHub from their official v8/v8 repository.

When we install Node.js on our system, we generally installed a compiled version of the whole Node.js source code along with its dependent libraries. This way, we don’t have to install these libraries manually. However, Node.js can also be build from the source code of these libraries.

This sounds really sophisticated but how this is really related to the Native Addons? Since Node.js is written in low-level languages like C and C++, it can talk to other programs that are written in C and C++ and vice-versa.

Node.js can dynamically load an external C or C++ DLL file at runtime and utilize its API to perform some operations written inside it from a JavaScript program. This is basically how a Native Addon works in Node.js.

Lets Code: Boilerplate setup

Most of the content below I took from this link for better understanding. By the end of the blog I will share my code as well.

Create a basic node project test-addon

node-gyp is the toolchain to compile the addons. node-addon-API am a helper project as described earlier that will make writing C++ add-ons easier.

In the package.json set the attribute gypfile: true and set up the following files as below:

binding.gyp file contains all the files that need to be compiled and all the include files/libraries that the project will be using. If you notice we have added cpp src/main.CPP file as our source file.

Also our package.json mentions an index.js file as its main file.

Let’s create both of them :

index js
const testAddon = require('./build/Release/testaddon.node');
module.exports = testAddon;
main.cpp
#include <napi.h>
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
    return exports;
}
NODE_API_MODULE(testaddon, InitAll)

The base boilerplate is complete. Let’s try and build our addon

type npm run build

You should have an output similar to this:

output
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

compilation was successful. Let’s run it. Type node index.js. Sadly you will not get any output here.

Ideally, you should use a debugger tool to debug and see what the contents of testAddon is but for demo here let’s just add a console.log like this

//index.js
const testAddon = require('./build/Release/testaddon.node');
console.log('addon',testAddon);
module.exports = testAddon;
Now run index.js again. You should see an output like this :
node index.js
addon {}
Now we have a working setup to start with.

Before we go ahead, let’s take a look at cppsrc/main.cpp in detail:
1. 
#include<napi.h> includes the napi header file so that we can access all the helper macros, classes and functions.
2. 
NODE_API_MODULE is a macro that accepts module name and register function as parameters.
3. In our case register function is 
InitAll and it takes two parameters which are passed by N-API. The first parameter env is the context that needs to be passed on to most N-API function and exports is the object used to set the exported functions and classes via N-API.

Let’s Exporting a Hello World C++ function using N-API

Now let’s add an example of exporting a C++ function to NodeJS. Let’s take an example of a simple function

Wow! It looks like a lot. But if you look closely, it’s not as complex as it looks. For every function in C++, we want to export we will basically create a NAPI wrapped function (HelloWrapped in this example) and add it to the exports object using Init.

Let’s take some time to understand the HelloWrapped function. Every wrapped function that needs to be exported to JS should have input params/return value from the Napi namespace.
Every wrapped function takes in 
CallbackInfo as the input parameter. This contains things like the context and the input parameters that need to be passed to the function.

Init function is used to just set the export key as hello with corresponding wrapped function HelloWrapped.

Now we need to make our node-gyp know that we have added extra c++ files. So make the following changes to binding.gyp,main.cpp and index.js

1. Add it to binding.gyp"sources": [
 "cppsrc/main.cpp",
 "cppsrc/Samples/functionexample.cpp"
],2. Add it to main.cpp
#include <napi.h>
#include "Samples/functionexample.h"
Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
 return functionexample::Init(env, exports);
}
NODE_API_MODULE(testaddon, InitAll)
3. Do npm rebuild and access it via JS.
const testAddon = require('./build/Release/testaddon.node');
console.log('addon',testAddon);
console.log(testAddon.hello());
module.exports = testAddon;
Now run it ! node index.js .You should see the output as follows:
node index.js
addon { hello: [Function] }
Hello World
Now we have a hello world from C++ world into JS World!

Similarly we can use N-API in various ways like :

1. functions with input parameters ? We can send some inputs from JS to c++ and vice versa.

2. We can Send complex js objects to the C++ world.

I hope this helps someone trying to use N-API and create C++/C addons for NodeJS

So positively you guys understand the concept of N-API. So by using this technique I developed my c++ code and export those c++ function to my web app and vice versa. Here is the code which I created and control system audio volume.

 

 

Conclude we can say that

There can be many reasons to write nodejs addons:
1. You may want to access some native APIs that is difficult using JS alone.
2. You may want to integrate a third-party library written in C/C++ and use it directly in NodeJs.
3. You may want to rewrite some of the modules in C++ for performance reasons.
Whatever your reason is, this blog focuses on explaining the N-API and how you can use it to build C/C++ based NodeJS addons.

Thanks for reading

More references :

– https://github.com/nodejs/abi-stable-node-addon-examples/

– https://github.com/nodejs/node-addon-api.git/

– https://nodejs.org/api/addons.html

About The Author