node-bindgen
Features
- Easy: Just write idiomatic Rust code, node-bindgen take care of generating Node.js FFI wrapper codes.
- Safe: Node.js arguments are checked automatically based on Rust types.
- Async: Support Async Rust. Async codes are translated into Node.js promises.
- Class: Rust struct can be accessed using Node.js classes.
- Stream: Implement Node.js stream using Rust
- N-API: Use Node.js N-API, which means you don't have to recompile your module.
Compatibility with Node.js version
This project uses the v7 of Node N-API. Please see following compatibility matrix.
Following OS are supported:
- Linux
- MacOs
- Windows
Why node-bindgen?
Writing native node-js requires lots of boilerplate code. Node-bindgen generates external "C" glue code from rust code, including native module registration. node-bindgen make it writing node-js module easy and fun.
Getting started
CLI Installation
Install nj-cli command line, which will be used to generate the native library.
cargo install nj-cli
This is one time step.
Configuring Cargo.toml
Add two dependencies to your projects' Cargo.toml
.
Add node-bindgen
as a regular dependency (as below):
[dependencies]
node-bindgen = { version = "4.0" }
Then add node-bindgen
's procedure macro to your build-dependencies as below:
[build-dependencies]
node-bindgen = { version = "4.0", features = ["build"] }
Then update crate type to cdylib
to generate node.js compatible native module:
[lib]
crate-type = ["cdylib"]
Finally, add build.rs
at the top of the project with following content:
fn main() {
node_bindgen::build::configure();
}
Example
Here is a function that adds two numbers. Note that you don't need to worry about JS conversion.
use node_bindgen::derive::node_bindgen;
/// add two integer
#[node_bindgen]
fn sum(first: i32, second: i32) -> i32 {
first + second
}
Building native library
To build node.js library, using nj-cli
to build:
nj-cli build
This will generate Node.js module in "./dist" folder.
To build a release version:
nj-cli build --release
./src
for Changes
Watching While developing your native module, you may want to watch for file changes and run a command when a change occurs, for example cargo check
or cargo build
.
For this, we can use nj-cli watch
.
nj-cli watch
installs [if it does not exist] and passes arguments to cargo watch
. By default, nj-cli watch
will run cargo check
against your ./src
files.
To see all available methods for nj-cli watch
, run the following command:
nj-cli watch -- --help
Using in Node.js
Then in the Node.js, rust function can be invoked as normal node.js function:
$ node
Welcome to Node.js v14.0.0.
Type ".help" for more information.
> let addon = require('./dist');
undefined
> addon.sum(2,3)
5
>
Features
Function name or method can be renamed instead of default mapping
#[node_bindgen(name="multiply")]
fn mul(first: i32,second: i32) -> i32 {
first * second
}
Rust function mul is re-mapped as multiply
Optional argument
Argument can be skipped if it is marked as optional
#[node_bindgen]
fn sum(first: i32, second: Option<i32>) -> i32 {
first + second.unwrap_or(0)
}
Then sum can be invoked as sum(10)
or sum(10,20)
Callback
JS callback are mapped as Rust closure.
#[node_bindgen]
fn hello<F: Fn(String)>(first: f64, second: F) {
let msg = format!("argument is: {}", first);
second(msg);
}
from node:
let addon = require('./dist');
addon.hello(2,function(msg){
assert.equal(msg,"argument is: 2");
console.log(msg); // print out argument is 2
});
Callback are supported in Async rust as well.
Support for Async Rust
Async rust function is mapped to Node.js promise.
use std::time::Duration;
use flv_future_aio::time::sleep;
use node_bindgen::derive::node_bindgen;
#[node_bindgen]
async fn hello(arg: f64) -> f64 {
println!("sleeping");
sleep(Duration::from_secs(1)).await;
println!("woke and adding 10.0");
arg + 10.0
}
let addon = require('./dist');
addon.hello(5).then((val) => {
console.log("future value is %s",val);
});
JavaScript class
JavaScript class is supported.
struct MyClass {
val: f64,
}
#[node_bindgen]
impl MyClass {
#[node_bindgen(constructor)]
fn new(val: f64) -> Self {
Self { val }
}
#[node_bindgen]
fn plus_one(&self) -> f64 {
self.val + 1.0
}
#[node_bindgen(getter)]
fn value(&self) -> f64 {
self.val
}
}
let addon = require('./dist');
const assert = require('assert');
let obj = new addon.MyObject(10);
assert.equal(obj.value,10,"verify value works");
assert.equal(obj.plusOne(),11);
There are more features in the examples folder.
Windows + Electron Support
When using node-bindgen with electron on Windows, nj-build
must compile a C++ file, win_delay_load_hook.cc
, and therefore it is required that the development environment has a valid C/C++ compiler.
If your machine does not have a valid C/C++ compiler, install Microsoft VSCode.
In the future, this file will be re-written in Rust, removing this dependency.
Contributing
If you'd like to contribute to the project, please read our Contributing guide.
License
This project is licensed under the Apache license.