Unlocking Native Performance in Node.js with Node-API (N-API)

Search for a command to run...

No comments yet. Be the first to comment.
Bun, the hyper-fast JavaScript runtime known for its incredible speed and Zig-based architecture, is being completely rewritten in Rust. For a project that has over 22 million monthly downloads and ea

Next.js 16.3 is almost here, and it's packed with a ton of improvements and let's see them in this post 1.Instant Navigation For a while now, there’s been a valid, lingering debateServer Components vs

It’s official. React has merged its long-awaited Rust port of the React Compiler (formerly known as React Forget) into the main repository. What started as an experimental research project by Joseph S

If you follow modern technology trends, you have likely been led to believe that artificial intelligence is entirely built on Python. Every tutorial, machine learning framework documentation, and open

Just when the JavaScript ecosystem was catching its breath after last week’s TanStack Router compromise, the Mini Shai-Hulud supply chain worm has continued its relentless march. Developed by the thre

Node.js is fast, flexible, and great for building APIs, servers, and full-stack apps. But sometimes JavaScript isn’t enough.
What if you need blazing-fast performance?
Or you want to use an existing C/C++ library instead of rewriting it in JS?
Or maybe you need access to low-level system features like file systems, drivers, or hardware?
That’s where Node-API (N-API) comes in.
Node-API is a stable C API for building native addons in Node.js.
Before Node-API, addons were tied directly to V8 (Node’s JavaScript engine). Every Node.js or V8 update risked breaking your addon.
With Node-API:
Addons work across Node.js versions.
It’s engine-independent (works with V8, ChakraCore, Hermes, etc.).
It provides a stable ABI (Application Binary Interface).
Future-proof - Your addon won’t break every time Node.js upgrades.
Performance - Run CPU-heavy tasks in native code.
Reuse existing libraries - Wrap C/C++ instead of reinventing in JS.
System access - Do things pure JS can’t (hardware, OS integration).
Popular modules that use native code:
bcrypt → password hashing
sharp → image processing
sqlite3 → database driver

JavaScript talks to Node-API, which safely communicates with your C/C++ addon.
Let’s make our hand’s dirty by building a tiny addon
Make sure you have:
Node.js
Python (for build tools)
C++ compiler (gcc/clang/MSVC)
npm install -g node-gyp
Create a new file named hello.cpp
#include <napi.h>
// This is the native C++ function that will be exposed to JavaScript.
// It takes the standard N-API CallbackInfo object and returns a Napi::Value.
Napi::Value HelloWorld(const Napi::CallbackInfo& info) {
// Napi::Env is the environment context for the current Node.js instance.
// It's used to create JavaScript values (strings, numbers, objects, etc.).
Napi::Env env = info.Env();
// Create a new JavaScript string with the value "Hello World from C++!"
// and return it. This value will be the result of calling the function
// from your Node.js code.
return Napi::String::New(env, "Hello World from C++!");
}
// The Init function is the entry point for the Node.js addon.
// It's responsible for setting up the exports that will be available
// in JavaScript when the addon is required.
Napi::Object Init(Napi::Env env, Napi::Object exports) {
// Set a property on the 'exports' object.
// The first argument is the name of the export (how you'll call it in JS).
// The second argument is a Napi::Function that wraps our native C++ function.
exports.Set(Napi::String::New(env, "hello"),
Napi::Function::New(env, HelloWorld));
// Return the modified exports object.
return exports;
}
// This macro registers the addon with Node.js.
// The first argument is the addon's name (must match 'target_name' in binding.gyp).
// The second argument is the initialization function we just defined.
NODE_API_MODULE(hello_world_addon, Init)
This is the core C++ source code. It defines the HelloWorld function that returns a string and an Init function that exports HelloWorld under the name hello.
Create binding.gyp
{
"targets": [
{
"target_name": "hello_world_addon",
"sources": [ "hello.cpp" ],
"include_dirs": [
"<!@(node -p "require('node-addon-api').include")"
],
"defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ]
}
]
}
This is a build configuration file for node-gyp. It tells the compiler which C++ files to compile (sources), what to name the final binary (target_name), and where to find the necessary header files (include_dirs).
node-gyp configure build
This generates build/Release/addon.node
Create index.js
const addon = require('./build/Release/hello_world_addon.node');
console.log(addon.hello()); // → "Hello from Node-API!"
Run it:
node index.js
You can find the entire source code for the helloworld addon in the following Github

Node-API → bridge to native C++ in Node.js.
Worklets → lightweight JS workers in browser rendering.
WebAssembly → portable, high-performance modules for both browser + Node.js.
Hermes — a JavaScript engine built by Meta for React Native. It’s optimized for:
Fast startup
Low memory usage
Small binary size
Node.js usually runs on V8, but the community has experimented with running Node.js on Hermes.
Here’s why Node-API is important:
Node-API is engine-independent.
If Node.js runs on V8, ChakraCore, or Hermes, your addon still works.
Without Node-API, you’d have to rewrite bindings for every engine.
Think of Node-API as a universal power adapter:
V8 = US plug
Hermes = EU plug
ChakraCore = UK plug
Without Node-API, you’d need a different charger each time. With Node-API, your addon plugs in anywhere.
Documentation: https://nodejs.org/api/n-api.html
Engine bindings doc: https://github.com/nodejs/abi-stable-node/blob/doc/node-api-engine-bindings.md
Hermes PR: https://github.com/facebook/hermes/pull/1377
Hermes Windows fork: https://github.com/microsoft/hermes-windows