Spring Data REST provides backend functionality
React has sophisticated features to build an easy-to-grok UI.
  • Spring Data REST provides a fast way to build hypermedia-powered repositories.
  • React is Facebook’s solution to efficient, fast, and easy-to-use views in the land of JavaScript.

Declaring your domain

The cornerstone of any Spring Data REST-based application are the domain objects. For this section, you will build an application to track the employees for a company. Kick that off by creating a data type like this:
src/main/java/com/greglturnquist/payroll/Employee.java

@Data
@Entity
public class Employee {

private @Id @GeneratedValue Long id;
private String firstName;
private String lastName;
private String description;

private Employee() {}

public Employee(String firstName, String lastName, String description) {
this.firstName = firstName;
this.lastName = lastName;
this.description = description;
}
}

@Entity is a JPA annotation that denotes the whole class for storage in a relational table.
@Id and @GeneratedValue are JPA annotations to note the primary key and that is generated automatically when needed.
@Data is a Project Lombok annotation to autogenerate getters, setters, constructors, toString, hash, equals, and other things. It cuts down on the boilerplate.

Defining the repository

Another key piece of a Spring Data REST application is to create a corresponding repository definition.
src/main/java/com/greglturnquist/payroll/EmployeeRepository.java
public interface EmployeeRepository extends CrudRepository<Employee, Long> {

}

The repository extends Spring Data Commons' CrudRepository and plugs in the type of the domain object and its primary key

Pre-loading the demo

To work with this application, you need to pre-load it with some data like this:
src/main/java/com/greglturnquist/payroll/DatabaseLoader.java
@Component
public class DatabaseLoader implements CommandLineRunner {

private final EmployeeRepository repository;

@Autowired
public DatabaseLoader(EmployeeRepository repository) {
this.repository = repository;
}

@Override
public void run(String... strings) throws Exception {
this.repository.save(new Employee("Frodo", "Baggins", "ring bearer"));
}
}

This class is marked with Spring’s @Component annotation so that it is automatically picked up by @SpringBootApplication.
It implements Spring Boot’s CommandLineRunner so that it gets run after all the beans are created and registered.
It uses constructor injection and autowiring to get Spring Data’s automatically created EmployeeRepository.
The run() method is invoked with command line arguments, loading up your data.

Adjusting the root URI

By default, Spring Data REST hosts a root collection of links at /. Because you will host a web UI on the same path, you need to change the root URI.
src/main/resources/application.properties
spring.data.rest.base-path=/api

Launching the backend

The last step needed to get a fully operational REST API off the ground is to write a public static void main using Spring Boot:
src/main/java/com/greglturnquist/payroll/ReactAndSpringDataRestApplication.java
@SpringBootApplication
public class ReactAndSpringDataRestApplication {

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

Tested REST service Data

localhost:8080/api
{
  "_links" : {
    "employees" : {
      "href" : "http://localhost:8080/api/employees"
    },
    "profile" : {
      "href" : "http://localhost:8080/api/profile"
    }
  }
}
localhost:8080/api/employees
{
  "_embedded" : {
    "employees" : [ {
      "firstName" : "Frodo",
      "lastName" : "Baggins",
      "description" : "ring bearer",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/api/employees/1"
        }
      }
    } ]
  }
}
localhost:8080/api/employees/1
{
  "firstName" : "Frodo",
  "lastName" : "Baggins",
  "description" : "ring bearer",
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/api/employees/1"
    }
  }
}

Setting up a custom UI controller

Spring Boot makes it super simple to stand up a custom web page. First, you need a Spring MVC controller.
src/main/java/com/greglturnquist/payroll/HomeController.java
@Controller
public class HomeController {

@RequestMapping(value = "/")
public String index() {
return "index";
}

}

@Controller marks this class as a Spring MVC controller.
@RequestMapping flags the index() method to support the / route.
It returns index as the name of the template, which Spring Boot’s autoconfigured view resolver will map to src/main/resources/templates/index.html.


Defining an HTML template

You are using Thymeleaf, although you won’t really use many of its features.
src/main/resources/templates/index.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta charset="UTF-8"/>
    <title>ReactJS + Spring Data REST</title>
    <link rel="stylesheet" href="/main.css" />
