A ReactJs Web Application With a Spring Boot Backend and Containerizing It Using Docker.
ReactJs is way to go path of developing web applications due to it’s high and profound support in the community and thousands of third party libraries to complement it with. However, connecting it to a Spring Boot application may be a daunting task for some. Thus this article plans to give brief introduction about React and a complete walkthrough on how one can integrate a ReactJs app along with Material-UI with a Spring Boot backend. For details about Spring Boot and a walkthrough of how to build an API using it,
What is ReactJs?
React is an open-source, front end, JavaScript library for building user interfaces or UI components. It is maintained by Facebook and a community of individual developers and companies. React can be used as a base in the development of single-page or mobile applications.
React JS basically represents a view in the application. To connect it with Spring Boot it’s very important to understand how React works under the hood.
React uses a virtual DOM that allows React web-apps to be displayed in a single index.html page for the entire applications rather than other applications which require more html pages for each of the views. The virtual DOM can plug in and out React based components, which holds the state of the application user to be displayed.
Now ReactJs components can be made in two ways —
- Class Based Components
- Functional Components
import React from 'react'
class ReactComponent extends React.Component{ constructor(props){
super(props);
this.state = {
user: "newuser" }
} render(){ return( <div>Hello {this.state.user}<div>
)
}}
The same with functional component will be something —
import React from react;const ReactComponent = (props) => { const [ user, setUser ] = React.useState('newuser'); return ( <div>Hello {user}</div>
);
}
For this walkthrough, we will be going forward with functional components.
Before moving forward i would suggest to go through the docs of useState and useEffect because we are going to use them very frequently.
Now let’s think of the states of the application and the pages of the application. Our application is an apparel store where registered users can view products available in the backend, and can search for them. The products are shown according to the gender of the user initially, and the type of item — new or discounted changes with user interaction ( which is done by the backend). Our job is just to render what is served from the backend. Moreover the user can add products to a cart and update the quantity or delete the item from the cart.
One can use React Redux for keeping state of the application in a global store and calling reducers and actions to manipulate it. However we will be going forward with ReactContext API.
This will be a long tutorial and so feel free to skip through the parts you are already familiar.
- App.js — setting up global authentication state using context API and declaring routes of the application.
- Registration and Login — To handle the authentication state of the user.
- Products and Pagination — Handling pagination in a react application by creating an infinite scroll list. More items are added on clicking on a load more button.
- Shopping Cart — Where items are added to the cart. One can delete and update the quantity of an item in the cart. Heavy use of useEffect to update the current state after each change.
- Building and Docker — The build process for building the react application along with the build of the spring boot app and combining them in a docker container.
First of all let’s make a new React app bootstrapped with Create React App.
npx create-react-app frontend
Now let’s save the dependencies that we will be needing,
npm i @material-ui/core @material-ui/icons @material-ui/lab axios react-router-dom
As discussed let’s make a context.js
Having done this, let’s decide our routes for the application. For routing we will be using React-Router-Dom for defining the different routes of the application.
Here’s App.js the starting point of the application.
Now as we discussed earlier the “products” route is only for authenticated users and it gives an alert to the users to login before proceeding to the authenticated routes.
For that we need to implement the PrivateRoute component.
Thus the privateRoute component displays the component requested if the user is authenticated otherwise redirects it to the home-page with a message in the location state.
Apart from this, the App.js calls an authenticationService function to check if user authentication is already done. This is done by calling the backend and checking if the current user is authenticated. The backend or spring boot checks the cookie in the request and checks if the current session of the user is authenticated. For the frontend, we only have to make a get request to the backend to know the state of authentication of the user.
Now let’s make our first screen — the home screen,
Responsive Design
The Grid component really helps in making responsive design.
For our application to look good on all devices we need to make a fully responsive design. The Grid component from material Ui really helps in making responsive design based on break-points such as xs, sm, lg and others. Now in a screen there is maximum of xs={12} columns. We can set breakpoints such as xs={12} and sm={6} — this means that on screens with size less than sm the component will take the entire width that is xs={12}, otherwise it will consume half of the screen size — 6. There are more breakpoints defined in the documentation.
Moreover we can pass props such as justify and alignItems to make our component align properly in the flex way. For wrappers we use the container prop and for children inside it, we use the item prop.
Now let’s see the code,
Once we are done with the home screen,
Let’s see the register screens and login screens.
The user first registers in the register screen and on successful registration, the router redirects him/her to the login page. The login page is where the actual authentication takes place. On successful verification of the password and email in the database, the user is authenticated and the global state auth is set to true. Now a cookie JSESSIONID is stored which holds the authentication state of the user set by spring boot. Now even on refreshing the browser the user remains authenticated.
I would suggest to go through this article to understand how the backend works.
Here’s the register page,
Here’s the login page on which the user is redirected after registration,
Now let’s come to the products page. We have products getting served from the backend initially based on the user gender. Moreover the products are paginated from the server. Thus we have to make support for pagination in the frontend as well. Each of the product is show in cart component. Let’s make that component first.
Products and Pagination
Now let’s make the products page. On mounting of the component an useEffect will be called to get the initial products from the backend. On click of a load more button the state is updated to next page and more products are fetched and added to the current state of the application. Moreover there is an implementation of the search query which calls another endpoint in the backend where product types similar to the search query are searched and returned to the frontend to be shown. Below is the implementation of the same.
Now we are only left with the shopping cart. The cart screen uses useEffect with and an empty dependency [] to first load the order items when the component is first mounted. When any state is changed such as quantity or in case of a deletion we setState the products which in turn triggers another useEffect with [products] dependency to fetch the changes. Here is the implementation of the same.
Thus we have covered the frontend portion made with React. Now how it is built along with spring boot.
We will be using docker for making them in a container and for the frontend to be built along with the backend we use this excellent plugin — maven frontend plugin.
Let’s see how it is configured in the pom.xml of the spring boot application.
It acts as a plugin and performs npm install and npm run build for the build process of the react application and finally copies the files to the static directory of the spring boot from where it is served.
We also need to make modifications in the spring boot security config for react-router to work. I suggest to go through the backend article where I have explained that.
Now coming to the docker configuration for making them combined in a single docker file. Due to frontend plugin, one can just run mvn install and the application will be up and running. The dockerfile is similar and simple too. We define a docker file in the root of the application.
FROM maven:3.6.1-jdk-8-slim AS build
RUN mkdir -p workspace
WORKDIR workspace
COPY pom.xml /workspace
COPY src /workspace/src
COPY frontend /workspace/frontend
RUN mvn -f pom.xml clean install -DskipTests=true
FROM openjdk:8-alpine
COPY --from=build /workspace/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
Thus we take a maven image and a jdk image and make a jar file of our application together with the reactJs build. Running it is defined in the ENTRYPOINT.
For composing it with a postgres database, we define a docker-compose file,
version: '3'
services:
db:
image: "postgres:9.6-alpine"
container_name: db
restart: always
ports:
- 5432:5432
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=flamup
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- PGDATA=/var/lib/postgresql/data/pgdata
flamup:
build: ./
container_name: flamup
environment:
- DB_SERVER:db
- POSTGRES_DB=flamup
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
ports:
- 8080:8080 # Forward the exposed port 8080 on the container to port 8080 on the host machine
depends_on:
- db
volumes:
postgres_data:
Now running it is as simple as, provided you have docker and docker-compose installed on your system.
docker-compose up --build
Thus the application is live at http://localhost:8080/
This marks the end of this article. In the next article I will be discussing the deployment of this app on AWS ( Amazon Web Services ) and some more future prospects.
So make sure to stay tuned and make sure to follow me.
So there see you at yet another article.
CIAO.