The objective of this tutorial is to give you a good idea of the steps necessary and the tools available to migrate an existing NAN Node native add-on module to N-API using the node-addon-api package.
This tutorial uses the conversion tool supplied with N-API to give you a head start on the migration. However, the conversion tool will only get you so far. Further manual cleanup is still required as described below.
To keep things somewhat constrained, this tutorial uses node-microtime which is a simple NAN-based native add-on. This add-on makes system calls to determine the current time to microsecond resolution if supported by the operating system.
Before you start, make sure you’ve got all the necessary prerequisites and tools installed.
N-API has been in public release and active development starting with Node 8.0.0. Since then, it’s undergone a number of refinements. This tutorial has been tested with Node 10.1.0 and is known to fail with older versions of Node. You can determine the version of Node you’re running with the command
node -v
.
node-microtime
As a first step, clone the node-microtime GitHub repository to your system:
1 | git clone https://github.com/wadey/node-microtime.git |
Before we make our modifications, it’s a good idea to first build and test node-microtime
to help verify that the necessary development tools have been correctly installed and configured.
1 | cd node-microtime |
The npm install
command invokes the build process and npm test
runs the code. You may see compiler warnings that do not affect the ability to run the code. When successfully built and run, you should see output that looks something like this:
1 | microtime.now() = 1526334357974754 |
Once the basic operation of the code has been verified, the next step is to run the N-API Conversion Tool. Be aware that the conversion tool replaces files in place. Never run the conversion tool on the only copy of your project. And obviously, you want to run it only once.
1 | npm install --save node-addon-api |
For this small project, the conversion tool runs very quickly. At this point, the conversion tool has modified the following project files:
Go ahead and rebuild the converted code:
1 | npm install |
As you’ll see, there are one or more compile errors that need to be addressed. There can be quite a few at times, but nothing insurmountable.
The conversion tool cannot anticipate every coding situation. So there will typically be issues that need to be addressed manually. Below are the issues you’re likely to encounter with this project. The best approach is to address each issue, one at a time, and attempt the npm install
after addressing each issue until there are no more errors.
This error, and its counterpart where napi.h
cannot be found, is due to code missing in the bind.gyp
file. For this project, you’ll see this code in the binding.gyp:
1 | 'include_dirs' : [ '<!(node -e "require(\'nan\')")' ] |
The C/C++ include directories are still pointing to NAN. They need to point to N-API instead. The line above should be replaced with this line:
1 | 'include_dirs' : [ "<!@(node -p \"require('node-addon-api').include\")" ] |
On other projects you may receive an error where napi.h
cannot be found. The cause is the same as this one. The include_dirs
property of each target must include the reference to node-addon-api
as shown above.
The three C++ functions—Now
, NowDouble
, and NowStruct
—each make a reference to the env
variable which is not defined. env
is meant to hold the N-API Environment variable which is essential to nearly all N-API calls. The value for env
can be easily obtained from the Napi::CallbackInfo
argument passed to each C++ function. One technique to correct this error is to add the following code as the first line in the body of each function:
1 | Napi::Env env = info.Env(); |
An alternative would be to replace each instance of env
in the three functions with info.Env()
. The choice is yours.
void
as the return valueEach of the three C++ functions—Now
, NowDouble
, and NowStruct
are defined to return a void
value. In fact, they should each be returning a JavaScript value. The best way to accomplish this is by replacing the void
with Napi::Value
. This permits each of the functions to return a JavaScript value of undetermined type. It could be any JavaScript value including String, Number, Boolean, Array, etc. Here’s what they should look like:
1 | Napi::Value Now(const Napi::CallbackInfo&info) { |
exports
N-API uses a different technique for defining the exports
object.
The code:
1 | Napi::Export(target, "now", Now); |
Needs to be replaced with:
1 | exports.Set(Napi::String::New(env,"now"), Napi::Function::New(env, Now)); |
exports
is a Napi::Object
which represents a JavaScript object. The Set
method sets the value of properties on the object and takes two arguments, the name of the property and its value. These two arguments must both be JavaScript values.
One more change is critical to the operation of N-API. The InitAll
function must return the exports
variable. This line must appear as the last line in the body of the function:
1 | return exports; |
Failure to add this line will likely result in a segfault error at runtime.
ErrnoException
The NAN ErrnoException
object does not exist in N-API. The existing code looks like:
1 | Napi::Error::New(env, Napi::ErrnoException(errno, "gettimeofday")).ThrowAsJavaScriptException(); |
But it can easily be replaced with code that looks like this:
1 | std::string msg = "gettimeofday: " + std::string(strerror(errno)); |
Once the code compiles without error, test the changes you’ve made:
1 | npm test |
You should see results similar to those from before the migration.
Congratulations! You’ve just converted your first NAN module to N-API.
Admittedly, this tutorial just scratches the surface on migrating NAN modules to N-API. However, the basic approach is the same. Run the conversion, attempt a compile, address the errors, compile again. Wash. Rinse. Repeat.
There are number of good resources to help you better understand N-API as you migrate your own modules:
Each of these resources is maintained by the N-API Working Group.