This article is part of the Java Week series:
- The Journey of Nx Gradle
- Polyglot Projects Made Easy
- Getting Mobile Into Your Monorepo
Behind every modern JavaScript app lies a powerful backend, and for many enterprises, that backend is powered by Java. At Nx, we believe Java is just as much a first-class citizen as JavaScript. While Nx has a proven history of streamlining JavaScript development, we also offer seamless integration for Java projects. With the @nx/gradle
plugin, you can bring your Java services built with Gradle into your Nx monorepo, creating a unified environment where both frontend and backend thrive together. Let’s dive into how Nx bridges these worlds effortlessly.
Spring into Java
For starters, let's look at our Java backend. It's just a simple app built using the Spring Boot framework. With this, we can set up various rest endpoints for interacting with some data. There's some endpoints for user management, authentication, and session storage for logged in users. For Java developers, running and testing this app is as simple as pressing the "play" button in IntelliJ.
Spring has a project generator that can help get you up and running, and you can check out this project on GitHub if you want to follow along.
Now if I want to interact with this backend, I would need to create a frontend app that I can make request from. But in a real world situation, the frontend and backend are probably maintained by different teams. This is the typical problem space that Nx works in, so we can reduce and simplify the development of this app.
You got your Java In my JavaScript
Our frontend project is just your standard React app. It already exists in a Nx workspace, so I have all of my usual Nx feature available to me. It interacts with the backend by making requests to it, but I need to start the two projects separately, which can just be cumbersome to manage.
To actually make these two projects work together, we can utilize the nx import
command to start migrating the backend into the existing workspace. import
can take a relative path or a git URL, and it will handle all of the necessary work of maintaining the git history, detecting the project type, and suggesting additional plugins you might want to include.
For our Java project, it's going to suggest that we include the @nx/gradle
plugin as we are using Gradle as our build system. Once installed, we can run nx graph
or nx show project java-backend
to inspect.
Here we can see all of the available tasks from Gradle that we could run for our Java project. But how does Nx know about these tasks? We actually include a Gradle plugin for Nx that we use under the hood to inspect the Java project. When we call any nx
command for our java project, we actually invoke Gradle in the background to get all of tasks that Gradle could run directly.
1plugins {
2 id 'java'
3 id 'org.springframework.boot' version '3.4.5'
4 id 'io.spring.dependency-management' version '1.1.7'
5 // Added by Nx
6 id "dev.nx.gradle.project-graph" version "0.1.0"
7}
8
9java {...}
10configurations {...}
11repositories {...}
12dependencies {...}
13
14allprojects {
15 apply {
16 plugin("dev.nx.gradle.project-graph")
17 }
18}
19
This project-graph is added to our build.gradle
file and is doing the real heavy lifting for us.
From here, we can start running the Java project
1nx run java-backend:bootRun
2
And our API is up and running:
Connecting Two Tasks
With the project imported, we can now coordinate the frontend and backend processes so we can easily run them together. This is actually pretty easy to do thanks to dependsOn
. In the package.json
for our React app, let's configure the dev
task and make it start the Java backend.
1{
2 "name": "@mono-app/react-frontend",
3 "version": "0.0.1",
4 "private": true,
5 "nx": {
6 "targets": {
7 "dev": {
8 "dependsOn": ["java-backend:bootRun"]
9 }
10 }
11 }
12}
13
Now we can run react-frontend:dev
and we'll automatically get our Java backend up and running
But we can take this one step further. Let's consider that we have a bug in our Java backend. A bad response from an endpoint, incorrect structure, or something similar. Since our code is all in one place, we could fix this easily, but to get the changes applied, we'll need to stop and restart our server. That's not ideal, and is super slow. So we can expand on that dependsOn
approach from earlier within our Java backend project.
If we create a simple project.json
in the java-backend directory, we'll be able to customize the bootRun
task. For starters, we want bootRun
to depend on the build
task we have:
1{
2 "targets": {
3 "bootRun": {
4 "dependsOn": ["build"]
5 }
6 }
7}
8
Now for the build task, we want to pass in an argument to our underlying Gradle command:
1"build": {
2 "continuous": true,
3 "options": {
4 "args": ["--continuous"]
5 }
6}
7
So we have the options.args
passing in a --continuous
flag and setting "continuous": true
. Now, these look similar, but are doing two different things. The "continuous": true
is telling Nx that this task is long-lived and will not end. The options.args
part is getting passed to Gradle directly and telling Gradle to watch for file changes.
For demo purposes, we're going to target the build task directly, but for more production ready example, create your own task so you do not impact any CI runs.
Now if we change one of our Java files, Gradle will rebuild our project, and the bootRun
will refresh with the new changes applied.
A Polyglot Future
With this, we have the beginnings of a great polyglot experience for teams that have a mix of languages. We can start to incorporate more Java related projects into Nx and bring all of the features that Nx has to offer. Be sure to stop by tomorrow to see what else we have in store for Java developers.
Learn more: