layout | toc_group | link_title | permalink |
---|---|---|---|
docs |
js |
Node.js Runtime |
/reference-manual/js/NodeJS/ |
GraalVM can run unmodified Node.js applications. GraalVM's Node.js runtime is based on a recent version of Node.js, and runs the GraalVM JavaScript engine (GraalJS) instead of Google V8. Some internal features (for example, VM-internal statistics, configuration, profiling, debugging, and so on) are unsupported, or supported with potentially different behavior.
Applications can freely import and use NPM packages, including native ones.
As of GraalVM for JDK 21, the GraalVM Node.js runtime is available as a separate distribution.
Two standalone runtime options are available for both Oracle GraalVM and GraalVM Community Edition: a Native Image compiled launcher or a JVM-based runtime.
To distinguish between them, the GraalVM Community Edition version has the suffix -community
in the name: graaljs-community-<version>-<os>-<arch>.tar.gz
, graalnodejs-community-<version>-<os>-<arch>.tar.gz
.
A standalone that comes with a JVM has a -jvm
suffix in a name.
To enable the GraalVM Node.js runtime, install the Node.js distribution based on Oracle GraalVM or GraalVM Community Edition for your operating system.
-
Navigate to GitHub releases and select a desired standalone for your operating system.
-
Unzip the archive:
tar -xzf <archive>.tar.gz
Alternatively, open the file in the Finder.
-
Check the version to see if the runtime is active:
./path/to/bin/node --version
The Node.js installation provides node
and npm
launchers:
node [options] [filename] [args]
The npm
command is equivalent to the default Node.js command, and features additional GraalVM-specific functionalities (for example, interoperability with Java).
A list of available options can be obtained with node --help
.
Use the node
launcher to execute a Node.js application. For example:
-
Install the
colors
andansispan
packages usingnpm install
as follows:npm install colors ansispan
After the packages are installed, you can use them from your application.
-
Add the following code snippet to a file named app.js and save it in the same directory where you installed the Node.js packages:
const http = require("http"); const span = require("ansispan"); require("colors"); http.createServer(function (request, response) { response.writeHead(200, {"Content-Type": "text/html"}); response.end(span("Hello Node.js!".green)); }).listen(8000, function() { console.log("Node.js server running at http://127.0.0.1:8000/".red); }); setTimeout(function() { console.log("DONE!"); process.exit(); }, 2000);
-
Execute it on the GraalVM Node.js runtime using the
node
command as follows:node app.js
The Node.js functionality is available when an application is started from the node
binary launcher.
Certain limits apply when launching a Node.js application or accessing NPM packages from a Java context, see Node.js vs. Java Script Context.
To install a Node.js package, use the npm
launcher.
The npm
command is equivalent to the default NPM command, and supports most of its options.
An NPM package can be installed with:
npm install [package]
As the npm
command of GraalVM Node.js is largely compatible with NPM, packages are installed in the node_modules/ directory, as expected.
Node packages can be installed globally using npm
and the -g
option.
By default, npm
installs global packages (links to their executables) in the path where the node
executable is installed, typically node/bin/.
That directory is where global packages are installed.
You might want to add that directory to your $PATH
if you regularly use globally installed packages, especially their command line interfaces.
Another option is to specify the global installation directory of npm
by setting the $PREFIX
environment variable, or by specifying the --prefix
option when running npm install
.
For example, the following command will install global packages in the /foo/bar/ directory:
npm install --prefix /foo/bar -g <package>
More details about prefix
can be found in the official NPM documentation.
The Node.js runtime cannot be embedded into a JVM but has to be started as a separate process.
-
Save the following code in a file named HelloPolyglot.java and compile:
import org.graalvm.polyglot.*; import org.graalvm.polyglot.proxy.*; public class HelloPolyglot { static String JS_CODE = "(function myFun(param){console.log('hello '+param);})"; public static void main(String[] args) { System.out.println("Hello Java!"); try (Context context = Context.create()) { Value value = context.eval("js", JS_CODE); value.execute(args[0]); } } }
-
Then save this code a file named app.js:
var HelloPolyglot = Java.type("HelloPolyglot"); HelloPolyglot.main(["from node.js"]); console.log("done");
-
Run it with
node
:node --vm.cp=. app.js
You should see the following output:
Hello Java! hello from node.js done
Both Node.js and JVM then run in the same process and the interoperability works using the same Value
classes as above.
For the differences between running the node
launcher and accessing Node.js NPM modules or ECMAScript modules from a Java Context
, see NodeJSVSJavaScriptContext.
The basic multithreading model of GraalJS applies to Node.js applications as well.
In Node.js, a Worker thread can be created to execute JavaScript code in parallel, but JavaScript objects cannot be shared between Workers.
On the contrary, a Java object created with GraalVM Java interoperability (for example, using Java.type()
) can be shared between Node.js Workers.
This allows multithreaded Node.js applications to share Java objects.
The GraalVM Node.js unit tests contain several examples of multithreaded Node.js applications. The most notable examples show how:
- Node.js worker threads can execute Java code.
- Java objects can be shared between Node.js worker threads.
- JavaScript
Promise
objects can be used toawait
on messages from workers, using Java objects to bind promises to worker messages.
GraalVM's Node.js runtime is largely compatible with the original Node.js (based on the V8 engine).
This leads to a high number of npm
-based modules being compatible.
In fact, out of the 100k npm
modules we test, more than 94% of them pass all tests.
Still, several sources of differences have to be considered:
-
Setup: GraalVM's Node.js mostly mimicks the original setup of Node, including the
node
executable,npm
, and similar. However, not all command-line options are supported (or behave exactly identically). Modules might require that native modules are (re)compiled against the v8.h file.As of GraalVM for JDK 21, the GraalVM Node.js runtime is available as a separate distribution. See Getting Started with Node.js.
-
Internals: GraalVM's Node.js is implemented on top of a JVM, and thus has a different internal architecture than Node.js based on V8. This implies that some internal mechanisms behave differently and cannot exactly replicate V8 behavior. This will hardly ever affect user code, but might affect modules implemented natively, depending on V8 internals.
-
Performance: Due to GraalVM's Node.js being implemented on top of a JVM, performance characteristics vary from the original native implementation. While GraalVM's peak performance can match V8 on many benchmarks, it will typically take longer to reach the peak (known as warmup). Be sure to give the Graal compiler some extra time when measuring (peak) performance.
-
Compatibility: GraalVM's Node.js runtime uses the following approaches to check and retain compatibility with Node.js code:
- node-compat-table: GraalVM's Node.js is compared against other engines using the node-compat-table module, highlighting incompatibilities that might break Node.js code.
- automated mass-testing of modules using mocha: in order to test a large set of modules, GraalVM's Node.js runtime is tested against 95k modules that use the mocha test framework. Using mocha allows automating the process of executing the test and comprehending the test result.
- manual testing of popular modules: a select list of
npm
modules is tested in a manual test setup. These highly-relevant modules are tested in a more sophisticated manner.
Node packages can be installed globally using npm
and the -g
option, both with the GraalVM's Node.js implementation.
While the original Node.js implementation has one main directory (node/bin/) to put binaries and globally installed packages and their command-line tools, GraalVM's Node.js puts binaries in the /path/to/graaljs/bin/ directory.
When installing NPM packages globally on the GraalVM Node.js runtime, links to the executables, for example, for command line interface tools are put to the JavaScript-specific directory.
In order for globally installed packages to function properly, you might need to add /path/to/graaljs/bin
to your $PATH
.
Another option is to specify the global installation directory of npm
by setting the $PREFIX
environment variable, or by specifying the --prefix
option when running npm install
.
For more details, see Installing npm
Packages Globally.