C/C++ in Node.js: Creating and Using Native Addons (N-API)

By Guilherme Luiz Maia Pinto
Picture of the author
Published on
Node.js C++ Addons Banner

Native addons let you extend Node.js with C/C++ for performance or to access OS/third‑party libraries. This guide shows how to create a minimal addon using N‑API (stable ABI) and the node-addon-api C++ wrapper, with all code written out (no screenshots).


Why N‑API?

N‑API provides a stable ABI across Node.js versions, reducing rebuild pain. The C++ convenience wrapper node-addon-api gives a friendly API on top of N‑API C.


1) Project Setup

mkdir cpp-addon && cd cpp-addon
npm init -y
npm i node-addon-api
npm i -D node-gyp

Add scripts to package.json (optional but handy):

{
  "scripts": {
    "build": "node-gyp rebuild",
    "rebuild": "node-gyp configure build",
    "start": "node index.js"
  }
}

2) binding.gyp

Create binding.gyp in the project root:

{
  "targets": [
    {
      "target_name": "addon",
      "sources": ["src/addon.cc"],
      "include_dirs": [
        "<!(node -p \"require('node-addon-api').include\")"
      ],
      "defines": ["NAPI_DISABLE_CPP_EXCEPTIONS"],
      "conditions": [
        ["OS=='mac'", { "xcode_settings": { "OTHER_CPLUSPLUSFLAGS": ["-std=c++17"] } }],
        ["OS=='linux' or OS=='freebsd'", { "cflags_cc": ["-std=c++17"] }],
        ["OS=='win'", { "msvs_settings": { "VCCLCompilerTool": { "AdditionalOptions": ["/std:c++17"] } } }]
      ]
    }
  ]
}

3) Addon Source (C++)

Create src/addon.cc:

#include <napi.h>

// add(a, b): returns a + b with basic argument validation
Napi::Value Add(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 2 || !info[0].IsNumber() || !info[1].IsNumber()) {
    Napi::TypeError::New(env, "Expected two numbers").ThrowAsJavaScriptException();
    return env.Null();
  }

  double a = info[0].As<Napi::Number>().DoubleValue();
  double b = info[1].As<Napi::Number>().DoubleValue();
  return Napi::Number::New(env, a + b);
}

// exports.add = Add
Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "add"), Napi::Function::New(env, Add));
  return exports;
}

NODE_API_MODULE(addon, Init)

Notes:

  • We disable C++ exceptions (NAPI_DISABLE_CPP_EXCEPTIONS) in binding.gyp to keep the binary smaller and rely on Node‑style error reporting.

4) Build the Addon

npx node-gyp configure build

The compiled module will be at ./build/Release/addon.node.


5) Use the Addon from JavaScript

Create index.js in the project root:

// Option A (direct path):
const addon = require('./build/Release/addon.node')

// Option B (via "bindings" helper):
// const addon = require('bindings')('addon')

console.log('2 + 3 =', addon.add(2, 3))

Run:

node index.js

Expected output:

2 + 3 = 5

6) TypeScript (Optional)

If you want TS types, add a minimal declaration file index.d.ts:

export function add(a: number, b: number): number

Then update package.json:

{
  "types": "index.d.ts"
}

7) Common Issues

  • Ensure native toolchain is installed (macOS: Xcode CLT; Linux: build‑essential; Windows: Visual Studio Build Tools).
  • If Node.js version changes, node-gyp rebuild may be needed.
  • Prefer N‑API over legacy NAN for ABI stability across Node versions.

Where to Go Next

  • Wrap existing C/C++ libraries and expose functions/classes to JS
  • Pass Buffers to C++ for high‑throughput compute and I/O
  • Explore Napi::ObjectWrap to create C++ classes exposed as JS objects

With N‑API, you can ship fast, portable native extensions that keep working across Node.js upgrades.

Stay Tuned

Want to become a Software Engineer pro?
The best articles and links related to web development are delivered once a week to your inbox.