deploying spring app with react frontend to heroku - reactjs

I have a react app with Spring backend. I have successfully deployed the Spring web app to Heroku, I can send http requests via postman to the endpoints and they work fine.
But I am unsure how I should allow the app on heroku to utilise the react frontend.
So far my structure is as below,
syftgolf
-.idea
-.mvn
-src
-target
-uploads-dev
-uploads-prod
-uploads-test
-.gitignore
-HELP.md
-mvnw
-mvnw.cmd
-pom.xml
-syftgolf.iml
-system.properties
Then in src I have
src
-main
--frontend //Front end react code
--java //Java code for Spring web app
--resources
-test
I am using the heroku CLI command git push heroku master on my route directory to deploy the app to heroku, this appears to then auto detact its a java app and then builds the app from there.
I am stuck though on how is the correct way to also build the react app? I read a post about building it first and adding it into the target/classes/static folder on my Spring app, but this folder doesn't appear to exist.
Which is the correct way to go about doing this please so when deployed, my app on heroku will also show the frontend stuff.

You need to tell maven where the react app resources are and where to add them in the output artifact. Use the maven resource plugin as follows:
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/resources</outputDirectory>
<resources>
<resource>
<directory>/path/to/react/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
Just change the resource directory path to your react app build directory. The plugin will copy all the files in the directory to your artifact resources folder so spring will find them.

Related

How to deploy Spring boot and React application together on GCP App Engine?

I don't have any experience in deployment and trying to deploy Frontend and backend together. I am able to successfully deploy the spring boot application.
I followed this tutorial and successfully generated a build.
Is it possible to deploy the frontend and backend together?
As mentioned in this GCP docs:
Use services in App Engine to factor your large apps into logical
components that can securely share App Engine features and communicate
with one another. Generally, your App Engine services behave like
microservices.
Deploying two apps written in different language on a same runtime in App Engine standard is not possible as you won't be able to run Javascript apps if the current runtime in your app.yaml is Java.
My suggestion would be to split your applications into separate services as mentioned in the docs.
Finally, if you insist on a monolithic approach, consider deploying your app to a custom runtime in App Engine flex. Refer to this doc. Do note that it requires a Dockerfile so you will have to manage the containerization of your apps.
We can deploy a spring boot backend application with react front end as a single JAR in GCP. I simply followed instructions in tutorial , Created an app engine in GCP and ran this command - gcloud app deploy ***.jar
You need to create the jar using maven and then run the command on that jar. It will deploy in gcp.
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<mainClass>${start-class}</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>

How to integrate a React webapp inside a spring boot Application with jar packaging

We have a react webapp and a spring boot application(built with maven) in development.
The React app(on npm) and boot app are running separately but time has come to now integrate them and put it into QA/staging.
We are using webpack for bundling the react application.
The boot app is a single module with REST API(consumed by the react front-end) with hibernate for database persistence.
Questions
What should be the directory structure for both react and boot app? We want to deploy the whole app(front-end and back-end) as a .jar (spring boot uberjar)
What is the react app development workflow ? A workflow without having to run maven and run java -jar every time a small css, html or .js change is made.
I found resources on web where both the react and the boot applications were on separate repos/directories without any integration, which is not optimal; I am yet to find a resource where both webapp resources and boot resources are in the same directory tree which also accounts for development lifecycle and production lifecycle.
Bonus : The react developers know only html/js/css. Is it possible that they have only the react app resources in their vscode/webstorm project ?
You can run React and SpringBoot on the same port and Package them as a single artifact !!
Please follow the steps that I have explained here, that should get you up and running.
To answer your questions-
The directory structure has been show below. Both will be under the same root directory.
Regarding react app development workflow- you can just develop it the way you develop a front-end react project, with hot reloads(save the file and the changes will reflect automatically) and meaningful error messages. You will have to use proxying to communicate.
Here is the Github link of the demo project that I am going to
explain here
Spring Boot can serve static content from src/main/resources/static folder. We will exploit the above mentioned feature of Spring Boot to serve the single page of the react project. We will serve a html page from the static folder in the target directory, not in the source directory.
The Project structure-
First, create a spring boot project with https://start.spring.io. Add the Web dependency. Set the groupId and artifactId to whatever you want. Generate the project and unzip it into your project directory.
Or, if you are using Spring Tools Suite you can simply click
File->New->Spring Starter Project and mention the required details to create a spring boot project.
The frontend folder inside src/main should have your react application build using create-react-app.
So, there are two steps-
create a production build of the frontend.
copy the production build into ${target/classes/}.
We we will use two maven plugins and Thymleaf for that.
frontend-maven-plugin for step 1.
maven-resources-plugin for step 2.
For frontend-maven-plugin at Step 1-- If you closely look at the pom.xml there I have mentioned the src directroy from where frontend-maven-plugin will take the files, create the production build and place the contents inside the output directory mentioned(inside src/main/frontend/build).
<workingDirectory>${frontend-src-dir}</workingDirectory>
<installDirectory>${project.build.directory}</installDirectory>
For maven-resources-plugin at step 2-- It will take the production build that was just created by frontend-maven-plugin and place it inside your root directory then target/classes/static.
Then we will use Thymleaf to serve the static content from the target/classes/static using a rest endpoint in the controller. Or else you have to type in the name of the html file, like http://localhost:8080/index.html
Your pom.xml should look like this-
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.springreact</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Run React Frontend and SpringBoot Backend on the same port.</description>
<properties>
<java.version>1.8</java.version>
<frontend-src-dir>${project.basedir}/src/main/frontend</frontend-src-dir>
<node.version>v14.15.4</node.version>
<yarn.version>v1.16.0</yarn.version>
<frontend-maven-plugin.version>1.7.6</frontend-maven-plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>${frontend-maven-plugin.version}</version>
<configuration>
<nodeVersion>${node.version}</nodeVersion>
<yarnVersion>${yarn.version}</yarnVersion>
<workingDirectory>${frontend-src-dir}</workingDirectory>
<installDirectory>${project.build.directory}</installDirectory>
</configuration>
<executions>
<execution>
<id>install-frontend-tools</id>
<goals>
<goal>install-node-and-yarn</goal>
</goals>
</execution>
<execution>
<id>yarn-install</id>
<goals>
<goal>yarn</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>build-frontend</id>
<goals>
<goal>yarn</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<arguments>build</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>position-react-build</id>
<goals>
<goal>copy-resources</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<outputDirectory>${project.build.outputDirectory}/static</outputDirectory>
<resources>
<resource>
<directory>${frontend-src-dir}/build</directory>
<filtering>false</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Here is the Controller code.
package com.springreact.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class IndexController {
#GetMapping("")
public ModelAndView home() {
ModelAndView mav=new ModelAndView("index");
return mav;
}
}
If you follow the above mentioned steps , you should see your React App being spun up on http://localhost:8080/.
For further details you can checkout the blog that I have written on it. Here are the links of the blog on two different platforms-
Dev Community- https://dev.to/arpan_banerjee7/run-react-frontend-and-springboot-backend-on-the-same-port-and-package-them-as-a-single-artifact-14pa
Medium- https://medium.com/codex/run-react-frontend-and-springboot-backend-on-the-same-port-and-package-them-as-a-single-artifact-a790c9e10ac1
I would suggest creating multimodule maven project, where one module will be spring-boot app and second one react app. To make it into uber jar, pack react app into webjar build by maven.
Building can be done also by maven with proper plugin.
So:
Create normal react app (build by npm and so on) in sub directory of you project
Add to this app pom.xml which will compile javascript on maven compile and pack it into proper webjar structure
In spring-boot module add react one as normal maven dependency. Check out this tutorial
This will allow you to have everything as one uber jar app. On the other hand, front-end people can work with react module as with any other react application.
I found this post that may answer your question. It shows how to develop the react front end and spring boot back end and then merge them into a single jar file for deployment.
https://medium.com/#mukundmadhav/build-and-deploy-react-app-with-spring-boot-and-mysql-6f888eb0c600
To answer the question on whether it is best practice to create an uber jar or to package the app in a single jar. It depends on certain things. Like for ex. If you have oauth2 implemented in your app and would like to access the rest end points without having to implement the logic of access token and such. You could put it in a single jar. else you'd have to create a header with the access token in the header.

How to create war file of angular project?

I am working on project where I used angular as frontend and spring boot as backend. I want to deploy my project on Sentora control panel. It requires only war files to deploy on server. I created war file of spring project but I dont know how to create war file of angular project. I used vs code for angular. I tried to find the way of creating war file for angular on internet but didn't find any solution. Should I need to import angular project in eclipse? Please someone tell me step by step solution to create war file of angular project.
Update: Error while deploying wabit.war file
I deployed simple spring boot project demo-0.0.1-SNAPSHOT and it started.
wabit.war file is deployed but it doesn't start. It gives error
FAIL - Application at context path [/wabit] could not be started
Why is this happening?
You can use maven for doing that.
Just add a pom.xml in you project with the following information.
With this configuration you will have to build your angular application in a file directory named dist.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- The Basics -->
<groupId>your.group</groupId>
<artifactId>projectName</artifactId>
<version>1.0.0</version>
<packaging>war</packaging>
<!-- Build Settings -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<webResources>
<resource>
<!-- this is relative to the pom.xml directory -->
<directory>dist</directory>
</resource>
</webResources>
</configuration>
</plugin>
</plugins>
</build>
</project>
After creating this file you will have to use the following command
mvn clean install
and the file will be created in the target/ directory
You can find more information on maven-war-plugins in the apache maven official documentation
Another solution is instead of creating a war file for your angular project just embed it in the spring boot war.
To do that
Just create your angular dist by ng build command.
Copy the contents of everything in that folder.
Go to your Spring Boot project and paste them inside a static package in the resources
directory. The path should be src/main/resources/static
Create and deploy the Spring boot war file and your angular will run as well.
This is how you directory of spring boot should look like

GAE OAuth deploy using different accounts based on project

So when deploying to GAE with maven using OAuth it creates .appcfg_oauth2_tokens_java file in your home directory. This works great if you can deploy all your projects with one account. But what can you do when you wish to use different account for one of projects?
Can this file be project specific?
I don't wish to delete this file every time I want to deploy with different account.
You can prevent storing cookies with appcfg.sh --oauth2 --no_cookies.
For use with maven you can add the following config to your pom.xml:
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>1.9.9</version>
<configuration>
<oauth2>true</oauth2>
<noCookies>true</noCookies>
</configuration>
</plugin>

Using the google appengine maven plugin to deploy a Jax-RS war

I'm trying to make a simple base project to deploy a service I make using Jax-RS librarys to my Google app engine cloud space. The problem is that I don't know how to configure the plugin properly to not keep looking to a webapp directory under the target folder. The structure of the Jax-rs project puts the web.xml and all other WEB-INF files under the resources directory instead of a webapp directory. Is there a way to configure the maven plugin to deploy my already built and zipped up war file?
This is the error I see
[INFO] Updating Google App Engine Application Unable to find the
webapp directory C:\dev\gameTrunk\server\target\HOMMTG-server-1.0
usage: AppCfg [options] [] []
Action must be one of: help: Print help for a specific action.
download_app: Download a previously uploaded app version.
request_logs: Write request logs in Apache common log format.
rollback: Rollback an in-progress update. start: Start the specified
server version.
and it goes on with all the appengine plugin targets...
This s my pom
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>2.5.1</version>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.3</version>
<configuration>
<archiveClasses>true</archiveClasses>
<webResources>
<!-- in order to interpolate version from pom into appengine-web.xml -->
<resource>
<directory>${basedir}/src/main/resources/WEB-INF</directory>
<filtering>true</filtering>
<targetPath>WEB-INF</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>${appengine.target.version}</version>
</plugin>
</plugins>
Thats just the plugins part but its almost exactly the same as the guestbook example project except for the path for the WEB-INF directory
Currently there is no way to configure the appengine-maven-plugin to use a different directory for the war contents. It would be best though to probably create a multimodule build where one submodule just used the maven-ant-plugin to assemble the war directory and then run the plugin on that instead. I don't think we want to make that configurable in the plugin, since it doesn't really align with the maven-war-plugin, and configuring that would make it difficult to setup your project to use the maven-war-plugin seamlessly in the future.
The configuration you currently have for the war-plugin in your pom isn't necessary unless you want interpolation of the version number into the appengine-web.xml. I'm happy to help with setting up your pom so that the official Google App Engine Maven plugin works correctly for you.
(The instructions below apply to maven-gae-plugin, not appengine-maven-plugin.
Have I told you how much Google sucks in Open Source today?)
I think you must add in your maven-gae-plugin a property called appDir pointing to your webapp directory, like this:
<plugin>
<groupId>net.kindleit</groupId>
<artifactId>maven-gae-plugin</artifactId>
<configuration>
<appDir>PATH-TO-YOUR-BUILT-EXPLODED-WAR-PATH</appDir>
</configuration>
</plugin>
However, I must stress that changing the path in Maven produces undesirable results (you're mixing source and object code, your .ignore files will get messed up, and other weird things)
Note you STILL NEED your unpacked war somewhere. One way to achieve that is to create another .war project and use dependencies-unpack into it. See http://maven.apache.org/plugins/maven-dependency-plugin/unpack-dependencies-mojo.html
(reference: https://github.com/maven-gae-plugin/maven-gae-plugin/blob/master/maven-gae-plugin/src/main/java/net/kindleit/gae/EngineGoalBase.java)
(just in case, there is a JAX-RS based project for GAE I've wrote a while ago, and its open. See https://github.com/ipeirotis/ReadabilityMetrics/ for an overview)

Resources