Stability: 2 - Stable
N-API (pronounced N as in the letter, followed by API) is an API for building native Addons. It is independent from the underlying JavaScript runtime (ex 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 major version to run on later major versions of Node.js without recompilation. The ABI Stability guide provides a more in-depth explanation.
Addons are built/packaged with the same approach/tools outlined in the section titled C++ Addons. 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 N-API are used.
APIs exposed by N-API are generally used to create and manipulate JavaScript values. Concepts and operations generally map to ideas specified in the ECMA262 Language Specification. The APIs have the following properties:
- All N-API calls return a status code of type
napi_status
. This status indicates whether the API call succeeded or failed. - The API's return value is passed via an out parameter.
- All JavaScript values are abstracted behind an opaque type named
napi_value
. - In case of an error status code, additional information can be obtained
using
napi_get_last_error_info
. More information can be found in the error handling section Error Handling.
The N-API is a C API that ensures ABI stability across Node.js versions
and different compiler levels. A C++ API can be easier to use.
To support using C++, the project maintains a
C++ wrapper module called
node-addon-api.
This wrapper provides an inlineable C++ API. Binaries built
with node-addon-api
will depend on the symbols for the N-API C-based
functions exported by Node.js. node-addon-api
is a more
efficient way to write code that calls N-API. Take, for example, the
following node-addon-api
code. The first section shows the
node-addon-api
code and the second section shows what actually gets
used in the addon.
Object obj = Object::New(env);
obj["foo"] = String::New(env, "bar");
napi_status status;
napi_value object, string;
status = napi_create_object(env, &object);
if (status != napi_ok) {
napi_throw_error(env, ...);
return;
}
status = napi_create_string_utf8(env, "bar", NAPI_AUTO_LENGTH, &string);
if (status != napi_ok) {
napi_throw_error(env, ...);
return;
}
status = napi_set_named_property(env, object, "foo", string);
if (status != napi_ok) {
napi_throw_error(env, ...);
return;
}
The end result is that the addon only uses the exported C APIs. As a result, it still gets the benefits of the ABI stability provided by the C API.
When using node-addon-api
instead of the C APIs, start with the API
docs
for node-addon-api
.
Although N-API provides an ABI stability guarantee, other parts of Node.js do not, and any external libraries used from the addon may not. In particular, none of the following APIs provide an ABI stability guarantee across major versions:
- the Node.js C++ APIs available via any of
#include <node.h> #include <node_buffer.h> #include <node_version.h> #include <node_object_wrap.h>
- the libuv APIs which are also included with Node.js and available via
#include <uv.h>
- the V8 API available via
#include <v8.h>
Thus, for an addon to remain ABI-compatible across Node.js major versions, it must make use exclusively of N-API by restricting itself to using
#include <node_api.h>
and by checking, for all external libraries that it uses, that the external library makes ABI stability guarantees similar to N-API.
In order to use the N-API functions, include the file
node_api.h
which is located in the src directory in the node development tree:
#include <node_api.h>
This will opt into the default NAPI_VERSION
for the given release of Node.js.
In order to ensure compatibility with specific versions of N-API, the version
can be specified explicitly when including the header:
#define NAPI_VERSION 3
#include <node_api.h>
This restricts the N-API surface to just the functionality that was available in the specified (and earlier) versions.
Some of the N-API surface is considered experimental and requires explicit opt-in to access those APIs:
#define NAPI_EXPERIMENTAL
#include <node_api.h>
In this case the entire API surface, including any experimental APIs, will be available to the module code.
1 | 2 | 3 | |
---|---|---|---|
v4.x | |||
v6.x | v6.14.2* | ||
v8.x | v8.0.0* | v8.10.0* | |
v9.x | v9.0.0* | v9.3.0* | v9.11.0* |
v10.x | v10.0.0 |
* Indicates that the N-API version was released as experimental
The N-APIs associated strictly with accessing ECMAScript features from native
code can be found separately in js_native_api.h
and js_native_api_types.h
.
The APIs defined in these headers are included in node_api.h
and
node_api_types.h
. The headers are structured in this way in order to allow
implementations of N-API outside of Node.js. For those implementations the
Node.js specific APIs may not be applicable.
The Node.js-specific parts of an addon can be separated from the code that
exposes the actual functionality to the JavaScript environment so that the
latter may be used with multiple implementations of N-API. In the example below,
addon.c
and addon.h
refer only to js_native_api.h
. This ensures that
addon.c
can be reused to compile against either the Node.js implementation of
N-API or any implementation of N-API outside of Node.js.
addon_node.c
is a separate file that contains the Node.js specific entry point
to the addon and which instantiates the addon by calling into addon.c
when the
addon is loaded into a Node.js environment.
// addon.h
#ifndef _ADDON_H_
#define _ADDON_H_
#include <js_native_api.h>
napi_value create_addon(napi_env env);
#endif // _ADDON_H_
// addon.c
#include "addon.h"
napi_value create_addon(napi_env env) {
napi_value result;
assert(napi_create_object(env, &result) == napi_ok);
napi_value exported_function;
assert(napi_create_function(env,
"doSomethingUseful",
NAPI_AUTO_LENGTH,
DoSomethingUseful,
NULL,
&exported_function) == napi_ok);
assert(napi_set_named_property(env,
result,
"doSomethingUseful",
exported_function) == napi_ok);
return result;
}
// addon_node.c
#include <node_api.h>
static napi_value Init(napi_env env, napi_value exports) {
return create_addon(env);
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
N-API exposes the following fundamental datatypes as abstractions that are consumed by the various APIs. These APIs should be treated as opaque, introspectable only with other N-API calls.
Integral status code indicating the success or failure of a N-API call. Currently, the following status codes are supported.
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_expected,
napi_name_expected,
napi_function_expected,
napi_number_expected,
napi_boolean_expected,
napi_array_expected,
napi_generic_failure,
napi_pending_exception,
napi_cancelled,
napi_escape_called_twice,
napi_handle_scope_mismatch,
napi_callback_scope_mismatch,
napi_queue_full,
napi_closing,
napi_bigint_expected,
} napi_status;
If additional information is required upon an API returning a failed status,
it can be obtained by calling napi_get_last_error_info
.
typedef struct {
const char* error_message;
void* engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
} napi_extended_error_info;
error_message
: UTF8-encoded string containing a VM-neutral description of the error.engine_reserved
: Reserved for VM-specific error details. This is currently not implemented for any VM.engine_error_code
: VM-specific error code. This is currently not implemented for any VM.error_code
: The N-API status code that originated with the last error.
See the Error Handling section for additional information.
napi_env
is used to represent a context that the underlying N-API
implementation can use to persist VM-specific state. This structure is passed
to native functions when they're invoked, and it must be passed back when
making N-API calls. Specifically, the same napi_env
that was passed in when
the initial native function was called must be passed to any subsequent
nested N-API calls. Caching the napi_env
for the purpose of general reuse is
not allowed.
This is an opaque pointer that is used to represent a JavaScript value.
Stability: 2 - Stable
This is an opaque pointer that represents a JavaScript function which can be
called asynchronously from multiple threads via
napi_call_threadsafe_function()
.
Stability: 2 - Stable
A value to be given to napi_release_threadsafe_function()
to indicate whether
the thread-safe function is to be closed immediately (napi_tsfn_abort
) or
merely released (napi_tsfn_release
) and thus available for subsequent use via
napi_acquire_threadsafe_function()
and napi_call_threadsafe_function()
.
typedef enum {
napi_tsfn_release,
napi_tsfn_abort
} napi_threadsafe_function_release_mode;
Stability: 2 - Stable
A value to be given to napi_call_threadsafe_function()
to indicate whether
the call should block whenever the queue associated with the thread-safe
function is full.
typedef enum {
napi_tsfn_nonblocking,
napi_tsfn_blocking
} napi_threadsafe_function_call_mode;
This is an abstraction used to control and modify the lifetime of objects created within a particular scope. In general, N-API values are created within the context of a handle scope. When a native method is called from JavaScript, a default handle scope will exist. If the user does not explicitly create a new handle scope, N-API values will be created in the default handle scope. For any invocations of code outside the execution of a native method (for instance, during a libuv callback invocation), the module is required to create a scope before invoking any functions that can result in the creation of JavaScript values.
Handle scopes are created using napi_open_handle_scope
and are destroyed
using napi_close_handle_scope
. Closing the scope can indicate to the GC
that all napi_value
s created during the lifetime of the handle scope are no
longer referenced from the current stack frame.
For more details, review the Object Lifetime Management.
Escapable handle scopes are a special type of handle scope to return values created within a particular handle scope to a parent scope.
This is the abstraction to use to reference a napi_value
. This allows for
users to manage the lifetimes of JavaScript values, including defining their
minimum lifetimes explicitly.
For more details, review the Object Lifetime Management.
Opaque datatype that is passed to a callback function. It can be used for getting additional information about the context in which the callback was invoked.
Function pointer type for user-provided native functions which are to be exposed to JavaScript via N-API. Callback functions should satisfy the following signature:
typedef napi_value (*napi_callback)(napi_env, napi_callback_info);
Function pointer type for add-on provided functions that allow the user to be
notified when externally-owned data is ready to be cleaned up because the
object with which it was associated with, has been garbage-collected. The user
must provide a function satisfying the following signature which would get
called upon the object's collection. Currently, napi_finalize
can be used for
finding out when objects that have external data are collected.
typedef void (*napi_finalize)(napi_env env,
void* finalize_data,
void* finalize_hint);
Function pointer used with functions that support asynchronous operations. Callback functions must statisfy the following signature:
typedef void (*napi_async_execute_callback)(napi_env env, void* data);
Implementations of this type of function should avoid making any N-API calls
that could result in the execution of JavaScript or interaction with
JavaScript objects. Most often, any code that needs to make N-API
calls should be made in napi_async_complete_callback
instead.
Function pointer used with functions that support asynchronous operations. Callback functions must statisfy the following signature:
typedef void (*napi_async_complete_callback)(napi_env env,
napi_status status,
void* data);
Stability: 2 - Stable
Function pointer used with asynchronous thread-safe function calls. The callback
will be called on the main thread. Its purpose is to use a data item arriving
via the queue from one of the secondary threads to construct the parameters
necessary for a call into JavaScript, usually via napi_call_function
, and then
make the call into JavaScript.
The data arriving from the secondary thread via the queue is given in the data
parameter and the JavaScript function to call is given in the js_callback
parameter.
N-API sets up the environment prior to calling this callback, so it is
sufficient to call the JavaScript function via napi_call_function
rather than
via napi_make_callback
.
Callback functions must satisfy the following signature:
typedef void (*napi_threadsafe_function_call_js)(napi_env env,
napi_value js_callback,
void* context,
void* data);
[in] env
: The environment to use for API calls, orNULL
if the thread-safe function is being torn down anddata
may need to be freed.[in] js_callback
: The JavaScript function to call, orNULL
if the thread-safe function is being torn down anddata
may need to be freed.[in] context
: The optional data with which the thread-safe function was created.[in] data
: Data created by the secondary thread. It is the responsibility of the callback to convert this native data to JavaScript values (with N-API functions) that can be passed as parameters whenjs_callback
is invoked. This pointer is managed entirely by the threads and this callback. Thus this callback should free the data.
N-API uses both return values and JavaScript exceptions for error handling. The following sections explain the approach for each case.
All of the N-API functions share the same error handling pattern. The
return type of all API functions is napi_status
.
The return value will be napi_ok
if the request was successful and
no uncaught JavaScript exception was thrown. If an error occurred AND
an exception was thrown, the napi_status
value for the error
will be returned. If an exception was thrown, and no error occurred,
napi_pending_exception
will be returned.
In cases where a return value other than napi_ok
or
napi_pending_exception
is returned, napi_is_exception_pending
must be called to check if an exception is pending.
See the section on exceptions for more details.
The full set of possible napi_status
values is defined
in napi_api_types.h
.
The napi_status
return value provides a VM-independent representation of
the error which occurred. In some cases it is useful to be able to get
more detailed information, including a string representing the error as well as
VM (engine)-specific information.
In order to retrieve this information napi_get_last_error_info
is provided which returns a napi_extended_error_info
structure.
The format of the napi_extended_error_info
structure is as follows:
typedef struct napi_extended_error_info {
const char* error_message;
void* engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
};
error_message
: Textual representation of the error that occurred.engine_reserved
: Opaque handle reserved for engine use only.engine_error_code
: VM specific error code.error_code
: n-api status code for the last error.
napi_get_last_error_info
returns the information for the last
N-API call that was made.
Do not rely on the content or format of any of the extended information as it is not subject to SemVer and may change at any time. It is intended only for logging purposes.
napi_status
napi_get_last_error_info(napi_env env,
const napi_extended_error_info** result);
[in] env
: The environment that the API is invoked under.[out] result
: Thenapi_extended_error_info
structure with more information about the error.
Returns napi_ok
if the API succeeded.
This API retrieves a napi_extended_error_info
structure with information
about the last error that occurred.
The content of the napi_extended_error_info
returned is only valid up until
an n-api function is called on the same env
.
Do not rely on the content or format of any of the extended information as it is not subject to SemVer and may change at any time. It is intended only for logging purposes.
This API can be called even if there is a pending JavaScript exception.
Any N-API function call may result in a pending JavaScript exception. This is obviously the case for any function that may cause the execution of JavaScript, but N-API specifies that an exception may be pending on return from any of the API functions.
If the napi_status
returned by a function is napi_ok
then no
exception is pending and no additional action is required. If the
napi_status
returned is anything other than napi_ok
or
napi_pending_exception
, in order to try to recover and continue
instead of simply returning immediately, napi_is_exception_pending
must be called in order to determine if an exception is pending or not.
In many cases when an N-API function is called and an exception is
already pending, the function will return immediately with a
napi_status
of napi_pending_exception
. However, this is not the case
for all functions. N-API allows a subset of the functions to be
called to allow for some minimal cleanup before returning to JavaScript.
In that case, napi_status
will reflect the status for the function. It
will not reflect previous pending exceptions. To avoid confusion, check
the error status after every function call.
When an exception is pending one of two approaches can be employed.
The first approach is to do any appropriate cleanup and then return so that
execution will return to JavaScript. As part of the transition back to
JavaScript the exception will be thrown at the point in the JavaScript
code where the native method was invoked. The behavior of most N-API calls
is unspecified while an exception is pending, and many will simply return
napi_pending_exception
, so it is important to do as little as possible
and then return to JavaScript where the exception can be handled.
The second approach is to try to handle the exception. There will be cases
where the native code can catch the exception, take the appropriate action,
and then continue. This is only recommended in specific cases
where it is known that the exception can be safely handled. In these
cases napi_get_and_clear_last_exception
can be used to get and
clear the exception. On success, result will contain the handle to
the last JavaScript Object
thrown. If it is determined, after
retrieving the exception, the exception cannot be handled after all
it can be re-thrown it with napi_throw
where error is the
JavaScript Error
object to be thrown.
The following utility functions are also available in case native code
needs to throw an exception or determine if a napi_value
is an instance
of a JavaScript Error
object: napi_throw_error
,
napi_throw_type_error
, napi_throw_range_error
and
napi_is_error
.
The following utility functions are also available in case native
code needs to create an Error
object: napi_create_error
,
napi_create_type_error
, and napi_create_range_error
,
where result is the napi_value
that refers to the newly created
JavaScript Error
object.
The Node.js project is adding error codes to all of the errors
generated internally. The goal is for applications to use these
error codes for all error checking. The associated error messages
will remain, but will only be meant to be used for logging and
display with the expectation that the message can change without
SemVer applying. In order to support this model with N-API, both
in internal functionality and for module specific functionality
(as its good practice), the throw_
and create_
functions
take an optional code parameter which is the string for the code
to be added to the error object. If the optional parameter is NULL
then no code will be associated with the error. If a code is provided,
the name associated with the error is also updated to be:
originalName [code]
where originalName
is the original name associated with the error
and code
is the code that was provided. For example, if the code
is 'ERR_ERROR_1'
and a TypeError
is being created the name will be: