🕸️ Using rust modules in JavaScript/Web Development (Part 3/ 3) [NodeJS]

Please read the Part 1 of the series here :

For this part of the series, you can safely skip Part 2, but if you are interested :

*Prerequisite:

Setup the rust project from Part 1 of this post. Follow the Setting up the rust project part of the post.

Hence you should have a simple calculator project in rust ready before continuing.

Lets Begin

We will do the following:

  1. Compile our rust module as a dynamic library.
  2. Use node-ffi (Node.js Foreign Function Interface) to use the functions exported from rust module.

Generating a dynamic library file

To generate a dynamic library file from a rust file run the compile command with the following arguments

rustc --crate-type=cdylib src/lib.rs -o libweb.dylib

PS: This is a slightly different command as compared to the one we used in Part 1

Note: If you prefer using cargo to do the build:

  1. Modify the Cargo.toml file and add the following
[package]
...
...
[lib]
crate-type = ["cdylib"]
[dependencies]
...
...

2. Remove the file src/main.rs otherwise this will not compile since we changed the crate-type to cdylib.

3. run cargo build --release

You should get an output like this:

➜  web git:(master) ✗ > cargo build --release
Compiling web v0.1.0 (file:///Users/atulr/Projects/Hobby/rust/projects/web)
Finished release [optimized] target(s) in 0.28 secs

4. You should find the libweb.dylib file at web/target/release/libweb.dylib

Note: Keep in mind this is a dylib file as compared to wasm file from part 1

We are now half way through the process !! 🌮

Use node-ffi (Node.js Foreign Function Interface) to use the functions exported from rust module.

node-ffi is a Node.js addon for loading and calling dynamic libraries using pure JavaScript. It can be used to create bindings to native libraries without writing any C++ code.

In short we can use node-ffi to call the public exported functions from the rust module.

To do this lets create a new node project.

  1. mkdir noderust && cd noderust— create a directory for our node project
  2. Now create the following file structure:
noderust
├── index.js
├── package.json
└── yarn.lock

3. yarn add ffi or npm install --save ffi — add the ffi dependency.

4. Your package.json should look something like this:

{
"name": "noderust",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"ffi": "^2.2.0"
}
}

5. Now add the following code to your index.js file.

const ffi = require('ffi');
const libPath = 'path/to/libweb.dylib';
const libWeb = ffi.Library(libPath, {
'add': [ 'int32', [ 'int32', 'int32' ] ],
'subtract': [ 'int32', [ 'int32', 'int32' ] ],
'multiply': [ 'int32', [ 'int32', 'int32' ] ]
});
const { add, subtract, multiply } = libWeb;
console.log('4 + 2 = ', add(4, 2));
console.log('4 - 2 = ', subtract(4, 2));
console.log('4 * 2 = ', multiply(4, 2));

Lets understand this code line by line.

const libPath = 'path/to/libweb.dylib';

This is the path to compiled dynamic library file of the rust code that we generated earlier.

const libWeb = ffi.Library(libPath, {
'add': [ 'int32', [ 'int32', 'int32' ] ],
'subtract': [ 'int32', [ 'int32', 'int32' ] ],
'multiply': [ 'int32', [ 'int32', 'int32' ] ]
});

Here the signature of the ff.Library function looks something like this:

ffi.Library(libraryFile, {
functionSymbol: [ returnType, [ arg1Type, arg2Type, ... ]],
...
...
});

Now since our add function in rust has the following signature

fn add(first: i32, second:i32) -> i32 

the corresponding format for add would be:

'add': [ 'int32', [ 'int32', 'int32' ] ],

Similarly, subtract and multiply have similar configurations.

You can check the complete list of types here: https://github.com/TooTallNate/ref/wiki/Known-%22types%22

That’s it, lets run it!

npm start

If everything went well, you should see:

➜  noderust git:(master) ✗ > yarn start
yarn run v1.5.1
$ node index.js
4 + 2 = 6
4 - 2 = 2
4 * 2 = 8
✨ Done in 0.20s.

Hurray !! We are now running rust module directly from NodeJS. 🎉🌮🧞‍♂️

PS: You can also use a very nice library neon https://github.com/neon-bindings/neon to achieve the same. But the purpose of the post was to show you a way to integrate rust with node (other than web-assembly way).

Hope this was fun ! 🌮🍺🎉

Source Code:

References: