- Assertion testing
- Asynchronous context tracking
- Async hooks
- Buffer
- C++ addons
- C/C++ addons with Node-API
- C++ embedder API
- Child processes
- Cluster
- Command-line options
- Console
- Corepack
- Crypto
- Debugger
- Deprecated APIs
- Diagnostics Channel
- DNS
- Domain
- Errors
- Events
- File system
- Globals
- HTTP
- HTTP/2
- HTTPS
- Inspector
- Internationalization
- Modules: CommonJS modules
- Modules: ECMAScript modules
- Modules:
node:moduleAPI - Modules: Packages
- Net
- OS
- Path
- Performance hooks
- Permissions
- Process
- Punycode
- Query strings
- Readline
- REPL
- Report
- Single executable applications
- Stream
- String decoder
- Test runner
- Timers
- TLS/SSL
- Trace events
- TTY
- UDP/datagram
- URL
- Utilities
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker threads
- Zlib
Node.js v18.20.5 documentation
- Node.js v18.20.5
- ► Table of contents
-
►
Index
- Assertion testing
- Asynchronous context tracking
- Async hooks
- Buffer
- C++ addons
- C/C++ addons with Node-API
- C++ embedder API
- Child processes
- Cluster
- Command-line options
- Console
- Corepack
- Crypto
- Debugger
- Deprecated APIs
- Diagnostics Channel
- DNS
- Domain
- Errors
- Events
- File system
- Globals
- HTTP
- HTTP/2
- HTTPS
- Inspector
- Internationalization
- Modules: CommonJS modules
- Modules: ECMAScript modules
- Modules:
node:moduleAPI - Modules: Packages
- Net
- OS
- Path
- Performance hooks
- Permissions
- Process
- Punycode
- Query strings
- Readline
- REPL
- Report
- Single executable applications
- Stream
- String decoder
- Test runner
- Timers
- TLS/SSL
- Trace events
- TTY
- UDP/datagram
- URL
- Utilities
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- Worker threads
- Zlib
- ► Other versions
- ► Options
Table of contents
C++ addons#
Addons are dynamically-linked shared objects written in C++. The
require() function can load addons as ordinary Node.js modules.
Addons provide an interface between JavaScript and C/C++ libraries.
There are three options for implementing addons: Node-API, nan, or direct use of internal V8, libuv, and Node.js libraries. Unless there is a need for direct access to functionality which is not exposed by Node-API, use Node-API. Refer to C/C++ addons with Node-API for more information on Node-API.
When not using Node-API, implementing addons is complicated, involving knowledge of several components and APIs:
-
V8: the C++ library Node.js uses to provide the JavaScript implementation. V8 provides the mechanisms for creating objects, calling functions, etc. V8's API is documented mostly in the
v8.hheader file (deps/v8/include/v8.hin the Node.js source tree), which is also available online. -
libuv: The C library that implements the Node.js event loop, its worker threads and all of the asynchronous behaviors of the platform. It also serves as a cross-platform abstraction library, giving easy, POSIX-like access across all major operating systems to many common system tasks, such as interacting with the file system, sockets, timers, and system events. libuv also provides a threading abstraction similar to POSIX threads for more sophisticated asynchronous addons that need to move beyond the standard event loop. Addon authors should avoid blocking the event loop with I/O or other time-intensive tasks by offloading work via libuv to non-blocking system operations, worker threads, or a custom use of libuv threads.
-
Internal Node.js libraries. Node.js itself exports C++ APIs that addons can use, the most important of which is the
node::ObjectWrapclass. -
Node.js includes other statically linked libraries including OpenSSL. These other libraries are located in the
deps/directory in the Node.js source tree. Only the libuv, OpenSSL, V8, and zlib symbols are purposefully re-exported by Node.js and may be used to various extents by addons. See Linking to libraries included with Node.js for additional information.
All of the following examples are available for download and may be used as the starting-point for an addon.
Hello world#
This "Hello world" example is a simple addon, written in C++, that is the equivalent of the following JavaScript code:
module.exports.hello = () => 'world';
First, create the file hello.cc:
// hello.cc
#include <node.h>
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
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").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:
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
There is no semi-colon after NODE_MODULE as it's not a function (see
node.h).
The module_name must match the filename of the final binary (excluding
the .node suffix).
In the hello.cc example, then, the initialization function is Initialize
and the addon module name is addon.
When building addons with node-gyp, using the macro NODE_GYP_MODULE_NAME as
the first parameter of NODE_MODULE() will ensure that the name of the final
binary will be passed to NODE_MODULE().
Addons defined with NODE_MODULE() can not be loaded in multiple contexts or
multiple threads at the same time.
Context-aware addons#
There are environments in which Node.js addons may need to be loaded multiple
times in multiple contexts. For example, the Electron runtime runs multiple
instances of Node.js in a single process. Each instance will have its own
require() cache, and thus each instance will need a native addon to behave
correctly when loaded via require(). This means that the addon
must support multiple initializations.
A context-aware addon can be constructed by using the macro
NODE_MODULE_INITIALIZER, which expands to the name of a function which Node.js
will expect to find when it loads an addon. An addon can thus be initialized as
in the following example:
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. */
}
Another option is to use the macro NODE_MODULE_INIT(), which will also
construct a context-aware addon. Unlike NODE_MODULE(), which is used to
construct an addon around a given addon initializer function,
NODE_MODULE_INIT() serves as the declaration of such an initializer to be
followed by a function body.
The following three variables may be used inside the function body following an
invocation of NODE_MODULE_INIT():
Local<Object> exports,Local<Value> module, andLocal<Context> context
The choice to build a context-aware addon carries with it the responsibility of carefully managing global static data. Since the addon may be loaded multiple times, potentially even from different threads, any global static data stored in the addon must be properly protected, and must not contain any persistent references to JavaScript objects. The reason for this is that JavaScript objects are only valid in one context, and will likely cause a crash when accessed from the wrong context or from a different thread than the one on which they were created.
The context-aware addon can be structured to avoid global static data by performing the following steps:
- Define a class which will hold per-addon-instance data and which has a static
member of the form
static void DeleteInstance(void* data) { // Cast `data` to an instance of the class and delete it. } - Heap-allocate an instance of this class in the addon initializer. This can be
accomplished using the
newkeyword. - Call
node::AddEnvironmentCleanupHook(), passing it the above-created instance and a pointer toDeleteInstance(). This will ensure the instance is deleted when the environment is torn down. - Store the instance of the class in a
v8::External, and - Pass the
v8::Externalto all methods exposed to JavaScript by passing it tov8::FunctionTemplate::New()orv8::Function::New()which creates the native-backed JavaScript functions. The third parameter ofv8::FunctionTemplate::New()orv8::Function::New()accepts thev8::Externaland makes it available in the native callback using thev8::FunctionCallbackInfo::Data()method.
This will ensure that the per-addon-instance data reaches each binding that can be called from JavaScript. The per-addon-instance data must also be passed into any asynchronous callbacks the addon may create.
The following example illustrates the implementation of a context-aware addon:
#include <node.h>
using namespace v8;
class AddonData {
public:
explicit AddonData(Isolate* isolate):
call_count(0) {
// Ensure this per-addon-instance data is deleted at environment cleanup.
node::AddEnvironmentCleanupHook(isolate, DeleteInstance, this);
}
// Per-addon data.
int call_count;
static void DeleteInstance(void* data) {
delete static_cast<AddonData*>(data);
}
};
static void Method(const v8::FunctionCallbackInfo<v8::Value>& info) {
// Retrieve the per-addon-instance data.
AddonData* data =
reinterpret_cast<AddonData*>(info.Data().As<External>()->Value());
data->call_count++;
info.GetReturnValue().Set((double)data->call_count);
}
// Initialize this addon to be context-aware.
NODE_MODULE_INIT(/* exports, module, context */) {
Isolate* isolate = context->GetIsolate();
// Create a new instance of `AddonData` for this instance of the addon and
// tie its life cycle to that of the Node.js environment.
AddonData* data = new AddonData(isolate);
// Wrap the data in a `v8::External` so we can pass it to the method we
// expose.
Local<External> external = External::New(isolate, data);
// Expose the method `Method` to JavaScript, and make sure it receives the
// per-addon-instance data we created above by passing `external` as the
// third parameter to the `FunctionTemplate` constructor.
exports->Set(context,
String::NewFromUtf8(isolate,