Combining the React profile projects
Learn how to generate a JAR file which combines React and Grails production artefacts.
Authors: Zachary Klein
Grails Version: 3.3.0
1 Grails Training
Grails Training - Developed and delivered by the folks who created and actively maintain the Grails framework!.
2 Getting Started
In this guide, you are going to create a combined build of the server
and
client
projects from the React profile. By default, the React profile sets up
separate frontend and backend apps, which can make for a more efficient development process.
However, this may not fit your deployment needs, particularly if your entire app is
being deployed to a servlet container such as Tomcat, or being executed as a "fat" JAR.
Thanks to the flexibility of Grails and the Gradle build tool, we can configure a
unified build of our Grails/React app with only a few changes.
2.1 What you will need
To complete this guide, you will need the following:
-
Some time on your hands
-
A decent text editor or IDE
-
JDK 1.7 or greater installed with
JAVA_HOME
configured appropriately
2.2 How to complete the guide
To get started do the following:
-
Download and unzip the source
or
-
Clone the Git repository:
git clone https://github.com/grails-guides/react-combined.git
The Grails guides repositories contain two folders:
-
initial
Initial project. Often a simple Grails app with some additional code to give you a head-start. -
complete
A completed example. It is the result of working through the steps presented by the guide and applying those changes to theinitial
folder.
To complete the guide, go to the initial
folder
-
cd
intograils-guides/react-combined/initial
and follow the instructions in the next sections.
You can go right to the completed example if you cd into grails-guides/react-combined/complete
|
3 Writing the Application
If you open the initial
project of this guide, you will find a plain project
generated from the React profile.
We did one minor change. We moved the initial/server/gradle.properties
to
the root directory initial/gradle.properties
Both the server
Grails app and the client
React app have production
build tasks set up - you can run ./gradlew server:assemble
to build a WAR or
JAR file, and you can run yarn build
(or ./gradlew client:build
) to generate
a static React/webpack bundle.
Our first step to create a new build.gradle
file at the top of our project
structure, above the client
and server
sub-projects.
3.1 Configuring Gradle
Create a new build.gradle
file (at the same level as settings.gradle
), and add the following content:
task copyClientResources(dependsOn: ':client:build') { (1)
group = 'build'
description = 'Copy client resources into server'
doLast {
copy {
from project(':client').buildDir.absolutePath
into "${project(':server').buildDir}/resources/main/public"
}
}
}
task assembleServerAndClient(dependsOn: ['copyClientResources', ':server:assemble']) { (2)
group = 'build'
description = 'Build combined server & client JAR'
doLast {
copy {
from fileTree(dir: "${project(':server').buildDir}/libs/") (4)
into "$rootDir/build/"
}
logger.quiet "JAR generated at $rootDir/build/. It combines the server and client projects."
}
}
task(":server:assemble").mustRunAfter(copyClientResources) (3)
1 | The copyClientResources task will copy the static files generated by yarn build into the resources directory of the server project. |
2 | The assembleServerAndClient task ties the copy step to the existing Gradle assemble task for the server project. This means that we will get our unified archive only when running this top-level task (running server:assemble ) alone will generate a "plain" WAR/JAR file without the client resources. |
3 | If copyClientResources is to be executed, it must be before :server:assemble . This ensures client static files will be available when the Grails app is assembled. |
4 | After the combined JAR file has been created, we copy the files to the top-level build directory. |
In this guide, we will be using an executable JAR file as our deployment target.
To enable this, edit the server
project’s build.gradle
file, and delete the following line:
apply plugin:"war"
3.2 Updating our config
Now that we have a unified Gradle build target, we have a couple more configuration changes to make. All of these changes have to do with the URL differences between the separate Grails & React apps and our new combined deployment.
By default, static resources in a deployed Grails app are served from the /static
base URL. That will conflict with what our React app expects, but fortunately, this is a very simple change.
Edit server/grails-app/conf/application.yml
:
grails:
resources:
pattern: /**
This simply sets the static resources URL to be set to the root context.
Along similar lines, we need to edit the URL that the React app uses to make REST calls against the Grails app. This can be done within src/config.js
in the client
project.
Edit client/src/config.js
:
import pjson from './../package.json';
const prod = process.env.NODE_ENV === 'production'; (1)
console.log(`Loading ${process.env.NODE_ENV} config...`);
export const SERVER_URL = prod ? '' : 'http://localhost:8080';
export const CLIENT_VERSION = pjson.version;
export const REACT_VERSION = pjson.dependencies.react;
1 | The process.env.NODE_ENV is a NodeJS environment variable that will tell us if we are running in production or development mode. We’re using a simple ternary operator to switch the SERVER_URL config property to point to our standalone Grails app during development, and to use the root context in production. |
3.3 URL Mappings
Our final change needs to happen in UrlMappings.groovy
. If you recall, by default the server
Grails app renders some application metadata at the root context. However, in order for our combined build to work, we need to load our React app at the root context. Fortunately, this is a very simple change as well.
Edit server/grails-app/controllers/demo/UrlMappings.groovy
:
if ( Environment.current == Environment.PRODUCTION ) { (1)
'/'(uri: '/index.html')
} else {
'/'(controller: 'application', action:'index')
}
1 | We are using the grails.util.Environment class to determine whether we are running in development mode or in a deployed artefact, and setting the URL mappings appropriately. |
With this new mapping config, when we build our executable JAR file, the /
URL will be mapped to /index.html
, which happens to also be the landing page for our React application.
4 Running the Application
To generate the unified executable JAR, file, simply run the following command:
~ ./gradlew assembleServerAndClient
...
JAR generated at build. It combines the server and client projects.
To run the application, use the java -jar
command:
~ java -jar build/server-0.1.jar
...
Grails application running at http://localhost:8080 in environment: production
Browse to http://localhost:8080
, and you should see the React application.
Make a GET request to http://localhost:8080/application/
, and you should see the application metadata from the Grails application.
Congratulations, you have a unified Grails/React production build!