napi-rs

April 27, 2022 · View on GitHub

This project was initialized from xray

A framework for building compiled Node.js add-ons in Rust via Node-API. Website: https://napi.rs

chat

Sponsors

Platform Support

Test & Release Address Sanitizer Memory Leak Detect

MSRV

Rust 1.88.0

node12node14node16node18node20node22
Windows x64✓✓✓✓✓✓
Windows x86✓✓✓✓✓✓
Windows arm64✓✓✓✓✓✓
macOS x64✓✓✓✓✓✓
macOS aarch64✓✓✓✓✓✓
Linux x64 gnu✓✓✓✓✓✓
Linux x64 musl✓✓✓✓✓✓
Linux aarch64 gnu✓✓✓✓✓✓
Linux aarch64 musl✓✓✓✓✓✓
Linux arm gnueabihf✓✓✓✓✓✓
Linux arm muslebihf✓✓✓✓✓✓
Linux powerpc64le gnu✓✓✓✓✓✓
Linux s390x gnu✓✓✓✓✓✓
Linux loong64 gnuN/AN/AN/A✓✓✓
Linux riscv64 gnuN/AN/A✓✓✓✓
Linux aarch64 android✓✓✓✓✓✓
Linux armv7 android✓✓✓✓✓✓
FreeBSD x64✓✓✓✓✓✓

This library depends on Node-API and requires Node@10.0.0 or later.

We already have some packages written by napi-rs: node-rs

One nice feature is that this crate allows you to build add-ons purely with the Rust/JavaScript toolchain and without involving node-gyp.

Taste

You can start from package-template to play with napi-rs

Define JavaScript functions

use napi::bindgen_prelude::*;
use napi_derive::napi;

/// module registration is done by the runtime, no need to explicitly do it now.
#[napi]
pub fn fibonacci(n: u32) -> u32 {
  match n {
    1 | 2 => 1,
    _ => fibonacci(n - 1) + fibonacci(n - 2),
  }
}

/// use `Fn`, `FnMut` or `FnOnce` traits to defined JavaScript callbacks
/// the return type of callbacks can only be `Result`.
#[napi]
pub fn get_cwd<T: Fn(String) -> Result<()>>(callback: T) {
  callback(
    std::env::current_dir()
      .unwrap()
      .to_string_lossy()
      .to_string(),
  )
  .unwrap();
}

/// or, define the callback signature in where clause
#[napi]
pub fn test_callback<T>(callback: T) -> Result<()>
where
  T: Fn(String) -> Result<()>,
{
  callback(std::env::current_dir()?.to_string_lossy().to_string())
}

/// async fn, require `async` feature enabled.
/// [dependencies]
/// napi = {version="2", features=["async"]}
#[napi]
pub async fn read_file_async(path: String) -> Result<Buffer> {
  Ok(tokio::fs::read(path).await?.into())
}

more examples at examples

Building

This repository is a Cargo crate. Any napi-based add-on should contain Cargo.toml to make it a Cargo crate.

In your Cargo.toml you need to set the crate-type to "cdylib" so that cargo builds a C-style shared library that can be dynamically loaded by the Node executable. You'll also need to add this crate as a dependency.

[package]
name = "awesome"

[lib]
crate-type = ["cdylib"]

[dependencies]
napi = "3"
napi-derive = "3"

[build-dependencies]
napi-build = "1"

And create build.rs in your own project:

// build.rs
extern crate napi_build;

fn main() {
  napi_build::setup();
}

So far, the napi build script has only been tested on macOS Linux Windows x64 MSVC and FreeBSD.

Install @napi-rs/cli as a local development dependency to build the Rust crate and copy its dynamic library to a loadable .node file.

{
  "name": "awesome-package",
  "devDependencies": {
    "@napi-rs/cli": "^3.0.0"
  },
  "napi": {
    "binaryName": "jarvis"
  },
  "scripts": {
    "build": "napi build --release",
    "build:debug": "napi build"
  }
}

Then you can require your native binding:

require('./jarvis.node')

The module_name would be your package name in your Cargo.toml.

xxx => ./xxx.node

xxx-yyy => ./xxx_yyy.node

You can also copy Dynamic lib file to an appointed location:

napi build [--release] ./dll
napi build [--release] ./artifacts

There are documents which contains more details about the @napi-rs/cli usage.

Testing

Because libraries that depend on this crate must be loaded into a Node executable in order to resolve symbols, all tests are written in JavaScript in the test_module subdirectory.

To run tests:

yarn build:test
yarn test

Features table

Rust TypeNode TypeNAPI VersionMinimal Node versionEnable by napi feature
u32Number1v8.0.0
i32/i64Number1v8.0.0
f64Number1v8.0.0
boolBoolean1v8.0.0
String/&'a strString1v8.0.0
OsString/&'a OsStrString1v8.0.0
PathBuf/&'a PathString1v8.0.0
Latin1StringString1v8.0.0latin1
UTF16StringString1v8.0.0
ObjectObject1v8.0.0
serde_json::MapObject1v8.0.0serde-json
serde_json::Valueany1v8.0.0serde-json
ArrayArray1v8.0.0
VecArray1v8.0.0
BufferBuffer1v8.0.0
ExternalExternal1v8.0.0
Nullnull1v8.0.0
Undefined/()undefined1v8.0.0
Result<()>Error1v8.0.0
T: Fn(...) -> ResultFunction1v8.0.0
Async/FuturePromise4v10.6.0async
AsyncTaskPromise1v8.5.0
JsGlobalglobal1v8.0.0
JsSymbolSymbol1v8.0.0
Int8Array/Uint8Array ...TypedArray1v8.0.0
JsFunctionthreadsafe function4v10.6.0napi4
BigIntBigInt6v10.7.0napi6