Preskoči na glavni sadržaj

Creating a new web app using create-react-app and Spring Boot

React is awesome. We know it, we love it. However, we also know that setting it up can be a bit of a pain the neck. Well, not setting up React itself but all the goodies that you usually want when developing a web app with React, such as  Webpack, Babel and ESLint.

Fortunately,  the good people at Facebook are well aware of this so they created an official, easy-to-use way to quickly set up a React app: create-react-app.

Here's how to get your app up and running in no time:

First, if you haven't already, install create-react-app (I'm using npm but, of course, nothing is stopping you from installing it with your prefered package manager):

$ npm install -g create-react-app

Then, create the app:

$ create-react-app boot-react-example

The example app that we're creating here will be, very imaginatively, called boot-react-example. Wait until create-react-app does its thing and... that's it!
No messing about with configuration files, no problems. That was all it took.

Start your app:

$ cd boot-react-example
$ npm start


You should now see your app running on localhost:3000.

Running

$ npm run build


will create an optimized production build of our app in the build folder. Index.html from the public folder will be used as a template for index.html of our app, which will have references to .css and .js files that Webpack will make by combining all our stylesheet and javascript resources into two respective files.

There's one more thing we can do to make our lives easier, and that's to enable hot module reloading. Go to the index.js file and change it so that it contains the following code:

if (module.hot) {
  module.hot.accept()
}

The application should now be reloaded any time you change and save a javascript file, but without the browser having to refresh the page.

Alas, frontend alone does not a web app make, and if you're a Java dev, you'll want to work with Java on the server side. Enter Spring Boot.
Spring Boot makes creating the server side of your app as easy as create-react-app makes creating the frontend easy.

We're going to be using Maven to handle our build and dependencies and to do that, our project needs to have a standard pom.xml file. So create a new pom.xml in your project's root directory:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.blogspot.matejsprogblog</groupId>
    <artifactId>boot-react-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    
</project>  

You can run

$ mvn clean package


from the project folder just to check that everything is ok.

Now, we said that we're going to create a Spring Boot project  so let's do it.
We're going to use a cool feature of Spring boot called starters. These will provide tested and production-ready dependency configurations for your app out of the box.
First, let's set spring-boot-starter-parent to be our project's parent. Add it to your pom.xml:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.blogspot.matejsprogblog</groupId>
    <artifactId>boot-react-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    

</project>

Spring-boot-starter-parent provides dependency and plugin management for Spring Boot projects with a set of sensible defaults. You can read about its features here. Having it as our parent means that we don't have to worry about versions of some of the most common dependencies, as well as whether we're going to have issues with different versions of our dependencies playing nice with each other - the starter parent will make sure that we don't.

Next, we'll add a dependency to spring-boot-starter-web starter.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.blogspot.matejsprogblog</groupId>
    <artifactId>boot-react-example</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>


This starter will provide us with everything that we need to build a REST API which will be consumed by our frontend.

Now, let's write some code for our server side. First of all, we'll need to create a standard project directory structure for our app because this is something that we didn't get when we created the application using create-react-app.

In the src folder, create a source folder main/java. Then, in that folder, create a new package which will hold our main application class and the class itself.

package com.blogspot.matejsprogblog;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BootReactExample {

 public static void main(String[] args) {
  SpringApplication.run(BootReactExample.class, args);
 }

}

This is really all we have to do to get a running app. We need a class annotated with SpringBootApplication that has a main method in which we call the run method of SpringApplication and pass our class and the provided arguments.

Still, our application, such as it is, isn't really doing much. Let's give it some functionality. A simple "Hello world!" will do.

package com.blogspot.matejsprogblog.greeting;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GreetingController {

 @GetMapping("/greet")
 public String greet() {
  return "Hello world!";
 }
}

Ok, so now we have a controller that returns a greeting. Let's test it out. Run BootReactExample as a standard Java app then go to http://localhost:8080/greet.
You should see our app greeting us with a nice "Hello world!". Port 8080 is the default port on which Boot's embedded Tomcat runs, but if for some reason you can't run the app on that port, you can create an application.properties file and add a new server.port property with a free port.

Let's see what we have so far. We have a running frontend, and a running backend that exposes a single REST service that returns a greeting when called.
All that's left to do now to get a fully functioning app is to get those two to work together so that we can see our greeting on the front page.

We'll change App.js  so that it displays a greeting:

import logo from './logo.svg';
import './App.css';

class App extends Component {
 
    constructor(props) {
      super(props);
   
      this.state = {
        greeting: "This is a greeting" 
      }
    } 
 
 
    render() {
     return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload!
        </p>
        <p>{this.state.greeting}</p>
      </div>
    );
  }
}

export default App;

If you take a look at the app in your browser, it should display "This is a greeting" below the rest of the text.

But this is not really what we want, we want the app to fetch the greeting from the server and display it on page load.

We could try to do it like this:


import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
 
    constructor(props) {
      super(props);
   
      this.state = {
        greeting: "This is a greeting" 
      }
    } 
 
    componentDidMount() {
        fetch("http://localhost:8080/greet").then(function(response) {
               return response.text();
            }).then((text) => {
                  this.setState({greeting: text})
               });
     }
 
    render() {
     return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload!
        </p>
        <p>{this.state.greeting}</p>
      </div>
    );
  }
}

export default App; 

But, of course, this will fail because of the same-origin policy.

The way to resolve this problem is to make the node.js server that's serving our frontend proxy the requests to the server serving our Boot backend.

Change the fetch request to ask for "/greet" as if it were served from the same origin as the script:

import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';

class App extends Component {
        
    constructor(props) {
      super(props);
   
      this.state = {
        greeting: "This is a greeting" 
      }
    } 
 
    componentDidMount() {
        fetch("/greet").then(function(response) {
               return response.text();
            }).then((text) => {
                  this.setState({greeting: text})
                });
     }
 
    render() {
     return (
      <div className="App">
        <div className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <h2>Welcome to React</h2>
        </div>
        <p className="App-intro">
          To get started, edit <code>src/App.js</code> and save to reload!
        </p>
        <p>{this.state.greeting}</p>
      </div>
    );
  }
}

export default App; 

Then, add the line ""proxy": "http://localhost:8080/"" to package.json so that requests to routes not recognized by our frontend are proxied to the server seving our Boot app:

{
  "name": "boot-react-example",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "react": "^15.5.4",
    "react-dom": "^15.5.4"
  },
  "devDependencies": {
    "react-scripts": "1.0.7"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  
  "proxy": "http://localhost:8080/"
}

Take a look at the app in the browser. If your Boot backend is running, you should now see our app greeting us with a "Hello world!".

Ok, so this solves the problem of communication between our frontend and our backend. We can now expose our services through a REST api provided by Spring Boot and develop our frontend with all the benefits of hot code reloading.

However, for production, we'd like to have two things. First, we'd like resources such as .js and .css files packaged into an optimized package by webpack and we'd also like the whole app served from one place.

This requires that we make some changes to our pom.xml file so that Maven knows what to do when we package our app for production.

First, we'll add Spring Boot Maven plugin to our build configuration. It will kick in during the package phase and repackage our app as a full-fledged Spring Boot app that can be executed by running "java -jar".

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>com.blogspot.matejsprogblog</groupId>
 <artifactId>boot-react-example</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 
 <packaging>jar</packaging>

 <parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>1.5.2.RELEASE</version>
 </parent>

 <properties>
  <java.version>1.8</java.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>
 </dependencies> 
 
 <build>
  <plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>   
  </plugins>
 </build>


</project>

Next, we'll use the awesome frontend-maven-plugin to perform the necessary steps to build our React frontend. Of course, if for any reason you can't use this plugin, you can always use standard exec-maven-plugin instead. Frontend-maven-plugin just makes everything a bit easier.

We'll use it to do three things. First, we're going to do a local installation of node and npm. This way, we don't depend on any external node and npm installations that may or may not be present on the build machines. Also, we get to control our node and npm version.

The second thing that we're going to do is an npm install. We'll use the goal "npm" for that. You can specify a configuration argument "install" if you want to, but that's actually not necessary as that argument is default for this goal. This will download any declared dependencies in our package.json.

Finally, we'll perform an npm frontend build by supplying the arguments "run build" to the "npm" goal.

<build>
 <plugins>
  <plugin>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-maven-plugin</artifactId>
  </plugin>
  <plugin>
   <groupId>com.github.eirslett</groupId>
   <artifactId>frontend-maven-plugin</artifactId>
   <version>1.4</version>
   <executions>
    <execution>
     <id>Install node and npm locally to the project</id>
     <goals>
      <goal>install-node-and-npm</goal>
     </goals>
     <configuration>
      <nodeVersion>v8.0.0</nodeVersion>
      <npmVersion>5.0.3</npmVersion>
     </configuration>
    </execution>

    <execution>
     <id>npm install</id>
     <goals>
      <goal>npm</goal>
     </goals>
    </execution>

    <execution>
     <id>Build frontend</id>
     <goals>
      <goal>npm</goal>
     </goals>
     <configuration>
      <arguments>run build</arguments>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>

