Building Spring Boot, MongoDB and React.js CRUD Web Application

by Didin J. on Mar 24, 2018 Building Spring Boot, MongoDB and React.js CRUD Web Application

Step by step tutorial on building Spring Boot 2.0.0, MongoDB and React.js CRUD Web Application

Comprehensive step by step tutorial on building Spring Boot 2.0.0, MongoDB and React.js CRUD Web Application. Previously we have done a tutorial on building Spring Boot, MongoDB and Angular 5 CRUD Web Application. From there we have a lesson that integrates React.js or other front-end frameworks with Java web application is simple. We will run the React.js application on the same port as the Spring Boot Java Web Application.

Table of Contents:

The following tools, frameworks, and modules are required for this tutorial achievement:

  1. Java Development Kit 8
  2. Spring Boot 2.0.0
  3. Spring Web
  4. Spring Data
  5. Gradle
  6. MongoDB
  7. React.js
  8. Node.js
  9. Spring Initializer
  10. Terminal or Command Line
  11. Text Editor or IDE

Before continuing to the main steps, make sure that you have installed above tools, frameworks, and modules in your machine.


Generate Spring Boot 2.0.0 Web Application

As usual, we start creating the Spring Boot 2.0.0 application by generating the application and its library using Spring Initializer. Spring Initializr provides an extensible API to generate quickstart projects, and to inspect the metadata used to generate projects, for instance, to list the available dependencies and versions. After go to Spring Initializer using the web browser, create the new application and add the library as below.

Building Spring Boot, MongoDB and React.js CRUD Web Application - Spring Initializr

Click `Generate Project` button and the Spring boot 2.0.0 application will be downloaded by your browser. Next, extract the zipped Spring Boot project then copy or move to your Java projects folder. To run the Spring Boot application, type this command from the terminal or command line.

cd react
./gradlew bootRun

You will not find a landing page on the browser yet because of there's no frontend or HTML page in the static folder. To connect Spring Boot with MongoDB, open and edit `src/main/java/resources/application.properties` then add this lines.

spring.data.mongodb.database=springreact
spring.data.mongodb.host=localhost
spring.data.mongodb.port=27017

Next, start MongoDB in the other terminal or command line if it's not run yet. Run again Spring Boot application. Now, Spring Boot and MongoDB are connected and ready.


Create Java Model or Entity Class

If are using Text Editor, you have to create a folder for models. From the terminal create a models folder in `src/main/java/com/djamware/react`.

mkdir src/main/java/com/djamware/react/models

To create Model or Entity Class simply create a new file at `src/main/java/com/djamware/react/models` then name it `Contact.java`.

touch  src/main/java/com/djamware/react/models/Contact.java

Open and edit `src/main/java/com/djamware/react/models/Contact.java` then add these lines of codes that contain a POJO for the contact model or entity including the constructor, getter, and setter.

package com.djamware.react.models;

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document(collection = "contacts")
public class Contact {
    @Id
    String id;
    String name;
    String address;
    String city;
    String phone;
    String email;

    public Contact() {
    }

    public Contact(String name, String address, String city, String phone, String email) {
        this.name = name;
        this.address = address;
        this.city = city;
        this.phone = phone;
        this.email = email;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

That class mapped to MongoDB collection with the name `contacts`.


Create a New Java Repository Interface for Contact Model

For connecting to Contact model, we need to create a Java interface or repository. Create a folder named `repositories` inside `src/main/java/com/djamware/react/` folder.

mkdir src/main/java/com/djamware/react/repositories

Next, create a Java Interface file inside that folder.

touch src/main/java/com/djamware/react/repositories/ContactRepository.java

Open and edit `src/main/java/com/djamware/react/repositories/ContactRepository.java` then add this lines of codes of extended CrudRepository and only add a single method for delete contact.

package com.djamware.react.repositories;

import com.djamware.react.models.Contact;
import org.springframework.data.repository.CrudRepository;

public interface ContactRepository extends CrudRepository<Contact, String> {
    @Override
    void delete(Contact deleted);
}

We only add `findOne` and `delete` method to the interface because the rest method already handled by `CrudRepository` of Spring Data MongoDB.


Create a New Spring MVC RESTful Controller

Now, it's a time for RESTful Web Service (REST API) implementation by creating a new Spring MVC RESTful controller file. Create a folder named `controllers` inside `src/main/java/com/djamware/react/` folder.

mkdir src/main/java/com/djamware/react/controllers

Create a Java file named `ContactController.java` inside that new folder.

touch src/main/java/com/djamware/react/controllers/ContactController.java

Open and edit `src/main/java/com/djamware/react/controllers/ContactController.java` then add this lines of codes that implementing request and response from or to the client-side using REST API. The REST API determine by @RestController and @RequestMapping Spring MVC annotation.

package com.djamware.react.controllers;

import com.djamware.react.models.Contact;
import com.djamware.react.repositories.ContactRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Optional;

@RestController
public class ContactController {

    @Autowired
    ContactRepository contactRepository;

    @RequestMapping(method=RequestMethod.GET, value="/contacts")
    public Iterable<Contact> contact() {
        return contactRepository.findAll();
    }

    @RequestMapping(method=RequestMethod.POST, value="/contacts")
    public Contact save(@RequestBody Contact contact) {
        contactRepository.save(contact);

        return contact;
    }

    @RequestMapping(method=RequestMethod.GET, value="/contacts/{id}")
    public Optional<Contact> show(@PathVariable String id) {
        return contactRepository.findById(id);
    }

    @RequestMapping(method=RequestMethod.PUT, value="/contacts/{id}")
    public Contact update(@PathVariable String id, @RequestBody Contact contact) {
        Optional<Contact> optcontact = contactRepository.findById(id);
        Contact c = optcontact.get();
        if(contact.getName() != null)
            c.setName(contact.getName());
        if(contact.getAddress() != null)
            c.setAddress(contact.getAddress());
        if(contact.getCity() != null)
            c.setCity(contact.getCity());
        if(contact.getPhone() != null)
            c.setPhone(contact.getPhone());
        if(contact.getEmail() != null)
            c.setEmail(contact.getEmail());
        contactRepository.save(c);
        return c;
    }

    @RequestMapping(method=RequestMethod.DELETE, value="/contacts/{id}")
    public String delete(@PathVariable String id) {
        Optional<Contact> optcontact = contactRepository.findById(id);
        Contact contact = optcontact.get();
        contactRepository.delete(contact);

        return "";
    }
}


Test the REST API using CURL

Now, re-run again Spring Boot application then test the REST API using CURL from the terminal.

./gradlew bootRun

Open another terminal then type this command to get a Contact list from the REST API.

curl -i -H "Accept: application/json" localhost:8080/contacts

It should display a response like this.

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 05 Feb 2018 12:14:39 GMT

[]

To add a contact, type this command to post a contact data.

curl -i -X POST -H "Content-Type: application/json" -d '{"name":"John Doe","address":"Jl. Cipaganti No.112","city":"Bandung","phone":"0811223112233","email":"[email protected]"}' localhost:8080/contacts

It should display a response like this.

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 06 Feb 2018 02:59:00 GMT

{"id":"5a7919f4b9d4f13c2cb6f539","name":"John Doe","address":"Jl. Cipaganti No.112","city":"Bandung","phone":"0811223112233","email":"[email protected]"}

You can check on MongoDB console to verify that new contact data is saved. Next, to get a single contact data by ID type this command.

curl -i -H "Accept: application/json" localhost:8080/contacts/5a784bbfb9d4f121d9a31195

And here's the response.

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Mon, 05 Feb 2018 12:22:49 GMT

{"id":"5a784bbfb9d4f121d9a31195","name":"John Doe","address":"Jl. Cipaganti No.112","city":"Bandung","phone":"0811223112233","email":"[email protected]"}

To update existing contact by ID type this command.

curl -i -X PUT -H "Content-Type: application/json" -d '{"name":"John Dodol"}' localhost:8080/contacts/5a784bbfb9d4f121d9a31195

It should response like this.

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 24 Mar 2018 08:49:11 GMT

{"id":"5a784bbfb9d4f121d9a31195","name":"John Dodol","address":"Jl. Cipaganti No.112","city":"Bandung","phone":"0811223112233","email":"[email protected]"}

To delete a contact by ID type this command.

curl -i -X DELETE localhost:8080/contacts/5a784bbfb9d4f121d9a31195

Here's the response.

HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 0
Date: Sat, 24 Mar 2018 07:51:42 GMT


Create a React.js Application

This time for the frontend side, in the terminal and root project folder type this command.

create-react-app frontend

That command will create a React.js application folder named `frontend`. To make React build running as Spring Boot frontend, open and edit `frontend/package.json` then change `build` value in the scripts block as below.

"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build && rm -rf ../src/main/resources/static && mv build ../src/main/resources/static",
  "test": "react-scripts test --env=jsdom",
  "eject": "react-scripts eject"
},

Now, test the React.js configuration by type this command.

cd ./frontend
npm run build

Back to root folder then run again Spring Boot application.

cd ..
./gradlew bootRun

Now, when you pointing your browser to `localhost:8080`, you should see this page.

Building Spring Boot, MongoDB and React.js CRUD Web Application - React.js page


Add React Router DOM for CRUD Front End

This React front end consists of the Contacts list, detail, create and edit. For navigating between that component, we need to create a route. We will use react-router-dom as React router library. First, install modules required by the components after stop the Spring Boot application.

cd ./frontend
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 './App.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 to making 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


Add List of Contacts to Existing App Component

To get the list of Contacts and display to the page, open and edit `frontend/src/App.js` file then replace with these lines of codes which contain React App component that fetch data from REST API then display to HTML template as a List.

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 = {
      contacts: []
    };
  }

  componentDidMount() {
    axios.get('/contacts')
      .then(res => {
        this.setState({ contacts: res.data });
        console.log(this.state.contacts);
      });
  }

  render() {
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              CONTACTS LIST
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/create"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span> Add Contact</Link></h4>
            <table class="table table-stripe">
              <thead>
                <tr>
                  <th>Name</th>
                  <th>Address</th>
                </tr>
              </thead>
              <tbody>
                {this.state.contacts.map(c =>
                  <tr>
                    <td><Link to={`/show/${c.id}`}>{c.name}</Link></td>
                    <td>{c.address}</td>
                  </tr>
                )}
              </tbody>
            </table>
          </div>
        </div>
      </div>
    );
  }
}

export default App;


Add a Components for Create a New Contact

For adding a new Contact, open and edit `frontend/src/components/Create.js` then replace all codes with these 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: '',
      phone: '',
      email: ''
    };
  }
  onChange = (e) => {
    const state = this.state
    state[e.target.name] = e.target.value;
    this.setState(state);
  }

  onSubmit = (e) => {
    e.preventDefault();

    const { name, address, city, phone, email } = this.state;

    axios.post('/contacts', { name, address, city, phone, email })
      .then((result) => {
        this.props.history.push("/")
      });
  }

  render() {
    const { name, address, city, phone, email } = this.state;
    return (
      <div class="container">
        <div class="panel panel-default">
          <div class="panel-heading">
            <h3 class="panel-title">
              ADD CONTACT
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Contacts 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">Phone:</label>
                <input type="text" class="form-control" name="phone" value={phone} onChange={this.onChange} placeholder="Phone Number" />
              </div>
              <div class="form-group">
                <label for="publisher">Email:</label>
                <input type="email" class="form-control" name="email" value={email} onChange={this.onChange} placeholder="Email Address" />
              </div>
              <button type="submit" class="btn btn-default">Submit</button>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

export default Create;


Add a Component for Show Contact Details

To show contact details that listed on the home page, open and edit `frontend/src/components/Show.js` then replace all codes with these 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 = {
      contact: {}
    };
  }

  componentDidMount() {
    axios.get('/contacts/'+this.props.match.params.id)
      .then(res => {
        this.setState({ contact: res.data });
        console.log(this.state.contact);
      });
  }

  delete(id){
    console.log(id);
    axios.delete('/contacts/'+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">
              Contact Details
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to="/"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span> Contacts List</Link></h4>
            <dl>
              <dt>Name:</dt>
              <dd>{this.state.contact.name}</dd>
              <dt>Address:</dt>
              <dd>{this.state.contact.address}</dd>
              <dt>City:</dt>
              <dd>{this.state.contact.city}</dd>
              <dt>Phone Number:</dt>
              <dd>{this.state.contact.phone}</dd>
              <dt>Email Address:</dt>
              <dd>{this.state.contact.email}</dd>
            </dl>
            <Link to={`/edit/${this.state.contact.id}`} class="btn btn-success">Edit</Link>&nbsp;
            <button onClick={this.delete.bind(this, this.state.contact.id)} class="btn btn-danger">Delete</button>
          </div>
        </div>
      </div>
    );
  }
}

