Helidon and Neo4j

Dmitry Aleksandrov
Helidon
Published in
6 min readMay 28, 2021

The idea to experiment with integrating Neo4j with Helidon came quite naturally.

Neo4j is a graph database management system developed by Neo4j, Inc. It is an ACID-compliant transactional database with native graph storage and processing. Neo4j is available in a GPL3-licensed open-source “community edition”. (Wikipedia, Retrieved 2021–02–25)

Neo4j is implemented in Java and accessible from software written in other languages using the Cypher query language through a transactional HTTP endpoint, or through the binary “bolt” protocol.

Neo4j is now the de facto standard for graph databases used by many industries.

It all started with a small chat with Michael Simons, one of the authors of Spring Data Neo4j 6 and the maintainer of Neo4j-OGM. We asked Michael his thoughts on how Helidon and Neo4J could work together. In less than an hour, Michael sent me a link to this repository with a fully functional example for Helidon MP and Neo4j SDN.

As I started reading the code, I saw a strange dependency in the pom.xml file:

Spring? Really? Why on earth do we have Spring in our project?

But as I continued exploring the code, I was quite surprised to see a CDI 2.0 extension, handling the driver initialization and preparation.

This CDI extension started seamlessly in Helidon, as it is a fully supported CDI 2.0 container. Remarkable! I once again felt the beauty and the power of standards.

The next day Michael sent me another repository. This time he included his ideas on how to use Neo4j with Helidon SE.

The integration worked quite seamlessly. It was easy to use Helidon Config to externalize all the configuration for the Neo4j in a standard place. And since Neo4j driver fully supports native images, the example can be compiled into a native executable without any additional actions from the programmer’s perspective.

But this was not enough! Neo4j driver provides a reactive API and there was no way to miss this opportunity! The third example repository was on its way.

This triggered discussions between Helidon and Neo4j teams about the kind of integration that is more suitable to be done with Helidon, and as a result, we created an official integration!

After some consultations with a Neo4j representative and my good friend Michael Simons, we all came to the conclusion that we only need to expose the configured driver to Helidon users. Metrics and Health checks from Neo4J should be translated to Helidon/MicroProfile Metrics and Health checks and should be provided as separate modules — separate Maven dependencies.

So, how should we write integration within Helidon?

There are two flavors of Helidon — MP and SE. SE is actually a set of Reactive APIs implemented with pure Java. There is absolutely no “magic”, like reflection or other tricks. Helidon MP essentially wraps SE and adds some “magic” like CDI. This means that it is a good idea first to implement the integration with Neo4J for Helidon SE and then wrap it like CDI extensions for Helidon MP.

Let us do it!

Disclaimer: In this article I will demonstrate only the key code snippets. Since Helidon is an Apache 2.0 licensed Open Source, the full code is available in the official Helidon/Neo4j repository.

In Helidon we usually create a so-called “support” object for integration. This object holds all of the configuration and initialization information.

We follow the “Builder pattern” to read from configuration. This means that we create an inner Builder object which should read all the data from configuration:

You may see the strings with keys, they are actually taken from the configuration file. Either from SE config, or from MicroProfile config. Every item is configured following the Builder pattern:

As all the fields are set, we can build the support class:

This way we guarantee that all values will be not null. The actual driver construction is quite straightforward (some methods omitted):

Then the Neo4j support object just returns the driver:

Now we are able to consume it:

Helidon will take care of creating and configuring it from the application.yaml file:

And just use it:

And voilà! Helidon and Neo4j are now able to work together.

But what about MP? We just need to wrap it in a CDI extension — it really is that straightforward:

As you can see, we are taking the configuration from SE:

Then just reusing our Neo4j SE support object:

… and return the driver:

And we are able to consume it in a pure MicroProfile CDI way:

The configuration will be read from `microprofile-config.properties` file:

And then you can use it just like we did in the SE example.

Now both Helidon SE and Helidon MP are able to work with Neo4j!

But that’s not all!

Metrics

As I’ve previously mentioned, for proper cloud native experience, we need to provide metrics and health checks for Neo4j as well.

Let us do it the same way — first for SE, and then wrap it for MP. Let’s start with metrics.

We will do a separate module for Neo4j metrics.

Like in Helidon SE, the Neo4j support class we will follow the Builder pattern to configure the Metrics support. Actually we only need the Neo4j driver, as we can get all the metrics out of it:

Then we should wrap the Counter and Gauges with just the Suppliers of the information from Neo4j:

We then need a way to register these counters in metrics to SE MetricsRegistry:

And finally, we can register the metrics:

Its always a good idea to refresh metrics, and for that we have a function:

We only need to enable the Neo4j driver to deliver metrics information for us in our our application.yaml file:

… or in our microprofile-config.properties:

neo4j.pool.metricsEnabled=true

Now if you launch the app query `/health`, you will get Neo4j readings as well.

Works perfect for SE, we only need to wrap the Metrics registration as a CDI extension. The event should happen after the driver is already initialized:

And that’s it! Neo4j metrics are now available in Helidon MP as well!

And now, last but not the least, Health checks!

Health Checks

And again, this should be a separate module to keep it pure.

Here we are going to start with the MP version:

Technically, for health check we are doing a simple Cypher request, and if it works, it means Neo4j is alive. This code is enough since it is a pure MicroProfile code.

We only need to include a Maven dependency

And it will work out of the box!

As for SE, since it is a pure java, we just need to initialize it:

And that is it! Now our application, both MP and SE can use Neo4j, read its Metrics, and do the Health checks. By the way, since Neo4j driver is fully GraalVM native-image ready, our Helidon MP or SE apps can take advantage of it and be compiled ahead-of-time to Native image! YAY!

In this article it was demonstrated how to create an integration with Helidon. But as for Neo4j, we have already officially done this for you!

You just need to include the following in your dependencies:

… and this is all you need to start you work with Helidon and Neo4j!

Conclusion

As you can see, integration with Helidon is quite simple. The usual way of doing it is to first write a Helidon SE support object following the Builder pattern for initialization, and then just wrap it with a CDI extension, to bring on its magic to MicroProfile!

You can play with Neo4j examples in our official Helidon Neo4j integrations repository.

Official video with Neo4j features demonstration is available on our official YouTube channel:

Speaking about Neo4j, you may also be interested in CypherDsl and an example with it.

Next Steps

With this article I wanted to show you not only how Neo4j works with Helidon, but also how you can write your own extensions! Since Helidon is open source, we invite you to contribute your integrations.

--

--