Show Navigation

Combining the Grails Vue profile client and server projects

Learn how to generate a JAR file which combines Vue and Grails production artefacts.

Authors: Nirav Assar, Zachary Klein

Grails Version: 3.3.8

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 Vue profile. By default, the Vue 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/Vue 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:

or

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 the initial folder.

To complete the guide, go to the initial folder

  • cd into grails-guides/grails-vue-combined/initial

and follow the instructions in the next sections.

You can go right to the completed example if you cd into grails-guides/grails-vue-combined/complete

3 Writing the Application

If you open the initial project of this guide, you will find a plain project generated from the Vue profile.

Both the server Grails app and the client Vue 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 Vue/webpack bundle. Our first step is 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

The initial folder contains a Grails application generated with the vue profile. The project is a Gradle Multiproject build.

Create a build.gradle file in the root project, at the same level as settings.gradle. Add the following content:

build.gradle
task copyClientResources(dependsOn: ':client:build', type: Copy) { (1)
    group = 'build'
    description = 'Copy client resources into server'
    from "${project(':client').projectDir}/dist"
    into "${project(':server').buildDir}/resources/main/public"
}
task assembleServerAndClient(type: Copy, dependsOn: ['copyClientResources', ':server:assemble']) { (2)
    group = 'build'
    description = 'Build combined server & client JAR/WAR'
    from fileTree(dir: "${project(':server').buildDir}/libs/") (3)
    into "${rootProject.buildDir}"
    doLast {
        logger.quiet "JAR/WAR generated at ${rootProject.buildDir}. It combines the server and client projects."
    }
}
task(":server:assemble").mustRunAfter(copyClientResources) (4)
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 :sever: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 open the server project’s build.gradle file, and delete the following line:

server/build.gradle
apply plugin:"war"
Leave the apply plugin: "war" line in server/build.gradle if you wish to generate a WAR file.

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 & Vue 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 Vue app expects, but fortunately, this is a very simple change.

Edit server/grails-app/conf/application.yml:

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 Vue app uses to make REST calls against the Grails app. This can be done within client/config/prod.env.js in the client project. This file is used in the production environment and sets the SERVER_URL to the root context.

client/config/prod.env.js
'use strict'
module.exports = {
  NODE_ENV: '"production"',
  SERVER_URL: '""'
}

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 Vue app at the root context. Fortunately, this is a very simple change as well.

Edit server/grails-app/controllers/demo/UrlMappings.groovy:

Delete this line:

'/'(controller: 'application', action:'index')

Replace it with this:

server/grails-app/controllers/example/grails/UrlMappings.groovy
if ( Environment.current == Environment.PRODUCTION ) {
    '/'(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 Vue 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 Vue 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/Vue production build!

To run the application in development mode:
- ./gradlew client:start - for the client Vue app
- ./gradlew server:bootRun - for the server Grails app
- ./gradlew bootRun -parallel - to launch them at the same time with one command

5 Help with Grails

OCI sponsored the creation of this Guide. OCI offers several Grails services:

Free consultation

The OCI Grails Team includes Grails co-founders, Jeff Scott Brown and Graeme Rocher. Check our Grails courses and learn from the engineers who developed, matured and maintain Grails.

Grails OCI Team