Quarkus + React JS + MongoDB CRUD Example
Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using Quarkus, React JS and MongoDB.
GitHub repository link is provided at the end of this tutorial. You can download the source code.
After completing this tutorial what we will build?
We will build a full-stack web application that is a basic User Management Application with CRUD features:
• Create User
• List User
• Update User
• Delete User
Technologies used:
Back-End:
- Java 11
- Quarkus 2.2.3Final
- MongoDB
- MongDB Panache
Front-End:
- React-router-dom 5+
- Axios 0.19.2
- bootstrap 4.4.1
User Interface
-Retrieve User by Id:
We divided this tutorial into two parts.
PART 1 - Rest APIs Development using Quarkus
PART 2 - UI development using React JS
PART 1 - Rest APIs Development using Quarkus
Project Directory:
Maven[pom.xml]
A Project Object Model or POM is the fundamental unit of work in Maven. It is an XML file that contains information about the project and configuration details utilized by Maven to build the project. It contains default values for most projects. Some of the configurations that can be designated in the POM is the project dependencies, the plugins or goals that can be executed, the build profiles, and so on. Other information such as the project version, description, developers, mailing lists and such can withal be designated.
<?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.knf.dev</groupId>
<artifactId>quarkus-mongo-panache-crud</artifactId>
<version>1.0</version>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.parameters>true</maven.compiler.parameters>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus-plugin.version>2.2.3.Final</quarkus-plugin.version>
<quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
<quarkus.platform.version>2.2.3.Final</quarkus.platform.version>
<surefire-plugin.version>2.22.1</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jsonb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemProperties>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemProperties>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
</systemProperties>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<properties>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>
application.properties
quarkus.mongodb.connection-string = mongodb://localhost:27017
quarkus.mongodb.database = test-user-data
quarkus.http.cors=true
quarkus.http.cors.origins=http://localhost:3000
quarkus.http.cors.methods=GET,PUT,POST,DELETE
Create Document - User.java
package com.knf.dev.model;
import io.quarkus.mongodb.panache.PanacheMongoEntity;
import org.bson.types.ObjectId;
public class User {
public ObjectId id;
private String firstName;
private String lastName;
private String emailId;
public ObjectId getId() {
return id;
}
public void setId(ObjectId id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public User(ObjectId id, String firstName, String lastName, String emailId) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public User() {
super();
}
}
Create UserRepository.java
package com.knf.dev.repository;
import com.knf.dev.model.User;
import io.quarkus.mongodb.panache.PanacheMongoRepository;
import javax.enterprise.context.ApplicationScoped;
@ApplicationScoped
public class UserRepository implements PanacheMongoRepository<User> {
}
Create UserController.java
package com.knf.dev.controller;
import com.knf.dev.model.User;
import com.knf.dev.repository.UserRepository;
import io.quarkus.mongodb.panache.PanacheQuery;
import org.bson.types.ObjectId;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@Path("/api/users")
public class UserController {
private final UserRepository userRepository;
@Inject
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GET
public List<User> getUsers() {
return userRepository.listAll();
}
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public User getUser
(@PathParam("id") String id) {
return userRepository.findById(new ObjectId(id));
}
@PUT
@Path("/{id}")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public void updateUser
(@PathParam("id") String id, User user) {
user.setId(new ObjectId(id));
userRepository.update(user);
}
@POST
public Response addUser(User user) {
userRepository.persist(user);
return Response.status(Response.Status.CREATED).build();
}
@DELETE
@Path("/{id}")
public void deleteUser(@PathParam("id") String id) {
User user = userRepository.findById(new ObjectId(id));
userRepository.delete(user);
}
}
PART 2 - UI Development using React JS
Project Structure of Front-End:
package.json
It is used by the npm CLI (and yarn) to identify your project and understand how to handle the project's dependencies. It's the package. json file that enables npm to start your project, run scripts, install dependencies, publish to the NPM registry, and many other useful tasks.
{
"name": "react-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"bootstrap": "^4.5.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
CreateUserComponent.jsx
import React, { Component } from 'react'
import UserService from '../services/UserService';
class CreateUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
// step 2
id: this.props.match.params.id,
firstName: '',
lastName: '',
emailId: ''
}
this.changeFirstNameHandler = this.changeFirstNameHandler.bind(this);
this.changeLastNameHandler = this.changeLastNameHandler.bind(this);
this.saveOrUpdateUser = this.saveOrUpdateUser.bind(this);
}
// step 3
componentDidMount() {
// step 4
if (this.state.id === '_add') {
return
} else {
UserService.getUserById(this.state.id).then((res) => {
let user = res.data;
this.setState({
firstName: user.firstName,
lastName: user.lastName,
emailId: user.emailId
});
});
}
}
saveOrUpdateUser = (e) => {
e.preventDefault();
let user = {
firstName: this.state.firstName, lastName: this.state.lastName,
emailId: this.state.emailId
};
console.log('user => ' + JSON.stringify(user));
// step 5
if (this.state.id === '_add') {
UserService.createUser(user).then(res => {
this.props.history.push('/users');
});
} else {
UserService.updateUser(user, this.state.id).then(res => {
this.props.history.push('/users');
});
}
}
changeFirstNameHandler = (event) => {
this.setState({ firstName: event.target.value });
}
changeLastNameHandler = (event) => {
this.setState({ lastName: event.target.value });
}
changeEmailHandler = (event) => {
this.setState({ emailId: event.target.value });
}
cancel() {
this.props.history.push('/users');
}
getTitle() {
if (this.state.id === '_add') {
return <h3 className="text-center">Add User</h3>
} else {
return <h3 className="text-center">Update User</h3>
}
}
render() {
return (
<div>
<br></br>
<div className="container">
<div className="row">
<div className="card col-md-6 offset-md-3 offset-md-3">
{
this.getTitle()
}
<div className="card-body">
<form>
<div className="form-group">
<label> First Name: </label>
<input placeholder="First Name" name="firstName" className="form-control"
value={this.state.firstName} onChange={this.changeFirstNameHandler} />
</div>
<div className="form-group">
<label> Last Name: </label>
<input placeholder="Last Name" name="lastName" className="form-control"
value={this.state.lastName} onChange={this.changeLastNameHandler} />
</div>
<div className="form-group">
<label> Email Id: </label>
<input placeholder="Email Address" name="emailId" className="form-control"
value={this.state.emailId} onChange={this.changeEmailHandler} />
</div>
<button className="btn btn-success" onClick={this.saveOrUpdateUser}>
Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style=
{{ marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CreateUserComponent
ListUserComponent.jsx
import React, { Component } from 'react'
import UserService from '../services/UserService'
class ListUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
users: []
}
this.addUser = this.addUser.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
deleteUser(id) {
UserService.deleteUser(id).then(res => {
this.setState({ users: this.state.users.filter(user =>
user.id !== id) });
});
}
viewUser(id) {
this.props.history.push(`/view-user/${id}`);
}
editUser(id) {
this.props.history.push(`/add-user/${id}`);
}
componentDidMount() {
UserService.getUsers().then((res) => {
this.setState({ users: res.data });
});
}
addUser() {
this.props.history.push('/add-user/_add');
}
render() {
return (
<div>
<h2 className="text-center">Users List</h2>
<div className="row">
<button className="btn btn-primary" onClick={this.addUser}>
Add User</button>
</div>
<br></br>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th> User First Name</th>
<th> User Last Name</th>
<th> User Email Id</th>
<th> Actions</th>
</tr>
</thead>
<tbody>
{
this.state.users.map(
user =>
<tr key={user.id}>
<td> {user.firstName} </td>
<td> {user.lastName}</td>
<td> {user.emailId}</td>
<td>
<button onClick={() => this.editUser(user.id)} className="btn btn-
info">Update </button>
<button style={{ marginLeft: "10px" }} onClick=
{() => this.deleteUser(user.id)} className="btn btn-danger">Delete </button>
<button style={{ marginLeft: "10px" }} onClick=
{() => this.viewUser(user.id)} className="btn btn-info">View </button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
)
}
}
export default ListUserComponent
UpdateUserComponent.jsx
import React, { Component } from 'react'
import UserService from '../services/UserService';
class UpdateUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
firstName: '',
lastName: '',
emailId: ''
}
this.changeFirstNameHandler = this.changeFirstNameHandler.bind(this);
this.changeLastNameHandler = this.changeLastNameHandler.bind(this);
this.updateUser = this.updateUser.bind(this);
}
componentDidMount() {
UserService.getUserById(this.state.id).then((res) => {
let user = res.data;
this.setState({
firstName: user.firstName,
lastName: user.lastName,
emailId: user.emailId
});
});
}
updateUser = (e) => {
e.preventDefault();
let user = {
firstName: this.state.firstName, lastName: this.state.lastName,
emailId: this.state.emailId
};
console.log('user => ' + JSON.stringify(user));
console.log('id => ' + JSON.stringify(this.state.id));
UserService.updateUser(user, this.state.id).then(res => {
this.props.history.push('/users');
});
}
changeFirstNameHandler = (event) => {
this.setState({ firstName: event.target.value });
}
changeLastNameHandler = (event) => {
this.setState({ lastName: event.target.value });
}
changeEmailHandler = (event) => {
this.setState({ emailId: event.target.value });
}
cancel() {
this.props.history.push('/users');
}
render() {
return (
<div>
<br></br>
<div className="container">
<div className="row">
<div className="card col-md-6 offset-md-3 offset-md-3">
<h3 className="text-center">Update User</h3>
<div className="card-body">
<form>
<div className="form-group">
<label> First Name: </label>
<input placeholder="First Name" name="firstName" className="form-control"
value={this.state.firstName} onChange={this.changeFirstNameHandler} />
</div>
<div className="form-group">
<label> Last Name: </label>
<input placeholder="Last Name" name="lastName" className="form-control"
value={this.state.lastName} onChange={this.changeLastNameHandler} />
</div>
<div className="form-group">
<label> Email Id: </label>
<input placeholder="Email Address" name="emailId" className="form-control"
value={this.state.emailId} onChange={this.changeEmailHandler} />
</div>
<button className="btn btn-success" onClick={this.updateUser}>Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style=
{{ marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default UpdateUserComponent
ViewUserComponent.jsx
import React, { Component } from 'react'
import UserService from '../services/UserService'
class ViewUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
user: {}
}
}
componentDidMount() {
UserService.getUserById(this.state.id).then(res => {
this.setState({ user: res.data });
})
}
render() {
return (
<div>
<br></br>
<div className="card col-md-6 offset-md-3">
<h3 className="text-center"> View User Details</h3>
<div className="card-body">
<div className="row">
<label> User First Name: </label>
<div> {this.state.user.firstName}</div>
</div>
<div className="row">
<label> User Last Name: </label>
<div> {this.state.user.lastName}</div>
</div>
<div className="row">
<label> User Email ID: </label>
<div> {this.state.user.emailId}</div>
</div>
</div>
</div>
</div>
)
}
}
export default ViewUserComponent
UserService.js
import axios from 'axios';
const USER_API_BASE_URL = "http://localhost:8080/api/users";
class UserService {
getUsers() {
return axios.get(USER_API_BASE_URL);
}
createUser(user) {
return axios.post(USER_API_BASE_URL, user);
}
getUserById(userId) {
return axios.get(USER_API_BASE_URL + '/' + userId);
}
updateUser(user, userId) {
return axios.put(USER_API_BASE_URL + '/' + userId, user);
}
deleteUser(userId) {
return axios.delete(USER_API_BASE_URL + '/' + userId);
}
}
export default new UserService()
Run
npm install
npm start
Download the source code:
git clone: