Comprehensive step by step by step tutorial on build Grails 3, MongoDB and React Profile CRUD Web Application. Previously we have to show you how to build CRUD Web Application with the famous Angular 5. Now, we will use React.js as front-end framework that will be working together on the same project with Grails 3. Using this profile will create 2 application, they are Grails 3 as server and React.js as Client.
The scenario for this tutorial almost same as previous tutorial using Angular 5 profile. they are creating, read, update and delete Customer data. CRUD mechanism using RESTful API provided by Grails 3 and visualization using React.js as the front end.
The following tools, frameworks, and module are required for this tutorial achievement:
Before moving to the steps of the tutorial, make sure you have installed above requirements.
1. Create New Grails 3 Application
Open the terminal or command line then go to your Grails projects folder. Type this command to create a new Grails 3 application with React profile.
grails create-app grails-react --profile=react
Go to the newly created Grails 3 application folder.
cd ./grails-react
Now you have server and client folder inside this project folder.
drwxr-xr-x 8 didin staff 256 Mar 20 20:25 client
drwxr-xr-x 3 didin staff 96 Mar 20 20:25 gradle
-rwxr--r-- 1 didin staff 4971 Mar 20 20:25 gradlew
-rwxr--r-- 1 didin staff 2314 Mar 20 20:25 gradlew.bat
drwxr-xr-x 10 didin staff 320 Mar 20 20:25 server
-rw-r--r-- 1 didin staff 26 Mar 20 20:25 settings.gradle
You can run server or client only by type this command.
./gradlew server:bootRun
./gradlew client:bootRun
To run both of them together, use this command.
./gradlew bootRun --parallel
Node.js and its dependencies will be downloaded automatically. React application will run using port 3000 and Grails 3 using port 8080. React application will open in the default browser automatically.
You will get this response on the browser when you change the address to localhost:8080
.
2. Create Grails 3 Domain Class
To save or populate MongoDB data, first, we have to add Gradle dependencies for MongoDB. Open and edit server/build.gradle
file then comment out all Hibernate and H2 dependencies then add dependencies for MongoDB.
buildscript {
...
dependencies {
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
classpath "com.moowork.gradle:gradle-node-plugin:0.13"
// classpath "org.grails.plugins:hibernate5:${gormVersion-".RELEASE"}"
classpath "org.grails.plugins:views-gradle:1.1.6"
}
}
...
dependencies {
...
// compile "org.grails.plugins:hibernate5"
// compile "org.hibernate:hibernate-core:5.1.5.Final"
compile "org.grails.plugins:views-json"
compile "org.grails.plugins:views-json-templates"
console "org.grails:grails-console"
profile "org.grails.profiles:react"
runtime "org.glassfish.web:el-impl:2.1.2-b03"
// runtime "com.h2database:h2"
runtime "org.apache.tomcat:tomcat-jdbc"
compile 'org.grails.plugins:mongodb'
...
}
Compile Grails 3 application by typing this command inside Server folder.
cd ./server
grails compile
Next, open and edit server/grails-app/conf/application.yml
then remove or replace in-memory H2 database and Hibernate configuration with this.
...
environments:
development:
grails:
mongodb:
host: "localhost"
port: 27017
username: ""
password: ""
databaseName: "grails-react"
production:
grails:
mongodb:
host: "localhost"
port: 27017
username: ""
password: ""
databaseName: "grails-react"
Now, we are ready to create a domain class for Customer data. In the terminal or command line inside Server folder type this command to enter Grails 3 interactive console.
grails
Create a new Grails 3 domain class by typing this command.
create-domain-class grails.react.Customer
Open and edit server/grails-app/domain/grails/react/Customer.groovy
file then Replace all codes with this.
package grails.react
import grails.rest.*
@Resource(uri='/customer')
class Customer {
String name
String address
String city
String postalCode
String phone
static constraints = {
name blank:false
address blank:false
city blank:false
postalCode blank:false
phone blank:false
}
String toString() {
name
}
}
That it’s, just modify a domain class we have a RESTful API for Customer data CRUD operation.
3. Test CRUD RESTful API
Now, it’s a time for testing a CRUD operation. We will use CURL
command for this. Open the other terminal or command line tab then run MongoDB server if there’s no currently running MongoDB server.
mongod
Run again the Server by using this command in previous terminal tab after type exit
in the Grails 3 interactive console.
cd ../
./gradlew server:bootRun
Open another terminal or command line tab again then type this command to get Customer data/list.
curl -i -H "Accept: application/json" localhost:8080/customer
The correct response should be like this.
HTTP/1.1 200
X-Application-Context: application:development
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 21 Mar 2018 00:48:40 GMT
[]
Next, save or post a single data to the Customer endpoint.
curl -i -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","address":"accross the river behind the mountain","city":"the hight mount","postalCode":"11111","phone":"123123123"}' localhost:8080/customer
You will get this response when data saved successfully to MongoDB database.
HTTP/1.1 201
X-Application-Context: application:development
Location: http://localhost:8080/customer/1
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Wed, 21 Mar 2018 00:51:05 GMT
{"id":1,"phone":"123123123","address":"accross the river behind the mountain","postalCode":"11111","name":"John Doe","city":"the hight mount"}
Now you have your RESTful API ready to access from the React front-end application. Don’t worry about the CORS for using the different port because it already enabled by Grails 3 default configuration.
4. Add React Router DOM for CRUD Front End
This React front end consists of the Customer list, detail, create and edit. For navigating between that component, we need to create a route. First, install modules that required by the components.
npm install --save react-router-dom
npm install --save-dev bootstrap
npm install --save axios
Next, open and edit src/index.js
then replace all codes with this.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import App from './App';
import './css/App.css';
import './css/grails.css';
import './css/main.css';
import Edit from './components/Edit';
import Create from './components/Create';
import Show from './components/Show';
ReactDOM.render(
<Router>
<div>
<Route exact path='/' component={App} />
<Route path='/edit/:id' component={Edit} />
<Route path='/create' component={Create} />
<Route path='/show/:id' component={Show} />
</div>
</Router>,
document.getElementById('root')
);
You see that Edit, Create and Show added as the separate component. Bootstrap also included in the import for make the views better. Now, create the new edit, create and show files.
mkdir src/components
touch src/components/Create.js
touch src/components/Show.js
touch src/components/Edit.js
5. Add List of Customer to Existing App Component
To get the list of Customer and display to the page, open and edit client/src/App.js
file then replace with this lines of codes.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Link } from 'react-router-dom';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = {
customers: []
};
}
componentDidMount() {
axios.get('http://localhost:8080/customer')
.then(res => {
this.setState({ customers: res.data });
console.log(this.state.customers);
});
}
render() {
return (
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
CUSTOMER LIST
</h3>
</div>
<div class="panel-body">
<h4><Link to="/create"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> Add Customer</Link></h4>
<table class="table table-stripe">
<thead>
<tr>
<th>Name</th>
<th>Address</th>
</tr>
</thead>
<tbody>
{this.state.customers.map(cust =>
<tr>
<td><Link to={`/show/${cust.id}`}>{cust.name}</Link></td>
<td>{cust.address}</td>
</tr>
)}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default App;
6. Add a Components for Create a New Customer
For add a new Customer, open and edit client/src/components/Create.js
then replace all codes with this lines of codes.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import { Link } from 'react-router-dom';
class Create extends Component {
constructor() {
super();
this.state = {
name: '',
address: '',
city: '',
postalCode: '',
phone: ''
};
}
onChange = (e) => {
const state = this.state
state[e.target.name] = e.target.value;
this.setState(state);
}
onSubmit = (e) => {
e.preventDefault();
const { name, address, city, postalCode, phone } = this.state;
axios.post('http://localhost:8080/customer', { name, address, city, postalCode, phone })
.then((result) => {
this.props.history.push("/")
});
}
render() {
const { name, address, city, postalCode, phone } = this.state;
return (
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
ADD CUSTOMER
</h3>
</div>
<div class="panel-body">
<h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Customer List</Link></h4>
<form onSubmit={this.onSubmit}>
<div class="form-group">
<label for="isbn">Name:</label>
<input type="text" class="form-control" name="name" value={name} onChange={this.onChange} placeholder="Name" />
</div>
<div class="form-group">
<label for="title">Address:</label>
<input type="text" class="form-control" name="address" value={address} onChange={this.onChange} placeholder="Address" />
</div>
<div class="form-group">
<label for="author">City:</label>
<input type="text" class="form-control" name="city" value={city} onChange={this.onChange} placeholder="City" />
</div>
<div class="form-group">
<label for="published_date">Postal Code:</label>
<input type="number" class="form-control" name="postalCode" value={postalCode} onChange={this.onChange} placeholder="Postal Code" />
</div>
<div class="form-group">
<label for="publisher">Phone:</label>
<input type="tetx" class="form-control" name="phone" value={phone} onChange={this.onChange} placeholder="Phone Number" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default Create;
7. Add a Component for Show Customer Details
To show customer details that listed on the home page, open and edit client/src/components/Show.js
then replace all codes with this lines of codes.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
class Show extends Component {
constructor(props) {
super(props);
this.state = {
customer: {}
};
}
componentDidMount() {
axios.get('http://localhost:8080/customer/'+this.props.match.params.id)
.then(res => {
this.setState({ customer: res.data });
console.log(this.state.customer);
});
}
delete(id){
console.log(id);
axios.delete('http://localhost:8080/customer/'+id)
.then((result) => {
this.props.history.push("/")
});
}
render() {
return (
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Customer Details
</h3>
</div>
<div class="panel-body">
<h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Customer List</Link></h4>
<dl>
<dt>Name:</dt>
<dd>{this.state.customer.name}</dd>
<dt>Address:</dt>
<dd>{this.state.customer.address}</dd>
<dt>City:</dt>
<dd>{this.state.customer.city}</dd>
<dt>Postal Code:</dt>
<dd>{this.state.customer.postalCode}</dd>
<dt>Phone Number:</dt>
<dd>{this.state.customer.phone}</dd>
</dl>
<Link to={`/edit/${this.state.customer.id}`} class="btn btn-success">Edit</Link>
<button onClick={this.delete.bind(this, this.state.customer.id)} class="btn btn-danger">Delete</button>
</div>
</div>
</div>
);
}
}
export default Show;
In this component, there is two button for edit current customer and for delete current customer. Delete function included in this component.
8. Add a Component for Edit a Customer
After show customer details, we need to edit the customer. For that open and edit, client/src/components/Edit.js
then add this lines of codes.
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
class Edit extends Component {
constructor(props) {
super(props);
this.state = {
customer: {}
};
}
componentDidMount() {
axios.get('http://localhost:8080/customer/'+this.props.match.params.id)
.then(res => {
this.setState({ customer: res.data });
console.log(this.state.customer);
});
}
onChange = (e) => {
const state = this.state.customer
state[e.target.name] = e.target.value;
this.setState({customer:state});
}
onSubmit = (e) => {
e.preventDefault();
const { name, address, city, postalCode, phone } = this.state.customer;
axios.put('http://localhost:8080/customer/'+this.props.match.params.id, { name, address, city, postalCode, phone })
.then((result) => {
this.props.history.push("/show/"+this.props.match.params.id)
});
}
render() {
return (
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
EDIT CUSTOMER
</h3>
</div>
<div class="panel-body">
<h4><Link to={`/show/${this.state.customer.id}`}><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> Customer List</Link></h4>
<form onSubmit={this.onSubmit}>
<div class="form-group">
<label for="name">Name:</label>
<input type="text" class="form-control" name="name" value={this.state.customer.name} onChange={this.onChange} placeholder="Name" />
</div>
<div class="form-group">
<label for="title">Address:</label>
<input type="text" class="form-control" name="address" value={this.state.customer.address} onChange={this.onChange} placeholder="Address" />
</div>
<div class="form-group">
<label for="author">City:</label>
<input type="text" class="form-control" name="city" value={this.state.customer.city} onChange={this.onChange} placeholder="City" />
</div>
<div class="form-group">
<label for="description">Postal Code:</label>
<input type="number" class="form-control" name="postalCode" value={this.state.customer.postalCode} onChange={this.onChange} placeholder="Postal Code" />
</div>
<div class="form-group">
<label for="published_date">Phone Number:</label>
<input type="number" class="form-control" name="phone" value={this.state.customer.phone} onChange={this.onChange} placeholder="Phone Number" />
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
);
}
}
export default Edit;
9. Run and Test Grails 3 and React Profile CRUD Web Application
It’s time for running the server and client side and test all CRUD functionality. Type this command from the root of the project directory.
./gradlew bootRun --parallel
If there’s some wrong with the client. You can run the client using the standard Node.js command in the other terminal tab.
cd ./client
npm start
Now, here the whole application looks like.
That it’s, if you need the full working source code, you can find it on our GitHub.
#mongodb #reactjs #web-development