</head>
<body>

    <div id="react"></div>

    <script src="built/bundle.js"></script>

</body>
</html>

The key part in this template is the <div id="react"></div> component in the middle. It is where you will direct React to plug in the rendered output.
You may also be wondering where that bundle.js file came from. The way it’s built is shown in the next section.

Loading JavaScript modules

This section contains the barebones information to get off the JavaScript bits off the ground. While you can install all of JavaScripts command line tools, you don’t have to. At least, not yet. Instead, all you need to add is the following to your pom.xml build file:
This little plugin perform multiple steps:
The install-node-and-npm command will install node.js and it’s package management tool, npm, into the target folder. (This ensures the binaries are NOT pulled under source control, and can be cleaned out with clean).
The npm command will execute the npm binary with the provided argument (install). This installs modules defined in package.json.
The webpack command will execute webpack binary, which compiles all the JavaScript code based on webpack.config.js.

What modules are installed? JavaScript developers typically use npm to build up a package.json file like the one below:
package.json
Key dependencies include:
  • react.js - toolkit used by this tutorial
  • rest.js - CujoJS toolkit used to make REST calls
  • webpack - toolkit used to compile JavaScript components into a single, loadable bundle
  • babel - write your JavaScript code using ES6 and compile it into ES5 to run in the browser
To build the JavaScript code you’ll poke at further down, you need to define a build file for webpack.
webpack.config.js
var path = require('path');

module.exports = {
    entry: './src/main/js/app.js',
    devtool: 'sourcemaps',
    cache: true,
    mode: 'development',
    output: {
        path: __dirname,
        filename: './src/main/resources/static/built/bundle.js'
    },
    module: {
        rules: [
            {
                test: path.join(__dirname, '.'),
                exclude: /(node_modules)/,
                use: [{
                    loader: 'babel-loader',
                    options: {
                        presets: ["@babel/preset-env", "@babel/preset-react"]
                    }
                }]
            }
        ]
    }
};
This webpack configuration file does the following:
  • Defines the entry point as ./src/main/js/app.js. In essence, app.js (a module we’ll write shortly) is the proverbial public static void main() of our JavaScript application. webpack must know this in order to know what to launch when the final bundle is loaded by the browser.
  • Creates sourcemaps so when debugging JS code in the browser, is able to link back to original source code.
  • Compile ALL of the JavaScript bits into ./src/main/resources/static/built/bundle.js, which is a JavaScript equivalent to a Spring Boot uber JAR. All your custom code AND the modules pulled in a la require()calls are stuffed into this file.
  • It hooks into the babel engine, using both es2015 and react presets, in order to compile ES6 React code into a format able to be run in any standard browser.
For more details on how each of these JavaScript tools operates, please read their corresponding reference docs.
src/main/js/app.js

const React = require('react');
const ReactDOM = require('react-dom');
const client = require('./client');

React and ReactDOM are the main libraries from Facebook used to build this app.
client is custom code that configures rest.js to include support for HAL, URI Templates, and other things. It also sets the default Accept request header to application/hal+json. You can read the code here.

Diving into React

React is based on defining components. Oftentimes, one component can hold multiple instances of another in a parent-child relationship. It’s easy for this concept to extend several layers.
To start things off, it’s very handy to have a top level container for all components. (This will become more evident as you expand upon the code throughout this series.) Right now, you only have the employee list. But you might need some other related components later on, so let’s start with this:
src/main/js/app.js - App component

class App extends React.Component {

constructor(props) {
super(props);
this.state = {employees: []};
}

componentDidMount() {
client({method: 'GET', path: '/api/employees'}).done(response => {
this.setState({employees: response.entity._embedded.employees});
});
}

render() {
return (
<EmployeeList employees={this.state.employees}/>
)
}
}

