In this post I will show how to create a NodeJS add-on using C++ and Bazel.

Bazel

One of my favorite things about Bazel is that it is a language agnostic build system. This is key for mono repos with code written in multiple languages. Being able to build the entire stack using a single toolchain is a great benefit you get from using Bazel.

In today’s demo I will combine a function written in C++ with a nodeJS application written in Typescript.

In don’t often get to work with C++ these days. It was basically the language that introduced me to real programming, but since most of my work is web based, C++ is usually not an ideal fit.

Some people may still use C++ for web applications these days, but it’s far from the norm.

NodeJS add-on development might be a semi common use case where C++ and JavaScript meet though.

C++

Bazel comes with rules for building C++ out of the box. In my demo application I am using the cc_library rule to generate a C++ library.

In the following code listing I will show how to configure the cc_library to build a simple add function written in C++.

The code is trivial, but the key point here is to show the integration via a single build tool (Bazel).

Bazel Rule

cc_library( name = "add", srcs = ["add.cpp"], hdrs = ["add.h"] )

add.cpp

#include "add.h" int add(int x, int y) { return x + y; }

add.h

extern "C" int add(int x, int y);

Now, calling “bazel build add” is all you need to compile the add function to an external library.

NodeJS

On the nodeJS side we have to link the C++ library as an external module. In this example I am using a library called ffi to help with this. You can install ffi from npm.

The following code shows how to wire it all up in NodeJS (Typescript):

const ffi = require('ffi'); const ref = require('ref'); export class AddService { private _add; constructor(libPath: string) { const int = ref.types.int; const addPtr = ffi.DynamicLibrary(libPath).get('add'); this._add = ffi.ForeignFunction(addPtr, int, [int, int]); } add(num1: number, num2: number) { return this._add(num1, num2); } }

I am using a combination of DynamicLibrary and ForeignFunction to link in the C++ add function. The libPath refers to the location of the .so file generated by the C++ compiler.

Now we can call the add method just like any other JavaScript function.

I have linked the Bazel rule responsible for building the C++ code to the rule used to run the Node application. This means the NodeJS application will be refreshed every time I make a change to the C++ method.

Demo

You can find the full source code in my demo repo.

This demo application takes the concept of multi language builds even further. The repo is now made up of code written in Typescript, Java and C++, all working in concert with a single build tool; Bazel.