Using Java libraries from a Node.js application. Testcontainers example.

Oleg Šelajev
graalvm
Published in
4 min readMar 26, 2019

--

GraalVM allows seamless and high-performance interoperability between a set of supported languages. Currently, this means JavaScript, including Node.js applications, Python, Ruby, R, JVM languages, and everything that compiles with LLVM.

A typical motivation for a polyglot runtime like GraalVM is to enhance an application written mostly in one language with small snippets in another language. For example, you could think of using R in a Java Spring application to visualize some data, or, perhaps, one could use Python’s machine learning libraries in a node app. Or you can use a runtime like GraalVM to add scripting capabilities to your platform, either on the JVM or a native application: kinda like the multilingual engine does in the Oracle Database.

But also every language ecosystem comes with libraries that are excellent and have worse alternatives in the other languages. Allowing to use these libraries (others too, but these are where the value comes from) from other languages means the library authors do not need to provide bindings in every language but can concentrate on whichever makes them the most productive.

For example, the Java ecosystem has a very neat library for using Docker containers programmatically: Testcontainers. Testcontainers is an excellent way to manage all your infrastructure dependencies during tests.

Testcontainers is a Java library, and it’s a perfect candidate to be used from other languages. The JVM languages have some sort of interop with Java so they can use it naturally, but other languages can’t. GraalVM can solve this problem.

Here’s how you use Testcontainers from a node.js application in a few straightforward steps.

When a Java application needs a library, you declare a dependency inbuild.gradle or pom.xml to allow the build tool to handle both obtaining the JAR file you depend on and all the necessary transitive dependencies. The same applies to a node.js application; you need a module, you add a dependency and npm handles the rest. However, npm doesn’t currently resolve Maven dependencies (at least I don’t know if it can do it), so we need to prepare a JAR file with all the necessary classes.

Clone the example Java application that uses Testcontainers and build an uberjar that contains Testcontainers and all its dependencies.

git clone git@github.com:kiview/testcontainers-prod-src-example.git
cd testcontainers-prod-src-example
./gradlew shadowJar
ls build/libs/testcontainers-prod-src-example-1.0-SNAPSHOT-all.jar

Now we need a runtime that is capable of running a node application and a Java application. GraalVM fulfils these requirements perfectly. Download it and ensure that both java and node commands work.

Now it’s time to create a Node.js app from which we’ll use the full power of Testcontainers!

Here’s myapp/index.js file that uses GraalVM polyglot API call Java.type() to access the Java class org.testcontainers.containers.GenericContainer.

It will return us the type for the class we asked for and afterwards we can use it as any normal JavaScript object.

We will create a new container with the nginx image, expose it on port80, start it, and print some information about the container, showing we can access it from the outside. We then just wait for some command line input before shutting everything down.

Using test-containers library from Node.js (on GraalVM)

If you have Docker running on your machine and run this command:

→ node --jvm --vm.cp=testcontainers-prod-src-example/build/libs/testcontainers-prod-src-example-1.0-SNAPSHOT-all.jar app/index.js

It will start a Docker container with nginx and you can access it locally. Magnificent, isn’t it?

Nginx output from a docker container started by TestContainers from a Node.js app

Now if your Node application depends on any infrastructure, like a data store or some other service, you can easily spin it up during tests in a docker container. There are a bunch of specialized containers prepared by the Testcontainers team: databases, Selenium, Kafka, Neo4j, etc. And you can always programmatically define custom ones.

While we’re at it, you can do the same trick and use the same library from, for example Python. Start the Python:

→ graalpython --jvm --vm.cp=testcontainers-prod-src-example/build/libs/testcontainers-prod-src-example-1.0-SNAPSHOT-all.jar

Run the following snippet which is a very close translation of the JavaScript code above:

Using test-containers library from Python (on GraalVM)

And you have the nginx container up and running.

Naturally, you can find more appropriate use cases for the test-containers library, for example, actually managing the environment for your integration tests, but in this blog post we just wanted to highlight that you can use it from all languages supported on GraalVM very easily.

Okay, I feel like I can do one more without losing your attention. Here’s Ruby!

Using test-containers library from Ruby (on GraalVM)

In this short post, we looked at how GraalVM polyglot capabilities allow you to use Java libraries, on the example of Testcontainers, from a Node application. Not only can GraalVM run both Java and node apps, it also supports other languages like Ruby, Python and R and allows you to mix the libraries from all their ecosystems in a very efficient way.

Download GraalVM and give it a try: https://www.graalvm.org/downloads/. If you have any questions or ideas about which libraries could be useful in other languages start a conversation below or find us on Twitter: @graalvm.

--

--