addons
C++ Addons in Node.js
Addons are extra modules written in C++ that can extend the capabilities of Node.js. They allow you to connect Node.js with other C/C++ libraries.
How Addons Work
There are a few ways to create addons, but the most common is using Node-API. Node-API provides a set of functions that make it easy to write addons without having to deal directly with the underlying V8 and libuv libraries.
V8
V8 is the engine that powers Node.js and executes JavaScript code. It's like the brain that understands and runs the code you write in JavaScript.
libuv
libuv is a cross-platform library that provides Node.js with low-level functionality, such as creating threads, starting timers, and handling file operations. It's like the toolkit that helps Node.js connect to the operating system and manage system resources.
Internal Node.js Libraries
Node.js also includes its own set of libraries that addons can use. One important one is node::ObjectWrap
, which allows addons to create their own JavaScript objects.
Real-World Applications
Addons are used in a wide variety of applications, including:
Image processing: Addons can provide faster performance for image manipulation tasks.
Data analysis: Addons can be used to analyze large datasets quickly and efficiently.
Hardware integration: Addons can connect Node.js to hardware devices, such as microcontrollers or sensors.
Code Example
Here's a simple example of an addon that prints a message:
This addon defines a function that creates a JavaScript string object with the message "Hello, world!". It then exports the message object as a property of the module. When you load this addon in a Node.js script, you can access the message property to print the message.
Conclusion
Addons are a powerful way to extend the capabilities of Node.js and integrate with C/C++ libraries. They are used in a wide variety of real-world applications, from image processing to data analysis.
Node.js Addons
In Node.js, you can extend the functionality of JavaScript by creating and using addons. Addons are written in C++ and provide access to native system resources or perform complex calculations more efficiently.
Creating a Hello World Addon
Let's create a simple "hello world" addon:
Understanding the Code
Initialize
Function: This is the entry point of the addon. It exports the "hello" method to Node.js.Method
Function: This function handles the "hello" method. It returns "world" as a string.NODE_MODULE
Macro: Registers the addon with Node.js. TheNODE_GYP_MODULE_NAME
macro specifies the name of the final addon binary.
Building and Using the Addon
To build and use the addon:
Example.js:
Potential Applications
Addons are used in a wide variety of applications, including:
Interfacing with hardware devices
Performing computationally intensive tasks
Accessing low-level system information
Integrating with native libraries written in other languages
Context-aware addons
In certain environments, Node.js addons may need to be loaded multiple times in different contexts. For example, the Electron runtime runs multiple instances of Node.js in a single process. Each instance will have its own require()
cache, meaning each instance will need a native addon to behave correctly when loaded via require()
. This means that the addon must support multiple initializations.
Creating a context-aware addon
A context-aware addon can be created using the NODE_MODULE_INITIALIZER
macro, which expands to the name of a function that Node.js will expect to find when it loads an addon. An addon can be initialized as follows:
Another option is to use the NODE_MODULE_INIT()
macro, which will also construct a context-aware addon. Unlike NODE_MODULE()
, which is used to construct an addon around a given addon initializer function, NODE_MODULE_INIT()
serves as the declaration of such an initializer to be followed by a function body.
The following three variables may be used inside the function body following an invocation of NODE_MODULE_INIT()
:
Local<Object> exports
,Local<Value> module
, andLocal<Context> context
Managing global static data
Building a context-aware addon requires careful management of global static data. Since the addon may be loaded multiple times, potentially even from different threads, any global static data stored in the addon must be properly protected and must not contain any persistent references to JavaScript objects. This is because JavaScript objects are only valid in one context and will likely cause a crash when accessed from the wrong context or from a different thread than the one on which they were created.
Avoiding global static data
A context-aware addon can be structured to avoid global static data by performing the following steps:
Define a class which will hold per-addon-instance data and which has a static member of the form:
Heap-allocate an instance of this class in the addon initializer. This can be accomplished using the
new
keyword.Call
node::AddEnvironmentCleanupHook()
, passing it the above-created instance and a pointer toDeleteInstance()
. This will ensure the instance is deleted when the environment is torn down.Store the instance of the class in a
v8::External
, andPass the
v8::External
to all methods exposed to JavaScript by passing it tov8::FunctionTemplate::New()
orv8::Function::New()
which creates the native-backed JavaScript functions. The third parameter ofv8::FunctionTemplate::New()
orv8::Function::New()
accepts thev8::External
and makes it available in the native callback using thev8::FunctionCallbackInfo::Data()
method.
This will ensure that the per-addon-instance data reaches each binding that can be called from JavaScript. The per-addon-instance data must also be passed into any asynchronous callbacks the addon may create.
Example of a context-aware addon
The following example illustrates the implementation of a context-aware addon:
Real-world applications of context-aware addons
Context-aware addons can be used in any situation where multiple instances of Node.js are running in the same process and need to share data or functionality. For example, context-aware addons can be used in:
Electron apps, where multiple instances of Node.js are running in different renderer processes.
Web extensions, where multiple instances of Node.js are running in different browser tabs.
Microservices, where multiple instances of Node.js are running in different containers or on different servers.
Worker Support
To be used in different Node.js environments like the main thread and a worker thread, an add-on needs to be either:
A Node-API addon
Declared as context-aware using
NODE_MODULE_INIT()
To support worker threads, add-ons must clean up any assigned resources when a worker thread exists. This is possible using the AddEnvironmentCleanupHook()
function:
This function includes a hook that will operate before a given Node.js instance shuts down. If essential, these hooks can be eliminated before they are run using RemoveEnvironmentCleanupHook()
, which has the same arrangement. Callbacks are operated in the last-in first-out order.
If necessary, there are additional AddEnvironmentCleanupHook()
and RemoveEnvironmentCleanupHook()
overloads, where the cleanup hook requires a callback function. This can be applied for shutting down asynchronous sources, like any libuv handles enrolled by the addon.
The accompanying addon.cc
uses AddEnvironmentCleanupHook
:
Test in JavaScript by running:
Real-world Applications
Creating add-ons that can be used in various Node.js environments, such as main threads and worker threads.
Cleaning up resources allocated by add-ons when a worker thread exits to prevent resource leaks and crashes.
Building Node.js Addons
Step 1: Describe Build Configuration
Create a file called binding.gyp
in your project directory. This file describes how to build your addon.
This file tells the build tool (node-gyp
) about your addon's source files (hello.cc
).
Step 2: Generate Build Files
Use the node-gyp configure
command to create the actual build files (Makefile
on Unix, vcxproj
on Windows).
Step 3: Build the Addon
Use the node-gyp build
command to compile your addon into a addon.node
file.
Step 4: Use the Addon in Node.js
Load the compiled addon into your Node.js script using require()
.
The addon's functions can now be used as shown in the example below.
Using the bindings
Package
bindings
PackageThe bindings
package simplifies the process of loading compiled addons by handling the search for the correct addon file based on the platform and environment.
This version provides more flexibility and is more robust than the manual require()
approach.
Real-World Applications
Node.js addons can be used to extend the capabilities of Node.js by adding native code functionality. Some examples include:
Interfacing with hardware devices (e.g., sensors, actuators)
High-performance computations
Accessing operating system-specific features
Linking to Libraries Included with Node.js
Introduction:
Node.js uses libraries like V8 (JavaScript engine), libuv (event loop), and OpenSSL (encryption) that are built into Node.js. When creating add-ons (extensions) for Node.js, you need to link to these libraries so your add-ons can use their functionality.
Locating Headers Automatically:
node-gyp
is a tool that helps build add-ons for Node.js. When you use node-gyp
, it automatically finds the headers (like #include <v8.h>
) you need to include in your add-on.
Downloading Headers vs. Full Source:
When building your add-on, node-gyp
will download either the full source code for the specific version of Node.js you're using or just the headers.
Full Source: You can access all the dependencies of Node.js.
Headers Only: You can access only the symbols exported by Node.js.
Using the --nodedir
Flag:
You can specify the location of the Node.js source code using the --nodedir
flag when running node-gyp
. This gives your add-on access to the full source code.
Real-World Examples:
Add-ons can be used to extend Node.js's functionality in various ways:
Image Processing: Create add-ons that use image manipulation libraries.
Database Connectivity: Enhance Node.js with add-ons that connect to databases.
Networking Utilities: Develop add-ons that provide additional networking capabilities.
Code Implementation:
To link to V8 in your add-on, you would typically include this header:
Potential Applications:
Add-ons can be used in a wide range of applications, including:
Web Servers: Enhance performance or add custom features to web servers.
Data Analysis: Process large datasets or create custom analytics tools.
Machine Learning: Implement machine learning algorithms or integrate with machine learning libraries.
Internet of Things (IoT): Connect and interact with IoT devices from Node.js applications.
Loading Addons Using require()
What are Addons?
Addons are extra pieces of code that you can add to Node.js to do things that Node.js can't do on its own. For example, you could use an addon to control a robot or to work with a specific type of hardware.
How to Load Addons
To load an addon, you use the require()
function. The require()
function tells Node.js to find the addon file and load it into your program.
Filename Extension for Addons
The addon files have a special filename extension: .node
. This tells Node.js that the file contains an addon.
Omitting the .node
Extension
Usually, you can omit the .node
extension when using require()
to load an addon. Node.js will still find and load the addon.
Potential Application
One potential application of addons is to control hardware devices. For example, you could use an addon to control a robot or to turn on and off a light.
Complete Code Example
Plain English Explanation
Imagine you have a box of tools. The box of tools is like Node.js, and the tools inside the box are like addons. You can add different tools to the box to do different things.
To use a tool, you need to find it and take it out of the box. To do this, you use a function called require()
. The require()
function tells Node.js to go find the tool and bring it to you.
The tools in the box have a special name tag: .node
. This tells Node.js that the tool is an addon.
Usually, you don't have to remember the name tag when using require()
. Node.js will still find the tool even if you forget to include the name tag.
One thing you can do with these tools is control different gadgets. For example, you could use a tool to turn on a light or to move a robot.
What are Native Abstractions for Node.js (nan)?
In Node.js, you can use native code written in C++ to create faster and more efficient extensions to the JavaScript environment. However, the V8 engine that powers Node.js can change dramatically between versions.
To help ensure that your native extensions keep working even with V8 updates, the Native Abstractions for Node.js (nan) library provides a stable set of APIs that hide the details of the underlying V8 changes.
How to use nan
To use nan, you need to include the following header file in your C++ code:
Let's take a simple example of how to use nan to create a custom function that you can call from JavaScript:
This code defines a function named Add
that adds two numbers together. It then exports this function as a JavaScript function that you can call from Node.js code.
To use this extension in your Node.js code, you would do the following:
Real-world applications of nan
Nan is used in many popular Node.js extensions, including:
Benefits of using nan
Using nan provides several benefits:
Stability: Your native extensions will continue to work even when V8 changes.
Portability: You can write code that works across different platforms and versions of Node.js.
Simplicity: Nan provides a simple and easy-to-use API for creating native extensions.
What is Node.js?
Node.js is a JavaScript runtime environment that allows you to write server-side code using JavaScript. It's used to create web applications, web servers, and other types of applications.
What are Native Addons?
Native addons are modules written in a language other than JavaScript, such as C or C++, that can be used in Node.js applications. They allow you to access native features of the operating system that are not available through JavaScript.
What is Node-API?
Node-API is an Application Binary Interface (ABI) that provides a stable set of functions that can be used to create native addons. This means that native addons compiled for one version of Node.js will work on subsequent versions of Node.js without recompilation.
How to Use Node-API
To use Node-API, you need to include the node_api.h
header file in your native addon code. You can then use the functions provided by Node-API to access native features of the operating system.
Example:
The following code snippet shows how to create a simple native addon using Node-API:
This addon can be compiled and used in a Node.js application using the following steps:
Install the
node-gyp
package using the following command:Create a
package.json
file for your addon with the following contents:Create a
binding.gyp
file for your addon with the following contents:Build the addon using the following command:
Install the addon in your Node.js application using the following command:
You can now use the hello
addon in your Node.js application as follows:
Real-World Applications
Native addons can be used to extend the functionality of Node.js applications in many ways. Here are a few examples:
Accessing low-level operating system features, such as file I/O, network I/O, and hardware devices
Improving the performance of computationally intensive tasks
Integrating with legacy code written in other languages
Creating custom modules for specific applications
Conclusion
Node-API is a powerful tool that allows you to create native addons for Node.js applications. By using the stable ABI provided by Node-API, you can ensure that your addons will work on future versions of Node.js without recompilation.
Addon examples
Addons are a way to extend the functionality of Node.js by writing native code in C++ that can be called from JavaScript. This can be useful for tasks that are difficult or impossible to do in JavaScript, such as accessing the operating system or working with hardware.
Examples:
Example 1: Hello World
The following addon simply prints "Hello, World!" to the console:
To use this addon, you would first need to install it using npm:
Then, you can require the addon in your JavaScript code:
Example 2: Accessing the operating system
The following addon shows how to access the operating system using the child_process
module:
To use this addon, you would first need to install it using npm:
Then, you can require the addon in your JavaScript code:
Example 3: Working with hardware
The following addon shows how to work with hardware using the i2c-bus
module:
To use this addon, you would first need to install it using npm:
Then, you can require the addon in your JavaScript code:
Potential applications in real world
Addons can be used in a wide variety of real-world applications, such as:
Interfacing with hardware: Addons can be used to control hardware devices, such as sensors, actuators, and motors.
Accessing operating system features: Addons can be used to access features of the operating system that are not available through the standard Node.js API, such as low-level file I/O and networking.
Improving performance: Addons can be used to improve the performance of Node.js applications by performing computationally intensive tasks in native code.
Extending the functionality of Node.js: Addons can be used to extend the functionality of Node.js by implementing new features that are not available in the standard library.
Function Arguments in Node.js Addons
Addons are extensions that allow you to extend the functionality of Node.js using C/C++ code. When you call a function from an addon in JavaScript, you need to pass arguments and receive the result.
Passing Arguments
When you call a function in an addon from JavaScript, the arguments are passed as an array of Value
objects:
Each element in the args
array represents a value that was passed from JavaScript. You can check the type of a value using IsNumber()
, IsString()
, etc.
Returning a Result
To return a result from a function in an addon, you set the ReturnValue
property of the FunctionCallbackInfo
object:
Example
Here's a complete example of an addon that defines a function called add
that takes two numbers and returns their sum:
You can use this addon from JavaScript like this:
Real-World Applications
Addons can be used to extend Node.js in a variety of ways, including:
Performance: C/C++ code can be significantly faster than JavaScript code, so addons can be used to improve the performance of computationally intensive tasks.
Native APIs: Addons can access native APIs that are not available to JavaScript code, such as operating system calls and device drivers.
Hardware acceleration: Addons can be used to accelerate hardware-intensive operations, such as graphics rendering and video encoding.
Callbacks in Node.js Addons
What are callbacks?
Callbacks are functions that are passed to other functions and then executed by those functions at some later point in time. This allows us to break down large tasks into smaller units and execute them asynchronously.
How to use callbacks in Node.js addons?
In Node.js addons, you can pass JavaScript functions to C++ functions and execute them from there. Here's an example:
How to use the addon?
Real-world applications:
Callbacks are used in a variety of real-world applications, such as:
Asynchronous I/O operations (e.g., reading from a file)
Event handling (e.g., responding to user input)
Timeouts and intervals (e.g., setting a timer to execute a function after a certain delay)
Object Factory in Node.js Addons
What is an Object Factory?
In Node.js, it's a feature that allows you to create and return new JavaScript objects from within a C++ function.
How It Works:
Initialize the Object: Create a new JavaScript object using the
Object::New()
function.Set Properties: Add properties to the object using the
Set()
function.Return the Object: Pass the newly created object as the return value of your C++ function.
Code Example:
JavaScript Example:
Real-World Applications:
Creating custom data formats
Generating complex objects with interdependent properties
Building modular applications where objects can be easily created and exchanged
Advantages:
Enables the creation of complex objects in C++ that can be used in JavaScript.
Improves performance by avoiding the overhead of converting objects between JavaScript and C++.
Enhances code organization and modularity.
Function factory
Concept:
A function factory is a way to create JavaScript functions that wrap C++ functions and return them to JavaScript.
Simplified Explanation:
Imagine you have a factory that produces cars. In the same way, a function factory produces JavaScript functions. The C++ functions are like the car parts, and the JavaScript functions are like the finished cars.
Code Snippet:
JavaScript Example:
Real-World Applications:
Creating custom JavaScript functions for use in Node.js applications.
Wrapping C++ libraries and exposing them to JavaScript.
Building reusable JavaScript components that can be easily integrated into different applications.
Wrapping C++ Objects
In Node.js, you can create C++ objects and expose them to JavaScript code. This allows you to extend Node.js with custom functionality written in C++.
Basic Example
Consider a C++ class called MyObject
that you want to expose to JavaScript:
To wrap this class in a way that allows JavaScript code to create new instances using the new
operator, you can inherit from node::ObjectWrap
:
Constructor Function
The New
function is the constructor for the JavaScript class. It creates a new MyObject
instance and wraps it in a JavaScript object:
Exposing a Method
The PlusOne
function increments the value stored in the MyObject
instance and returns the new value:
JavaScript Usage
In JavaScript, you can create instances of MyObject
and call its methods like this:
Real-World Applications
Wrapping C++ objects can be useful for building custom modules that extend Node.js with:
High-performance numerical calculations
Database connectivity
Image processing
Machine learning models
Factory of wrapped objects
Explanation:
Instead of using the new
keyword to create JavaScript objects, you can use a factory pattern. This pattern involves creating a function that returns a new object instance without using new
.
How it works:
In C++, you define a
createObject()
function in your addon (e.g.,addon.cc
). This function does not usenew
to create objects.In JavaScript, you call the
createObject()
function to get a new object instance. This is similar to calling a constructor, but without thenew
keyword.
Benefits:
Avoids explicit use of
new
.Simplifies object creation and makes it more consistent with the JavaScript way of creating objects.
Code snippet:
Real-world application:
This pattern is useful in situations where you want to create objects in a consistent manner, without exposing the complexity of using the new
keyword. For example, you could use this pattern to create objects that represent database models or service objects.
Improved versions or examples
Here's an improved version of the example above that uses a class method for object creation:
This version is more idiomatic JavaScript and allows you to use the new
keyword to create objects.
Potential applications
Creating database models
Implementing service objects
Encapsulating complex object creation logic
Providing a consistent interface for object creation
Passing Wrapped Objects Around
When working with Node.js addons, it's possible to pass wrapped C++ objects around within your Node.js code. This is useful when you need to work with the same object in multiple functions or modules.
To pass a wrapped object around, you first need to unwrap it using the node::ObjectWrap::Unwrap()
helper function. This function takes a JavaScript object as input and returns a pointer to the corresponding C++ object.
Once you have unwrapped the object, you can pass it to other functions or modules as needed. When you're done with the object, be sure to wrap it again using the ObjectWrap::Wrap()
function. This will reattach the JavaScript object to the C++ object and allow you to continue working with it in Node.js code.
Here's an example of how to pass a wrapped object around in Node.js:
Real-World Applications
Passing wrapped objects around can be useful in a variety of real-world applications, such as:
Caching: You can wrap objects in Node.js code and then pass them to C++ code for caching. This can improve the performance of your application by avoiding the need to repeatedly create and destroy objects.
Data transfer: You can wrap objects in Node.js code and then pass them to C++ code for processing or storage. This can be useful for tasks such as data analysis or image processing.
Interfacing with native code: You can wrap objects in Node.js code and then pass them to C++ code for interfacing with native libraries. This can be useful for tasks such as accessing hardware devices or running external programs.