Go Lang + ReactJS + Mysql CRUD example - Go Full stack development

Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using React, Go, and Mysql.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 All Users 
• Update User 
• Delete User 
• View User 


We divided this tutorial into two parts 

PART 1 - Rest APIs Development using Go and Mysql 

PART 2 - UI development using React JS


PART 1 - Rest APIs Development using Go Language

These are APIs that the Go Language application will export:
GET all User's        :     /users 
GET User by ID     :     /users/{_id} 
POST User             :     /users 
PUT User               :     /users/{_id} 
DELETE User       :     /users/{_id}

Backend Project Structure:


Create database 'userdb':

CREATE DATABASE userdb;

Create table 'users':

CREATE TABLE `users` (
`id` bigint(20) UNSIGNED NOT NULL,
`first_name` varchar(200) NOT NULL,
`last_name` varchar(200) NOT NULL,
`email` varchar(200) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


ALTER TABLE `users`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `id` (`id`);

ALTER TABLE `users`
MODIFY `id` bigint(20) UNSIGNED NOT NULL
AUTO_INCREMENT, AUTO_INCREMENT=11;

Initialize the Go project:

Initialize the Go project using the following command
go mod init backend

Adding the modules required for the project:

go get github.com/gorilla/mux
go get github.com/go-sql-driver/mysql

main.go

package main

import (
"database/sql"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)

func main() {
Routers()
}

func Routers() {
InitDB()
defer db.Close()
router := mux.NewRouter()
router.HandleFunc("/users",
GetUsers).Methods("GET")
router.HandleFunc("/users",
CreateUser).Methods("POST")
router.HandleFunc("/users/{id}",
GetUser).Methods("GET")
router.HandleFunc("/users/{id}",
UpdateUser).Methods("PUT")
router.HandleFunc("/users/{id}",
DeleteUser).Methods("DELETE")
http.ListenAndServe(":9080",
&CORSRouterDecorator{router})
}

/***************************************************/

//Get all users
func GetUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var users []User
result, err := db.Query("SELECT id, first_name," +
"last_name,email from users")
if err != nil {
panic(err.Error())
}
defer result.Close()
for result.Next() {
var user User
err := result.Scan(&user.ID, &user.FirstName,
&user.LastName, &user.Email)
if err != nil {
panic(err.Error())
}
users = append(users, user)
}
json.NewEncoder(w).Encode(users)
}

//Create user
func CreateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
stmt, err := db.Prepare("INSERT INTO users(first_name," +
"last_name,email) VALUES(?,?,?)")
if err != nil {
panic(err.Error())
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err.Error())
}
keyVal := make(map[string]string)
json.Unmarshal(body, &keyVal)
first_name := keyVal["firstName"]
last_name := keyVal["lastName"]
email := keyVal["email"]
_, err = stmt.Exec(first_name, last_name, email)
if err != nil {
panic(err.Error())
}
fmt.Fprintf(w, "New user was created")
}

//Get user by ID
func GetUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
result, err := db.Query("SELECT id, first_name,"+
"last_name,email from users WHERE id = ?", params["id"])
if err != nil {
panic(err.Error())
}
defer result.Close()
var user User
for result.Next() {
err := result.Scan(&user.ID, &user.FirstName,
&user.LastName, &user.Email)
if err != nil {
panic(err.Error())
}
}
json.NewEncoder(w).Encode(user)
}

//Update user
func UpdateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
stmt, err := db.Prepare("UPDATE users SET first_name = ?," +
"last_name= ?, email=? WHERE id = ?")
if err != nil {
panic(err.Error())
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err.Error())
}
keyVal := make(map[string]string)
json.Unmarshal(body, &keyVal)
first_name := keyVal["firstName"]
last_name := keyVal["lastName"]
email := keyVal["email"]
_, err = stmt.Exec(first_name, last_name, email,
params["id"])
if err != nil {
panic(err.Error())
}
fmt.Fprintf(w, "User with ID = %s was updated",
params["id"])
}

//Delete User
func DeleteUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
stmt, err := db.Prepare("DELETE FROM users WHERE id = ?")
if err != nil {
panic(err.Error())
}
_, err = stmt.Exec(params["id"])
if err != nil {
panic(err.Error())
}
fmt.Fprintf(w, "User with ID = %s was deleted",
params["id"])
}

/***************************************************/

