Skip to main content

Overview

Addons are dynamically-linked shared objects written in C++ that provide an interface between JavaScript and native libraries. They can be loaded as ordinary Node.js modules using require().
Node-API (formerly N-API) is the recommended approach for building addons, providing ABI stability across Node.js versions.

Building Addons

There are three options for implementing addons:

Node-API

Recommended approach with ABI stability

NAN

Native Abstractions for Node.js

Direct V8

Direct V8, libuv, and Node.js APIs

Key Components

When building addons without Node-API, you’ll work with:
The C++ library providing JavaScript implementation. Access through v8.h header file.
#include <v8.h>

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Value;
Provides event loop, worker threads, and asynchronous behaviors. Cross-platform abstraction for system tasks.
#include <uv.h>
C++ APIs including node::ObjectWrap class.
#include <node.h>

Hello World Example

A simple addon equivalent to module.exports.hello = () => 'world':
hello.cc
#include <node.h>

namespace demo {

using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::NewStringType;
using v8::Object;
using v8::String;
using v8::Value;

void Method(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  args.GetReturnValue().Set(String::NewFromUtf8(
      isolate, "world", NewStringType::kNormal).ToLocalChecked());
}

void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

}  // namespace demo
All Node.js addons must export an initialization function following the pattern above. There is no semicolon after NODE_MODULE as it’s not a function.

Node-API for Version Stability

Why Node-API?

Node-API is independent from the underlying JavaScript runtime (V8) and provides ABI stability across Node.js versions.
1

ABI Stability

Modules compiled for one major version can run on later versions without recompilation
2

Runtime Independence

Not tied to V8 internals - insulates addons from engine changes
3

Standard Interface

All JavaScript values abstracted behind opaque napi_value type

Node-API Properties

// All Node-API calls return a status code
napi_status status;
napi_value object, string;

status = napi_create_object(env, &object);
if (status != napi_ok) {
  napi_throw_error(env, nullptr, "Failed to create object");
  return;
}

status = napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, &string);
if (status != napi_ok) {
  napi_throw_error(env, nullptr, "Failed to create string");
  return;
}

status = napi_set_named_property(env, object, "foo", string);
if (status != napi_ok) {
  napi_throw_error(env, nullptr, "Failed to set property");
  return;
}

Using node-addon-api (C++ Wrapper)

The official C++ binding provides a cleaner interface:
Object obj = Object::New(env);
obj["foo"] = String::New(env, "bar");

Context-Aware Addons

For environments requiring multiple addon instances (e.g., Electron):
using namespace v8;

extern "C" NODE_MODULE_EXPORT void
NODE_MODULE_INITIALIZER(Local<Object> exports,
                        Local<Value> module,
                        Local<Context> context) {
  /* Perform addon initialization steps here. */
}

Using NODE_MODULE_INIT

NODE_MODULE_INIT(/* exports, module, context */) {
  Isolate* isolate = context->GetIsolate();
  Local<Object> exports = exports;
  
  // Add your methods here
  NODE_SET_METHOD(exports, "method", Method);
}
Context-aware addons require careful management of global static data to ensure thread safety and prevent crashes when loaded in multiple contexts.

Building with node-gyp

Create a binding.gyp configuration file:
binding.gyp
{
  "targets": [
    {
      "target_name": "addon",
      "sources": [ "hello.cc" ]
    }
  ]
}
Build the addon:
# Install node-gyp globally
npm install -g node-gyp

# Configure and build
node-gyp configure
node-gyp build

Best Practices

Use Node-API

Unless you need direct V8 access, always use Node-API for stability

Error Handling

Always check return status codes and handle errors appropriately

Memory Management

Be careful with object lifetimes and use proper cleanup hooks

Thread Safety

Avoid blocking the event loop - offload work to worker threads

Embedding Node.js

Embed Node.js in C++ applications

V8 Integration

Deep dive into V8 engine integration

Node.js Internals

Explore Node.js C++ core architecture

Node-API Docs

Official Node-API documentation