export default Show;

In this component, there are two buttons for edit current contact and for delete current contact. Delete function included in this component.


Add a Component for Edit a Contact

Aftershow contact details, we need to edit the contact. For that open and edit, `frontend/src/components/Edit.js` then add these 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 = {
      contact: {}
    };
  }

  componentDidMount() {
    axios.get('/contacts/'+this.props.match.params.id)
      .then(res => {
        this.setState({ contact: res.data });
        console.log(this.state.contact);
      });
  }

  onChange = (e) => {
    const state = this.state.contact
    state[e.target.name] = e.target.value;
    this.setState({contact:state});
  }

  onSubmit = (e) => {
    e.preventDefault();

    const { name, address, city, postalCode, phone } = this.state.contact;

    axios.put('/contacts/'+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 Contact
            </h3>
          </div>
          <div class="panel-body">
            <h4><Link to={`/show/${this.state.contact.id}`}><span class="glyphicon glyphicon-eye-open" aria-hidden="true"></span> Contact 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.contact.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.contact.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.contact.city} onChange={this.onChange} placeholder="City" />
              </div>
              <div class="form-group">
                <label for="published_date">Phone Number:</label>
                <input type="text" class="form-control" name="phone" value={this.state.contact.phone} onChange={this.onChange} placeholder="Phone Number" />
              </div>
              <div class="form-group">
                <label for="description">Email:</label>
                <input type="email" class="form-control" name="email" value={this.state.contact.email} onChange={this.onChange} placeholder="Email Address" />
              </div>
              <button type="submit" class="btn btn-default">Update</button>
            </form>
          </div>
        </div>
      </div>
    );
  }
}

export default Edit;


Run and Test Spring Boot 2.0.0, MongoDB and React.js CRUD Web Application

It's time for running the server and client-side and test all CRUD functionality. Type this command to build the React.js application and deploy to Spring Boot 2.0.0 application.

npm run build

Now, back to the root directory then type this command to run again Spring Boot 2.0.0 application.

cd ..
./gradlew bootRun

Here the whole application looks like.

Building Spring Boot, MongoDB and React.js CRUD Web Application - Contact List
Building Spring Boot, MongoDB and React.js CRUD Web Application - Add Contact
Building Spring Boot, MongoDB and React.js CRUD Web Application - Contact Details
Building Spring Boot, MongoDB and React.js CRUD Web Application - Edit Contact

That's it, a simple CRUD application build by Spring Boot 2.0.0, MongoDB and React.js. You can compare with the full working source code from our GitHub.

That just the basic. If you need more deep learning about Java and Spring Framework you can take the following cheap course:

Thanks!