# A minimal Java service for Node.js developers
You have a running Node.js Moleculer system and you want to add one action in Java — nothing
more. This page is that path end to end: a single class, one action, a NATS transporter, started from
a plain main(). No Spring, no Netty, no Jakarta EE, no WAR. When you later want the web gateway
or Spring, those are add-ons — they do not change the service you write here.
The service class is identical with or without Spring — only how you create and start the broker differs. Everything in the Core reference applies to this service as-is.
# 1. Add the dependencies (Maven)
<!-- pom.xml -->
<dependencies>
<!-- Moleculer core -->
<dependency>
<groupId>com.github.berkesa</groupId>
<artifactId>moleculer-java</artifactId>
<version>2.0.0</version>
</dependency>
<!-- NATS transporter client (optional inside moleculer-java, so declare it) -->
<dependency>
<groupId>io.nats</groupId>
<artifactId>jnats</artifactId>
<version>2.21.1</version>
</dependency>
<!-- An SLF4J binding so logs go somewhere (see the Logging page) -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>2.0.18</version>
</dependency>
</dependencies>
# 2. Write the service
One class, one action. The action is a public instance field (a lambda) — that is how the
broker discovers it, and public is what publishes it to the cluster so your Node.js node can call it.
import io.datatree.Tree;
import services.moleculer.service.Name;
import services.moleculer.service.Service;
import services.moleculer.service.Action;
@Name("mathJava")
public class MathJavaService extends Service {
public Action add = ctx ->
ctx.params.get("a", 0) + ctx.params.get("b", 0);
}
That ctx.params is a Tree — the Java stand-in for a JavaScript object.
# 3. Start a broker and register it
A plain main() builds the broker, points it at the shared NATS bus, registers the service and
starts. This is the whole Java process.
import io.datatree.Tree;
import services.moleculer.ServiceBroker;
import services.moleculer.config.ServiceBrokerConfig;
import services.moleculer.transporter.NatsTransporter;
import services.moleculer.serializer.JsonSerializer;
public class Main {
public static void main(String[] args) throws Exception {
ServiceBrokerConfig cfg = new ServiceBrokerConfig();
cfg.setNodeID("java-node"); // unique in the cluster
cfg.setProtocolVersion("5"); // matches Moleculer JS 0.15 (the default)
NatsTransporter nats = new NatsTransporter("nats://localhost:4222");
nats.setSerializer(new JsonSerializer()); // JSON is the default; set explicitly for clarity
cfg.setTransporter(nats);
ServiceBroker broker = new ServiceBroker(cfg);
broker.createService(new MathJavaService()); // register BEFORE start
broker.start();
}
}
That is the complete standalone node — no application server, no framework. (Need a NATS server?
docker run -p 4222:4222 nats.)
# 4. Call it from Node.js
From your existing Node.js system, mathJava.add is just another action — call it as if it were
local:
For the cluster-alignment details (transporter, serializer, protocol version, unique nodeID) see
Setup — one cluster; for a runnable hello in both directions see the
Quick start.
# Where to go next in the core reference
Everything below builds on the exact service above — the class never changes:
- Services — names, versions, lifecycle handlers, multiple actions.
- Actions — call options, metadata, validation, streaming, REST aliases.
- Events — subscribe to and emit events across the cluster.
- Caching, Fault tolerance, Load balancing — the cluster-side building blocks.
- Want HTTP/REST in front of it? Add the Web API Gateway. Want Spring DI? See Running under Spring — same service class, different startup.