Go Lang + Vue.js + Mysql CRUD example - Go Full stack development

Hello everyone, today we will learn how to build a full-stack application that is a basic User Management Application using Go Lang, Mysql, and Vue.js.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 

Following is the screenshot of our application -

-Fetch all Users:

-Add a User:

-Update User:

We divided this tutorial into two parts

PART 1 - Rest APIs Development using Go and Mysql
PART 2 - UI development using Vue.js


PART 1 - Rest APIs Development using Go Language

These are APIs that the Go Language application will export: 
  1. GET all User's        :     /users
  2. GET User by ID     :     /users/{_id}
  3. POST User             :     /users 
  4. PUT User               :     /users/{_id} 
  5. 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 Vue.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-vuejs",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"axios": "^0.18.0",
"vue": "^2.6.6",
"vue-router": "^3.0.2"
},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.5.0",
"@vue/cli-plugin-eslint": "^3.5.0",
"@vue/cli-service": "^3.5.0",
"babel-eslint": "^10.0.1",
"eslint": "^5.8.0",
"eslint-plugin-vue": "^5.0.0",
"vue-template-compiler": "^2.5.21"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"rules": {},
"parserOptions": {
"parser": "babel-eslint"
}
},
"postcss": {
"plugins": {
"autoprefixer": {}
}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

Components 

Vue Components are one of the important features of VueJS that creates custom elements, which can be reused in HTML

User.vue

<template>
<div>
<h3>User</h3>
<div class="container">
<form @submit="validateAndSubmit">
<div v-if="errors.length">
<div
class="alert alert-danger"
v-bind:key="index"
v-for="(error, index) in errors"
>
{{ error }}
</div>
</div>
<fieldset class="form-group">
<label>First Name</label>
<input type="text" class="form-control"
v-model="firstName" />
</fieldset>
<fieldset class="form-group">
<label>Last Name</label>
<input type="text" class="form-control"
v-model="lastName" />
</fieldset>
<fieldset class="form-group">
<label>Email</label>
<input type="text" class="form-control"
v-model="email" />
</fieldset>
<button class="btn btn-success"
type="submit">Save</button>
</form>
</div>
</div>
</template>
<script>
import UserDataService from "../service/UserDataService";

export default {
name: "User",
data() {
return {
firstName: "",
lastName: "",
email: "",
errors: [],
};
},
computed: {
id() {
return this.$route.params.id;
},
},
methods: {
refreshUserDetails() {
UserDataService.retrieveUser(this.id).then((res) => {
this.firstName = res.data.firstName;
this.lastName = res.data.lastName;
this.email = res.data.email;
});
},
validateAndSubmit(e) {
e.preventDefault();
this.errors = [];
if (!this.firstName) {
this.errors.push("Enter valid values");
} else if (this.firstName.length < 4) {
this.errors.push
("Enter atleast 4 characters in First Name");
}
if (!this.lastName) {
this.errors.push("Enter valid values");
} else if (this.lastName.length < 4) {
this.errors.push
("Enter atleast 4 characters in Last Name");
}
if (this.errors.length === 0) {
if (this.id == -1) {
UserDataService.createUser({
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
}).then(() => {
this.$router.push("/users");
});
} else {
UserDataService.updateUser(this.id, {
id: this.id,
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
}).then(() => {
this.$router.push("/users");
});
}
}
},
},
created() {
this.refreshUserDetails();
},
};
</script>

Users.vue

<template>
<div class="container">
<h3>All Users</h3>
<div v-if="message" class="alert alert-success">
{{ this.message }}</div>
<div class="container">
<table class="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Update</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr v-for="user in users" v-bind:key="user.id">
<td>{{ user.firstName }}</td>
<td>{{ user.lastName }}</td>
<td>{{ user.email }}</td>
<td>
<button class="btn btn-warning"
v-on:click="updateUser(user.id)">
Update
</button>
</td>
<td>
<button class="btn btn-danger"
v-on:click="deleteUser(user.id)">
Delete
</button>
</td>
</tr>
</tbody>
</table>
<div class="row">
<button class="btn btn-success"
v-on:click="addUser()">Add</button>
</div>
</div>
</div>
</template>
<script>
import UserDataService from "../service/UserDataService";

export default {
name: "Users",
data() {
return {
users: [],
message: "",
};
},
methods: {
refreshUsers() {
UserDataService.retrieveAllUsers().then((res) => {
this.users = res.data;
});
},
addUser() {
this.$router.push(`/user/-1`);
},
updateUser(id) {
this.$router.push(`/user/${id}`);
},
deleteUser(id) {
UserDataService.deleteUser(id).then(() => {
this.refreshUsers();
});
},
},
created() {
this.refreshUsers();
},
};
</script>

UserDataService.js

import axios from 'axios'

const USER_API_URL = 'http://localhost:9080'

class UserDataService {

retrieveAllUsers() {

return axios.get(`${USER_API_URL}/users`);
}

retrieveUser(id) {

return axios.get(`${USER_API_URL}/users/${id}`);
}

deleteUser(id) {

return axios.delete(`${USER_API_URL}/users/${id}`);
}

updateUser(id, user) {

return axios.put(`${USER_API_URL}/users/${id}`, user);
}

createUser(user) {

return axios.post(`${USER_API_URL}/users`, user);
}
}

export default new UserDataService()

App.vue

<template>
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">
Go Lang + Mysql + Vue.js CRUD Application
</a><br/><br/>
</div>
<router-view/>
</div>
</template>

<script>
export default {
name: "app"
};
</script>

<style>
@import
url(https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css);

</style>

routes.js

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

const router = new Router({
mode: 'history',
routes: [
{
path: "/",
name: "Users",
component: () => import("./components/Users"),
},
{
path: "/users",
name: "Users",
component: () => import("./components/Users"),
},
{
path: "/user/:id",
name: "User",
component: () => import("./components/User"),
},
]
});

export default router;

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './routes';

Vue.config.productionTip = false

new Vue({
router,
render: h => h(App),
}).$mount('#app')

index.html

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport"
content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>golang-vuejs-mysql</title>
</head>

<body>
<noscript>
<strong>We're sorry but golang-vuejs-mysql-
crud doesn't work properly without JavaScript
enabled. Please
enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</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 run serve
 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