Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 68 additions & 92 deletions doc/api/addons.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@

<!-- type=misc -->

_Addons_ are dynamically-linked shared objects written in C++. The
[`require()`][require] function can load addons as ordinary Node.js modules.
Addons provide an interface between JavaScript and C/C++ libraries.
_Addons_ are dynamically-linked shared objects that can be loaded via the
[`require()`][] function as ordinary Node.js modules.
Addons provide a foreign function interface between JavaScript and native code.

There are three options for implementing addons:

* Node-API
* [Node-API][] (recommended)
* `nan` ([Native Abstractions for Node.js][])
* 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](n-api.md) for more information on
Node-API.

When not using Node-API, implementing addons becomes more complex, requiring\
This rest of this document focuses on the latter, requiring
knowledge of multiple components and APIs:

* [V8][]: the C++ library Node.js uses to provide the
Expand All @@ -28,7 +23,7 @@ knowledge of multiple components and APIs:
`v8.h` header file (`deps/v8/include/v8.h` in the Node.js source
tree), and is also available [online][v8-docs].

* [libuv][]: The C library that implements the Node.js event loop, its worker
* [`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
Expand Down Expand Up @@ -87,37 +82,40 @@ void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "hello", Method);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize) // N.B.: no semi-colon, this is not a function

} // namespace demo
```

All Node.js addons must export an initialization function following
the pattern:
On most platforms, the following `Makefile` can get us started:

```cpp
void Initialize(Local<Object> exports);
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
<!--lint disable no-tabs remark-lint-->

```bash
NODEJS_DEV_ROOT ?= $(shell dirname "$$(command -v node)")/..
CXXFLAGS = -std=c++23 -I$(NODEJS_DEV_ROOT)/include/node -fPIC -shared -Wl,-undefined,dynamic_lookup
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the flag -Wl,-undefined,dynamic_lookup only works on macOS, and does not match the above words "On most platforms"...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested it from within a docker container with gcc, it didn't seem to cause problems. Happy to update to something else if you have suggestions


hello.node: hello.cc
$(CXX) $(CXXFLAGS) -o $@ $<
```

There is no semi-colon after `NODE_MODULE` as it's not a function (see
`node.h`).
<!--lint enable no-tabs remark-lint-->

The `module_name` must match the filename of the final binary (excluding
the `.node` suffix).
Then running the following commands will compile and run the code:

In the `hello.cc` example, then, the initialization function is `Initialize`
and the addon module name is `addon`.
```console
$ make
$ node -p 'require("./hello.node").hello()'
world
```

To integrate with the npm ecosystem, see the [Building][] section.

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()`.
### Context-aware addons

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
Expand Down Expand Up @@ -257,8 +255,8 @@ changes:
In order to be loaded from multiple Node.js environments,
such as a main thread and a Worker thread, an add-on needs to either:

* Be an Node-API addon, or
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above
* Be an [Node-API][] addon.
* Be declared as context-aware using `NODE_MODULE_INIT()` as described above.

In order to support [`Worker`][] threads, addons need to clean up any resources
they may have allocated when such a thread exits. This can be achieved through
Expand Down Expand Up @@ -340,7 +338,7 @@ require('./build/Release/addon');
Once the source code has been written, it must be compiled into the binary
`addon.node` file. To do so, create a file called `binding.gyp` in the
top-level of the project describing the build configuration of the module
using a JSON-like format. This file is used by [node-gyp][], a tool written
using a JSON-like format. This file is used by [`node-gyp`][], a tool written
specifically to compile Node.js addons.

```json
Expand Down Expand Up @@ -375,7 +373,7 @@ version of `node-gyp` to perform this same set of actions, generating a
compiled version of the addon for the user's platform on demand.

Once built, the binary addon can be used from within Node.js by pointing
[`require()`][require] to the built `addon.node` module:
[`require()`][] to the built `addon.node` module:

```js
// hello.js
Expand Down Expand Up @@ -422,18 +420,46 @@ aware of:
### Loading addons using `require()`

The filename extension of the compiled addon binary is `.node` (as opposed
to `.dll` or `.so`). The [`require()`][require] function is written to look for
to `.dll` or `.so`). The [`require()`][] function is written to look for
files with the `.node` file extension and initialize those as dynamically-linked
libraries.

