Node-API (formerly N-API) is an API for building native addons. It is independent from the underlying JavaScript runtime (V8) and is maintained as part of Node.js itself.
Overview
Node-API provides an Application Binary Interface (ABI) stable API across versions of Node.js. It:
- Insulates addons from changes in the underlying JavaScript engine
- Allows modules compiled for one major version to run on later versions without recompilation
- Uses the same approach/tools as C++ Addons (node-gyp, etc.)
- Provides a different set of APIs focused on ABI stability
Key Features
- ABI Stability: Binary compatibility across Node.js versions
- Version Independent: No need to recompile for new Node.js releases
- Status-based Error Handling: All calls return
napi_status
- Opaque Types: JavaScript values abstracted behind
napi_value
- Multiple Language Support: Can be used from C, C++, Rust, and other languages
Building with Node-API
Unlike modules written in JavaScript, developing Node-API addons requires:
-
C/C++ Toolchain
- Linux: GCC or LLVM
- macOS: Xcode command line tools (
xcode-select --install)
- Windows: Visual Studio or
npm install --global windows-build-tools
-
Build Tools
- node-gyp: Most common, uses GYP build system
- CMake.js: Alternative using CMake
-
Deployment Options
- node-pre-gyp: Upload binaries to custom servers
- prebuild: Upload binaries to GitHub releases
- prebuildify: Bundle binaries with npm package
Usage
Include the Node-API header:
This opts into the default NAPI_VERSION for the Node.js release.
Specifying Version
For compatibility with specific Node-API versions:
#define NAPI_VERSION 3
#include <node_api.h>
Experimental Features
To use experimental APIs:
#define NAPI_EXPERIMENTAL
#include <node_api.h>
Node-API Version Matrix
Node-API versions are additive up to version 9. From version 9 onwards, add-ons may need code updates between major versions while maintaining ABI stability.
| Node-API Version | Supported In |
|---|
| 10 | v22.14.0+, 23.6.0+ and later |
| 9 | v18.17.0+, 20.3.0+, 21.0.0+ |
| 8 | v12.22.0+, v14.17.0+, v15.12.0+, 16.0.0+ |
| 7 | v10.23.0+, v12.19.0+, v14.12.0+, 15.0.0+ |
| 6 | v10.20.0+, v12.17.0+, 14.0.0+ |
Basic Data Types
napi_status
Integral status code indicating success or failure:
typedef enum {
napi_ok,
napi_invalid_arg,
napi_object_expected,
napi_string_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_status;
napi_env
Represents a context for Node-API operations. Must be passed to most Node-API functions.
Do not cache or share napi_env between Worker threads. Each thread receives its own napi_env.
napi_value
Opaque pointer representing a JavaScript value.
napi_callback
Function pointer type for native functions exposed to JavaScript:
typedef napi_value (*napi_callback)(napi_env env, napi_callback_info info);
Error Handling
Node-API uses both return values and JavaScript exceptions for error handling.
Return Values
All Node-API functions return napi_status:
napi_ok: Success, no exception
napi_pending_exception: Exception pending, no error
- Other values: Error occurred
napi_status napi_get_last_error_info(node_api_basic_env env,
const napi_extended_error_info** result);
Returns detailed error information including:
error_message: Human-readable error description
error_code: Node-API status code
engine_error_code: VM-specific error code
Throwing Exceptions
// Throw generic error
napi_status napi_throw_error(napi_env env,
const char* code,
const char* msg);
// Throw TypeError
napi_status napi_throw_type_error(napi_env env,
const char* code,
const char* msg);
// Throw RangeError
napi_status napi_throw_range_error(napi_env env,
const char* code,
const char* msg);
Creating Error Objects
napi_status napi_create_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result);
napi_status napi_create_type_error(napi_env env,
napi_value code,
napi_value msg,
napi_value* result);
Working with JavaScript Values
Creating Values
// Create string
napi_status napi_create_string_utf8(napi_env env,
const char* str,
size_t length,
napi_value* result);
// Create number
napi_status napi_create_double(napi_env env,
double value,
napi_value* result);
// Create boolean
napi_status napi_get_boolean(napi_env env,
bool value,
napi_value* result);
// Create object
napi_status napi_create_object(napi_env env,
napi_value* result);
// Create array
napi_status napi_create_array(napi_env env,
napi_value* result);
Type Checking
napi_status napi_typeof(napi_env env,
napi_value value,
napi_valuetype* result);
napi_status napi_is_array(napi_env env,
napi_value value,
bool* result);
Converting Values
// Get string
napi_status napi_get_value_string_utf8(napi_env env,
napi_value value,
char* buf,
size_t bufsize,
size_t* result);
// Get number
napi_status napi_get_value_double(napi_env env,
napi_value value,
double* result);
// Get boolean
napi_status napi_get_value_bool(napi_env env,
napi_value value,
bool* result);
Working with Objects
Setting Properties
napi_status napi_set_named_property(napi_env env,
napi_value object,
const char* utf8name,
napi_value value);
napi_status napi_set_property(napi_env env,
napi_value object,
napi_value key,
napi_value value);
Getting Properties
napi_status napi_get_named_property(napi_env env,
napi_value object,
const char* utf8name,
napi_value* result);
napi_status napi_get_property(napi_env env,
napi_value object,
napi_value key,
napi_value* result);
Checking Properties
napi_status napi_has_named_property(napi_env env,
napi_value object,
const char* utf8name,
bool* result);
Working with Functions
Creating Functions
napi_status napi_create_function(napi_env env,
const char* utf8name,
size_t length,
napi_callback cb,
void* data,
napi_value* result);
Calling Functions
napi_status napi_call_function(napi_env env,
napi_value recv,
napi_value func,
size_t argc,
const napi_value* argv,
napi_value* result);
Getting Callback Info
napi_status napi_get_cb_info(napi_env env,
napi_callback_info cbinfo,
size_t* argc,
napi_value* argv,
napi_value* this_arg,
void** data);
Memory Management
Handle Scopes
Handle scopes manage the lifetime of napi_value objects:
// Open handle scope
napi_status napi_open_handle_scope(napi_env env,
napi_handle_scope* result);
// Close handle scope
napi_status napi_close_handle_scope(napi_env env,
napi_handle_scope scope);
References
References allow control over object lifetimes:
// Create reference
napi_status napi_create_reference(napi_env env,
napi_value value,
uint32_t initial_refcount,
napi_ref* result);
// Delete reference
napi_status napi_delete_reference(napi_env env,
napi_ref ref);
// Get reference value
napi_status napi_get_reference_value(napi_env env,
napi_ref ref,
napi_value* result);
Instance Data
Associate data with a Node.js environment instance:
// Set instance data
napi_status napi_set_instance_data(node_api_basic_env env,
void* data,
napi_finalize finalize_cb,
void* finalize_hint);
// Get instance data
napi_status napi_get_instance_data(node_api_basic_env env,
void** data);
Async Operations
Async Work
// Create async work
napi_status napi_create_async_work(napi_env env,
napi_value async_resource,
napi_value async_resource_name,
napi_async_execute_callback execute,
napi_async_complete_callback complete,
void* data,
napi_async_work* result);
// Queue async work
napi_status napi_queue_async_work(napi_env env,
napi_async_work work);
Thread-safe Functions
// Create thread-safe function
napi_status napi_create_threadsafe_function(
napi_env env,
napi_value func,
napi_value async_resource,
napi_value async_resource_name,
size_t max_queue_size,
size_t initial_thread_count,
void* thread_finalize_data,
napi_finalize thread_finalize_cb,
void* context,
napi_threadsafe_function_call_js call_js_cb,
napi_threadsafe_function* result);
// Call thread-safe function
napi_status napi_call_threadsafe_function(
napi_threadsafe_function func,
void* data,
napi_threadsafe_function_call_mode is_blocking);
Example: Hello World
#include <node_api.h>
static napi_value Method(napi_env env, napi_callback_info info) {
napi_status status;
napi_value greeting;
status = napi_create_string_utf8(env, "world", NAPI_AUTO_LENGTH, &greeting);
if (status != napi_ok) return NULL;
return greeting;
}
static napi_value Init(napi_env env, napi_value exports) {
napi_status status;
napi_value fn;
status = napi_create_function(env, NULL, 0, Method, NULL, &fn);
if (status != napi_ok) return NULL;
status = napi_set_named_property(env, exports, "hello", fn);
if (status != napi_ok) return NULL;
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)
C++ Wrapper: node-addon-api
For C++ developers, node-addon-api provides a more efficient wrapper:
#include <napi.h>
Napi::String Method(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
return Napi::String::New(env, "world");
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set("hello", Napi::Function::New(env, Method));
return exports;
}
NODE_API_MODULE(hello, Init)
Best Practices
- Check Return Values: Always check
napi_status return values
- Handle Scopes: Properly manage handle scope lifetimes
- Error Handling: Use appropriate error handling for both sync and async operations
- Thread Safety: Use thread-safe functions for cross-thread communication
- Memory Management: Use references and finalizers to manage object lifetimes
- Version Compatibility: Define appropriate
NAPI_VERSION for your needs
Further Resources