The default phase for these executions is generate-resources, which suits us perfectly. This is because we'll take what was produced by our npm build (remember, npm put the built frontend in the "build" folder in our project) and copy it to the correct place in our target folder in the next phase, process-resources.

<build>
 <plugins>
  <plugin>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-maven-plugin</artifactId>
  </plugin>
  <plugin>
   <groupId>com.github.eirslett</groupId>
   <artifactId>frontend-maven-plugin</artifactId>
   <version>1.4</version>
   <executions>
    <execution>
     <id>Install node and npm locally to the project</id>
     <goals>
      <goal>install-node-and-npm</goal>
     </goals>
     <configuration>
      <nodeVersion>v8.0.0</nodeVersion>
      <npmVersion>5.0.3</npmVersion>
     </configuration>
    </execution>

    <execution>
     <id>npm install</id>
     <goals>
      <goal>npm</goal>
     </goals>
    </execution>

    <execution>
     <id>Build frontend</id>
     <goals>
      <goal>npm</goal>
     </goals>
     <configuration>
      <arguments>run build</arguments>
     </configuration>
    </execution>
   </executions>
  </plugin>
  <plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-resources-plugin</artifactId>
   <executions>
    <execution>
     <id>Copy frontend build to target</id>
     <phase>process-resources</phase>
     <goals>
      <goal>copy-resources</goal>
     </goals>
     <configuration>
      <outputDirectory>${basedir}/target/classes/resources</outputDirectory>
      <resources>
       <resource>
        <directory>${basedir}/build</directory>
        <filtering>true</filtering>
       </resource>
      </resources>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>

Let's take it for a spin! Run the Maven build:

$ mvn clean package

Once it's done, you should get a file called boot-react-example-0.0.1-SNAPSHOT.jar in your target folder. Run it:

$ java -jar boot-react-example-0.0.1-SNAPSHOT.jar

If you go to localhost:8080 in your browser, you should see the familiar face of our app, greeting us with a "Hello world!".

That's it! As you can see, creating a new React app with Spring Boot and create-react-app is pretty easy.

I hope this article helped you and thanks for reading!

Primjedbe

  1. very informative blog and useful article thank you for sharing with us , keep posting learn more Ruby on Rails Online Course

    OdgovoriIzbriši
  2. Well Explained . Keep this work up. There is good rectjs tutorail visit React Js Tutorial

    OdgovoriIzbriši
  3. Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.

    Digital Marketing Training in Chennai

    Digital Marketing Course in Chennai

    OdgovoriIzbriši
  4. Harrah's Cherokee Casino Resort reopening on June 30
    Harrah's Cherokee Casino Resort 김해 출장샵 is reopening 강릉 출장안마 June 30. The casino, which is owned by the Eastern Band 제천 출장안마 of 통영 출장안마 Cherokee Indians, 대구광역 출장마사지

    OdgovoriIzbriši

  5. Renting a car is usually a less-than-exciting experience when traveling. Getting the keys to a bland four door is nothing to write home about. What many do not realize is that any vacation or business trip can be an adventure with a car hire. Luxury Car Rental Coimbatore

    No matter the destination, having the keys to an exotic car is a thrill. Even the gas station can be fun when fellow customers notice the beauty at the pump. A car that totals more than many people's mortgage is sure to turn heads wherever it goes. Just the sound of the engine will catch the attention of admirers as you fly by.

    OdgovoriIzbriši
  6. BLCK Luxury - Bangalore | Luxury Car Rental Bangalore | Luxury Taxi Bangalore | Self Drive Cars in Bangalore

    Luxury Car Rental Bangalore

    OdgovoriIzbriši
  7. Shri Mintu's Art forayed into the online furniture space as https://shrimintus.com/
    to offer value-for-money furniture made of sheesham wood to Indian consumers.
    The manufacturer and online seller provides finished
    as well as customised products to buyers.
    Enjoy a wide variety of traditional and modern living room furniture with shri Mintus Art.

    Wooden furniture

    OdgovoriIzbriši

Objavi komentar