When calling [`require()`][require], the `.node` extension can usually be
When calling [`require()`][], the `.node` extension can usually be
omitted and Node.js will still find and initialize the addon. One caveat,
however, is that Node.js will first attempt to locate and load modules or
JavaScript files that happen to share the same base name. For instance, if
there is a file `addon.js` in the same directory as the binary `addon.node`,
then [`require('addon')`][require] will give precedence to the `addon.js` file
then [`require('addon')`][`require()`] will give precedence to the `addon.js` file
and load it instead.

### Loading addons using `import`

<!-- YAML
added:
- v23.6.0
- v22.20.0
-->

> Stability: 1.0 - Early development

You can use the [`--experimental-addon-modules`][] flag to enable support for
both static `import` and dynamic `import()` to load binary addons.

If we reuse the Hello World example from earlier, you could do:

```mjs
// hello.mjs
import myAddon from './hello.node';
// N.B.: import {hello} from './hello.node' would not work

console.log(myAddon.hello());
```

```console
$ node --experimental-addon-modules hello.mjs
world
```

## Native abstractions for Node.js

Each of the examples illustrated in this document directly use the
Expand All @@ -453,59 +479,7 @@ illustration of how it can be used.

> Stability: 2 - Stable

Node-API is an API for building native addons. It is independent from
the underlying JavaScript runtime (e.g. V8) and is maintained as part of
Node.js itself. This API will be Application Binary Interface (ABI) stable
across versions of Node.js. It is intended to insulate addons from
changes in the underlying JavaScript engine and allow modules
compiled for one version to run on later versions of Node.js without
recompilation. Addons are built/packaged with the same approach/tools
outlined in this document (node-gyp, etc.). The only difference is the
set of APIs that are used by the native code. Instead of using the V8
or [Native Abstractions for Node.js][] APIs, the functions available
in the Node-API are used.

Creating and maintaining an addon that benefits from the ABI stability
provided by Node-API carries with it certain
[implementation considerations][].

To use Node-API in the above "Hello world" example, replace the content of
`hello.cc` with the following. All other instructions remain the same.

```cpp
// hello.cc using Node-API
#include <node_api.h>

namespace demo {

napi_value Method(napi_env env, napi_callback_info args) {
napi_value greeting;
napi_status status;

status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting);
if (status != napi_ok) return nullptr;
return greeting;
}

napi_value init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;

status = napi_create_function(env, nullptr, 0, Method, nullptr, &fn);
if (status != napi_ok) return nullptr;

status = napi_set_named_property(env, exports, "hello", fn);
if (status != napi_ok) return nullptr;
return exports;
}

NAPI_MODULE(NODE_GYP_MODULE_NAME, init)

} // namespace demo
```

The functions available and how to use them are documented in
[C/C++ addons with Node-API](n-api.md).
See [C/C++ addons with Node-API][Node-API].

## Addon examples

Expand Down Expand Up @@ -1378,18 +1352,20 @@ console.log(result);
// Prints: 30
```

[Building]: #building
[Electron]: https://electronjs.org/
[Embedder's Guide]: https://v8.dev/docs/embed
[Linking to libraries included with Node.js]: #linking-to-libraries-included-with-nodejs
[Native Abstractions for Node.js]: https://github.com/nodejs/nan
[Node-API]: n-api.md
[V8]: https://v8.dev/
[`--experimental-addon-modules`]: cli.md#--experimental-addon-modules
[`Worker`]: worker_threads.md#class-worker
[`libuv`]: https://github.com/libuv/libuv
[`node-gyp`]: https://github.com/nodejs/node-gyp
[`require()`]: modules.md#requireid
[bindings]: https://github.com/TooTallNate/node-bindings
[download]: https://github.com/nodejs/node-addon-examples
[examples]: https://github.com/nodejs/nan/tree/HEAD/examples/
[implementation considerations]: n-api.md#implications-of-abi-stability
[installation instructions]: https://github.com/nodejs/node-gyp#installation
[libuv]: https://github.com/libuv/libuv
[node-gyp]: https://github.com/nodejs/node-gyp
[require]: modules.md#requireid
[v8-docs]: https://v8docs.nodesource.com/
Loading