How Node.js Runs C++ and Rust Native Addons

Node.js allows developers to execute high-performance, low-level code written in C++ or Rust directly within the JavaScript runtime using native addons. This article explains how Node.js manages these native addons through Node-API (formerly N-API), detailing the role of Application Binary Interface (ABI) stability, how the translation layer works, and how JavaScript loads and executes compiled binary code.

What is Node-API (N-API)?

Historically, writing native addons for Node.js required interacting directly with the V8 JavaScript engine APIs. However, because V8 changes frequently, addons had to be recompiled for every major Node.js release.

To solve this, Node.js introduced Node-API (originally called N-API). Node-API is a fully stable, engine-independent API for building native addons. It provides an abstraction layer over the V8 engine, guaranteeing Application Binary Interface (ABI) stability. This means an addon compiled for one version of Node.js will run on newer versions of Node.js without needing recompilation, even if the underlying V8 engine undergoes major updates.

The Bridge: How JavaScript Communicates with Native Code

Node-API acts as a translator between the managed heap of JavaScript and the unmanaged memory of C++ or Rust.

  1. Type Conversion: JavaScript types (such as Objects, Strings, and Numbers) do not exist in C++ or Rust. Node-API provides C-style functions to safely convert JavaScript values into native types and vice versa (e.g., converting a JavaScript Number to a C++ double).
  2. Context and Scope Management: Node-API manages the lifecycle of JavaScript objects passed to native code. It prevents the V8 garbage collector from deleting objects while they are being processed by C++ or Rust.
  3. Threading: JavaScript runs on a single main thread. Node-API allows native addons to offload CPU-intensive tasks to worker threads using the underlying libuv thread pool, preventing the JavaScript event loop from blocking.

How C++ Addons are Handled

For C++ development, the community uses node-addon-api, a header-only C++ wrapper class that simplifies the plain C functions of Node-API.

How Rust Addons are Handled

Rust has become highly popular for Node.js addons due to its memory safety and performance. The integration relies on tools like napi-rs or Neon, with napi-rs being the industry standard for Node-API integration.

Loading and Executing the Addon in JavaScript

Once the C++ or Rust code is compiled into a .node binary, loading it in JavaScript is straightforward.

Node.js treats .node files as native modules. When you call:

const nativeAddon = require('./build/Release/addon.node');

Node.js internally uses the operating system’s dynamic linker (such as dlopen on macOS/Linux or LoadLibrary on Windows) to load the binary into the process’s memory space.

Once loaded, the exported native functions are mapped to JavaScript properties. Developers can then invoke these functions just like standard JavaScript functions, enjoying near-native execution speeds for computationally heavy tasks.