type User struct {
ID string `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
}

//Db configuration
var db *sql.DB
var err error

func InitDB() {
db, err = sql.Open("mysql",
"root:@tcp(127.0.0.1:3306)/userdb")
if err != nil {
panic(err.Error())
}
}

/***************************************************/

// CORSRouterDecorator applies CORS headers to a mux.Router
type CORSRouterDecorator struct {
R *mux.Router
}

func (c *CORSRouterDecorator) ServeHTTP(rw http.ResponseWriter,
req *http.Request) {
if origin := req.Header.Get("Origin"); origin != "" {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods",
"POST, GET, OPTIONS, PUT, DELETE")
rw.Header().Set("Access-Control-Allow-Headers",
"Accept, Accept-Language,"+
" Content-Type, YourOwnHeader")
}
// Stop here if its Preflighted OPTIONS request
if req.Method == "OPTIONS" {
return
}

c.R.ServeHTTP(rw, req)
}

Run:

go run main.go



PART 2 - UI development using React JS

Front Project Structure:



package.json 

A package.json is a JSON file that subsists at the root of a Javascript/Node project. It holds metadata pertinent to the project and is utilized for managing the project's dependencies, scripts, version, and a whole lot more.
{
"name": "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: '',
email: ''
}
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,
email: user.email
});
});
}
}
saveOrUpdateUser = (e) => {
e.preventDefault();
let user = { firstName: this.state.firstName, lastName:
this.state.lastName, email: this.state.email };
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({ email: 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 : </label>
<input placeholder="Email Address"
name="email" className="form-control"
value={this.state.email}
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) => {
if(res.data==null)
{
this.props.history.push('/add-user/_add');
}
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 </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.email}</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


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 : </label>
<div> { this.state.user.email }
</div>
</div>
</div>

</div>
</div>
)
}
}

export default ViewUserComponent


FooterComponent.jsx

import React, { Component } from 'react'

class FooterComponent extends Component {
constructor(props) {
super(props)

this.state = {
}
}

render() {
return (
<div>
<footer className = "footer">
<span className="text-muted">
All Rights Reserved 2021
@Knowledgefactory.net</span>
</footer>
</div>
)
}
}

export default FooterComponent


HeaderComponent.js

import React, { Component } from 'react'
import 'bootstrap/dist/css/bootstrap.min.css';
class HeaderComponent extends Component {
constructor(props) {
super(props)

this.state = {

}
}

render() {
return (
<div>
<header>
<nav
className="navbar navbar-dark bg-primary">
<div>
<a href="/users"
className="navbar-brand">
User Management App
</a></div>
</nav>
</header>
</div>
)
}
}

export default HeaderComponent


UserService.js

import axios from 'axios';

const USER_API_BASE_URL = "http://localhost:9080/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()


App.css

.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.footer {
position: absolute;
bottom: 0;
width:100%;
height: 50px;
background-color: black;
text-align: center;
color: white;
}


App.js

import React from 'react';
import logo from './logo.svg';
import './App.css';
import {BrowserRouter as Router, Route, Switch}
from 'react-router-dom'
import ListUserComponent from './components/ListUserComponent';
import HeaderComponent from './components/HeaderComponent';
import FooterComponent from './components/FooterComponent';
import CreateUserComponent from './components/CreateUserComponent';
import ViewUserComponent from './components/ViewUserComponent';

function App() {
return (
<div>
<Router>
<HeaderComponent />
<div className="container">
<Switch>
<Route path = "/" exact component =
{ListUserComponent}></Route>
<Route path = "/users" component =
{ListUserComponent}></Route>
<Route path = "/add-user/:id" component =
{CreateUserComponent}></Route>
<Route path = "/view-user/:id" component =
{ViewUserComponent}></Route>
</Switch>
</div>
<FooterComponent />
</Router>
</div>
);
}

export default App;


index.css

body {
margin: 0;
font-family: -apple-system,
BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell',
'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro,
Menlo, Monaco, Consolas, 'Courier New',
monospace;
}


index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

serviceWorker.unregister();


index.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width,
initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon"
href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest"
href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript
to run this app.</noscript>
<div id="root"></div>
</body>
</html>


Frontend Local Setup

Step 1: The npm install installs all modules that are listed on package.json file and their
dependencies
npm install
Step 2: Run the Frontend application
npm start
 App running at:

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Spring boot video streaming example-HTML5

Spring Boot + Mockito simple application with 100% code coverage

Spring Boot + OpenCSV Export Data to CSV Example

Custom Exception Handling in Quarkus REST API

Registration and Login with Spring Boot + Spring Security + Thymeleaf

DataTable-Pagination example with Spring boot, jQuery and ajax

Spring Webflux File Download - REST API Example

Node JS mini projects with source code - free download

ReactJS, Spring Boot JWT Authentication Example