Chuyển đến nội dung chính

Lesson 16: Native Addons & N-API

N-API (Node-API), napi-rs (Rust bindings). node-gyp, prebuild. C/C++ addons, performance-critical native code. When to use native addons vs Worker Threads vs WASM.

💻 Programming — Lesson 16 Lesson 16: Native Addons & N-API

Node.js Core: From Basics to Advanced

Part 4: Building Without Frameworks

xdev.asia

1. N-API (Node-API)

Node-API is a stable ABI (Application Binary Interface) that allows writing native addons in C/C++ or Rust without rebuilding when upgrading Node.js version.

ApproachLanguageBuild toolsWhen to use
node-addon-apiC++node-gypC/C++ libraries are available
napi-rsRustcargo.cargoRust projects, performance
WASMAny → WASMwasm-packPortable, sandbox
Worker ThreadsJavaScriptNoneCPU tasks are simple

2. napi-rs (Rust → Node.js)

# Tạo project napi-rs
npm create napi@latest
# Chọn: @myapp/native, supported platforms
// src/lib.rs
use napi_derive::napi;
use napi::Result;

#[napi]
pub fn fibonacci(n: u32) -> u64 {
    match n {
        0 => 0,
        1 => 1,
        _ => {
            let (mut a, mut b) = (0u64, 1u64);
            for _ in 2..=n {
                let tmp = a + b;
                a = b;
                b = tmp;
            }
            b
        }
    }
}

#[napi]
pub fn hash_password(password: String, salt: String) -> Result {
    use argon2::{self, Config};
    let config = Config::default();
    argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &config)
        .map_err(|e| napi::Error::from_reason(e.to_string()))
}
// Sử dụng trong JS/TS
import { fibonacci, hashPassword } from '@myapp/native'

console.log(fibonacci(50))  // 12586269025 — nhanh hơn JS
const hash = hashPassword('mypassword', 'randomsalt')

3. C++ Addon with node-addon-api

// addon.cc
#include <napi.h>

Napi::Value Add(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  double a = info[0].As<Napi::Number>().DoubleValue();
  double b = info[1].As<Napi::Number>().DoubleValue();
  return Napi::Number::New(env, a + b);
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set("add", Napi::Function::New(env, Add));
  return exports;
}

NODE_API_MODULE(addon, Init)
// binding.gyp
{
  "targets": [{
    "target_name": "addon",
    "sources": ["addon.cc"],
    "include_dirs": ["<!@(node -p \"require('node-addon-api').include\")"]
  }]
}

4. Prebuild & Distribution

{
  "scripts": {
    "build": "napi build --release",
    "prepublishOnly": "napi prepublish -t npm"
  },
  "napi": {
    "name": "mylib",
    "triples": {
      "defaults": true,
      "additional": ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu"]
    }
  }
}

5. When to use Native Addons?

// Benchmark: Fibonacci(40)
// JavaScript:    ~1200ms
// Rust (napi-rs): ~35ms  (34x faster)
// WASM:          ~50ms   (24x faster)

// → Dùng native addons khi:
// 1. CPU-bound tasks (crypto, image processing, compression)
// 2. Cần tích hợp C/C++ libraries (FFmpeg, OpenCV)
// 3. Performance là critical
// 4. Cần memory-safe parallel processing (Rust)

Next article: Testing Node.js Applications — node:test, Vitest, Supertest.