Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreTo see updates to this code, visit our React.js and Spring Data REST tutorial. |
Welcome Spring community,
This is the first of several blog entries. In this session, you will see how to get a bare-bones Spring Data REST application up and running quickly. Then you will build a simple UI on top of it using Facebook’s React.js toolset.
Feel free to grab the code from this repository and follow along.
If you want to do it yourself, visit http://start.spring.io and pick these items:
This demo uses Java 8, Maven Project, and the latest stable release of Spring Boot. This will give you a clean, empty project. From there, you can add the various files shown explicitly in this session, and/or borrow from the repository listed above.
In the beginning there was data. And it was good. But then people wanted to access the data through various means. Over the years, people cobbled together lots of MVC controllers, many using Spring’s powerful REST support. But doing over and over cost a lot of time.
Spring Data REST addresses how simple this problem can be if some assumptions are made:
The cornerstone of any Spring Data REST-based application are the domain objects. For this session, you will build an application to track the employees for a company. Kick that off by creating a data type like this:
@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 annotation to note the primary key and that is generated automatically when needed.
@Data
and @RequiredArgsConstructor
are Project Lombok annotations to autogenerate getters, setters, constructors, toString, hash, equals, and other things. It cuts down on the boilerplate.
This entity is used to track employee information. In this case, their name and job description.
Note
|
Spring Data REST isn’t confined to JPA. It supports many NoSQL data stores, but you won’t be covering those here. |
Another key piece of a Spring Data REST application is to create a corresponding repository definition.
public interface EmployeeRepository extends CrudRepository<Employee, Long> {
}
CrudRepository
and plugs in the type of the domain object and its primary key
That is all that is needed! In fact, you don’t even have to annotate this invisible if its top-level and visible. If you use your IDE and open up CrudRepository
, you’ll find a fist full of pre-built methods already defined.
Note
|
You can define your own repository if you wish. Spring Data REST supports that as well. |
To work this this application, you need to pre-load it with some data like this:
@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"));
}
}
@Component
annotation so that it is automatically picked up by @SpringBootApplication
.
CommandLineRunner
so that it gets run after all the beans are created and registered.
EmployeeRepository
.
run()
method is invoked with command line arguments, loading up your data.
One of the biggest, most powerful features Spring Data is its ability to write JPA queries for you. This not only cuts down on your development time, but also reduces the risk of bugs and errors. Spring Data looks at the name of methods in a repository class and figures out the operation you need including saving, deleting, and finding.
That is how we can write an empty interface and inherit already build save, find, and delete operations.
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.
spring.data.rest.base-path=/api
The last step needed to get a fully operationl REST API off the ground is to write a public static void main
using Spring Boot:
@SpringBootApplication
public class ReactAndSpringDataRestApplication {
public static void main(String[] args) {
SpringApplication.run(ReactAndSpringDataRestApplication.class, args);
}
}
Assuming the previous class as well as your Maven build file were generated from http://start.spring.io, you can now launch it either by running that main()
method inside your IDE, or type ./mvnw spring-boot:run
on the command line. (mvnw.bat for Windows users).
Note
|
If you aren’t up-to-date on Spring Boot and how it works, you should consider watch one of Josh Long’s introductory presentations. Did it? Press on! |
With the app running, you can check things out on the command line using cURL (or any other tool you like).
$ curl localhost:8080/api { "_links" : { "employees" : { "href" : "http://localhost:8080/api/employees" }, "profile" : { "href" : "http://localhost:8080/api/profile" } } }
When you ping the root node, you get back a collection of links wrapped up in a HAL-formatted JSON document.
EmployeeRepository
interface.
You can further dig into this service by navigating the employees link.
$ curl localhost:8080/api/employees { "_embedded" : { "employees" : [ { "firstName" : "Frodo", "lastName" : "Baggins", "description" : "ring bearer", "_links" : { "self" : { "href" : "http://localhost:8080/api/employees/1" } } } ] } }
At this stage, you are viewing the entire collection of employees.
What’s included along with the data you pre-loaded earlier is a _links attribute with a self link. This is the canonical link for that particular employee. What is canonical? It means free of context. For example, the same user could be fetched through a link like /api/orders/1/processor, in which the employee is assocated with processing a particular order. Here, there is no relationship to other entities.
Important
|
Links are a critical facet of REST. They provide the power to navigate to related items. It makes it possible for other parties to navigate around your API without having to rewrite things everytime there is a change. Updates in the client is a common problem when the clients hard code paths to resources. Restructuring resources can cause big upheavals in code. If links are used and instead the navigation route is maintained, then it becomes easy and flexible to make such adjustments. |
You can decide to view that one employee if you wish.
$ curl localhost:8080/api/employees/1 { "firstName" : "Frodo", "lastName" : "Baggins", "description" : "ring bearer", "_links" : { "self" : { "href" : "http://localhost:8080/api/employees/1" } } }
Little change here, except that there is no need for the _embedded wrapper since there is only domain object.
That’s all and good, but you are probably itching to create some new entries.
$ curl -X POST localhost:8080/api/employees -d '{"firstName": "Bilbo", "lastName": "Baggins", "description": "burglar"}' -H 'Content-Type:application/json' { "firstName" : "Bilbo", "lastName" : "Baggins", "description" : "burglar", "_links" : { "self" : { "href" : "http://localhost:8080/api/employees/2" } } }
You can also PUT, PATCH, and DELETE as shown in this related guide. But let’s not dig into that. You have already spent way too much time interacting with this REST service manually. Don’t you want to build a slick UI instead?
Spring Boot makes it super simple to stand up a custom web page. First, you need a Spring MVC controller.
@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.
index
as the name of the template, which Spring Boot’s autoconfigured view resolver will map to src/main/resources/templates/index.html
.
You are using Thymeleaf, although you won’t really use many of its features.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<meta charset="UTF-8"/>
<title>ReactJS + Spring Data REST</title>
<script async="" data-main="/run.js" src="/bower_components/requirejs/require.js"></script>
<link rel="stylesheet" href="/main.css" />
</head>
<body>
<div id="react"></div>
</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.
This tutorial won’t go into extensive detail on how it uses require.js to load JavaScript modules. But thanks to the frontend-maven-plugin, you don’t have to install any of the node.js tools to build and run the code.
The following JavaScript modules will be used:
If you’re interested, the paths for the JavaScript moodules are defined in run.js. The main JavaScript app is loaded by the web page through main.js.
With all that in place, you can focus on the React bits which are fetched after the DOM is loaded. It’s broken down into parts as below:
Since you are using require.js to load things, go ahead and fetch the modules you need:
var React = require('react');
var client = require('./client');
React
is the main library from Facebook for building 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.
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:
var App = React.createClass({
getInitialState: function () {
return ({employees: []});
},
componentDidMount: function () {
client({method: 'GET', path: '/api/employees'}).done(response => {
this.setState({employees: response.entity._embedded.employees});
});
},
render: function () {
return (
<EmployeeList employees={this.state.employees}/>
)
}
})
React.createClass({…})
is the method to create a React component.
getInitialState
is the API to initialize state data (data that is expected to vary).
componentDidMount
is the API invoked after React renders a component in the DOM.
render
is the API to "draw" the component on the screen.
Note
|
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 getInitialState
. 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.
Warning
|
JavaScript doesn’t lock down data structures like other languages. You can try to subvert properties by assigning values, but this doesn’t work with React’s differential engine and should be avoided. |
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
.
var EmployeeList = React.createClass({
render: function () {
var employees = this.props.employees.map(employee =>
<Employee key={employee._links.self.href} employee={employee}/>
);
return (
<table>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</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.href
and employee
.
Important
|
Whenever you work with Spring Data REST, the self link IS the key for a given resource. React needs a unique identifer for child nodes, and _links.self.href is perfect.
|
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. If you noticed the file extension, this is JSX. There is no requirement to use it. React can be written using pure JavaScript, but the JSX syntax is quite terse.
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 self variable.
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.
var Employee = React.createClass({
render: function () {
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.
React.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.
You can see the initial employee loaded up by the system.
Remember using cURL to create new entries? Do that again.
curl -X POST localhost:8080/api/employees -d '{"firstName": "Bilbo", "lastName": "Baggins", "description": "burglar"}' -H 'Content-Type:application/json'
Refresh the browser, and you should see the new entry:
And now you can see both of them listed on the web site.
In this session:
Issues?
/api/employees
.
These are things we can address in the next session. Until then, happy coding!