class Foo extends React.Component{…​} is the method to create a React component.
componentDidMount is the API invoked after React renders a component in the DOM.
render is the API to "draw" the component on the screen.
In React, uppercase is the convention for naming components.
In the App component, an array of employees is fetched from the Spring Data REST backend and stored in this component’s state data.
React components have two types of data: state and properties.
State is data that the component is expected to handle itself. It is also data that can fluctuate and change. To read the state, you use this.state. To update it, you use this.setState(). Every time this.setState() is called, React updates the state, calculates a diff between the previous state and the new state, and injects a set of changes to the DOM on the page. This results a fast and efficient updates to your UI.
The common convention is to initialize state with all your attributes empty in the constructor. Then you lookup data from the server using componentDidMount and populate your attributes. From there on, updates can be driven by user action or other events.
Properties encompass data that is passed into the component. Properties do NOT change but are instead fixed values. To set them, you assign them to attributes when creating a new component and you’ll soon see.
In this code, the function loads data via client, a Promise compliant instance of rest.js. When it is done retrieving from /api/employees, it then invokes the function inside done()and set’s the state based on it’s HAL document (response.entity._embedded.employees). You might remember the structure of curl /api/employees earlier and see how it maps onto this structure.
When the state is updated, the render() function is invoked by the framework. The employee state data is included in creation of the <EmployeeList /> React component as an input parameter.
Below is the definition for an EmployeeList.
src/main/js/app.js - EmployeeList component

class EmployeeList extends React.Component{
render() {
const employees = this.props.employees.map(employee =>
<Employee key={employee._links.self.href} employee={employee}/>
);
return (
<table>
<tbody>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</tbody>
</table>
)
}
}

Using JavaScript’s map function, this.props.employees is transformed from an array of employee records into an array of <Element /> React components (which you’ll see a little further down).

<Employee key={employee._links.self.href} data={employee} />
This shows a new React component (note the uppercase format) being created along with two properties: key and data. These are supplied the values from employee._links.self.hrefand employee.

Finally, you return an HTML table wrapped around the array of employees built with mapping.
<table>
    <tr>
        <th>First Name</th>
        <th>Last Name</th>
        <th>Description</th>
    </tr>
    {employees}
</table>

This simple layout of state, properties, and HTML shows how React lets you declaritively create a simple and easy-to-understand component.
Does this code contain both HTML and JavaScript? Yes, this is JSX. There is no requirement to use it. React can be written using pure JavaScript, but the JSX syntax is quite terse. Thanks to rapid work on the Babel.js, the transpiler provides both JSX and ES6 support all at once
JSX also includes bits and pieces of ES6. The one used in the code is the arrow function. It avoids creating a nested function() with its own scoped this, and avoids needing a selfvariable.
Worried about mixing logic with your structure? React’s APIs encourage nice, declarative structure combined with state and properties. Instead of mixing a bunch of unrelated JavaScript and HTML, React encourages building simple components with small bits of related state and properties that work well together. It lets you look at a single component and understand the design. Then they are easy to combine together for bigger structures.
Next, you need to actually define what an <Employee /> is.
src/main/js/app.js - Employee component
class Employee extends React.Component{
render() {
return (
<tr>
<td>{this.props.employee.firstName}</td>
<td>{this.props.employee.lastName}</td>
<td>{this.props.employee.description}</td>
</tr>
)
}
}

This component is very simple. It has a single HTML table row wrapped around the employee’s three properties. The property itself is this.props.employee. Notice how passing in a JavaScript object makes it easy to pass along data fetched from the server?
Because this component doesn’t manage any state nor does it deal with user input, there is nothing else to do. This might tempt you to cram it into the <EmployeeList /> up above. Don’t do it! Instead, splitting your app up into small components that each do one job will make it easier to build up functionality in the future.
The last step is to render the whole thing.
src/main/js/app.js - rendering code
ReactDOM.render(
 <App />,
 document.getElementById('react')
)
React.render() accepts two arguments: a React component you defined as well as a DOM node to inject it into. Remember how you saw the <div id="react"></div> item earlier from the HTML page? This is where it gets picked up and plugged in.
With all this in place, re-run the application (./mvnw spring-boot:run) and visit http://localhost:8080.

Review

In this section:
  • You defined a domain object and a corresponding repository.
  • You let Spring Data REST export it with full blown hypermedia controls.
  • You created two simple React components in a parent-child relationship.
  • You fetched server data and rendered them in as a simple, static HTML structure.
Issues?
  • The web page wasn’t dynamic. You had to refresh the browser to fetch new records.
  • The web page didn’t use any hypermedia controls or metadata. Instead, it was hardcoded to fetch data from /api/employees.
  • It’s read only. While you can alter records using cURL, the web page offers none of that.
These are things we can address in the next section.
source code