Spring Boot 3 + Vue JS 3 + MongoDB CRUD Application Example
In this section, we will learn how to develop a full-stack web application that is a basic User Management Application using Spring Boot 3, MongoDB, and Vue 3. You could download the source code from our Github repository, the download link is provided at the end of this tutorial.
Download 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
We divided this tutorial into two parts.
PART 1 - Restful API Development with Spring Boot 3 & MongoDB.
PART 2 - UI development using Vue JS 3.
PART 1 - Restful API Development with Spring Boot 3
These are APIs that Spring backend App will export:
Backend project directory:
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.
<?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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.knf.dev.demo</groupId>
<artifactId>spring-boot-crud-application</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-boot-crud-application</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Configuring MongoDB Database
Go to your MongoDB Compass and create a Database named userdb and inside the database create a collection named user as seen in the below image.
Creating User Document
package com.knf.dev.demo.crudapplication.model;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collection = "user")
public class User {
private String _id;
private String firstName;
private String lastName;
private String emailId;
public String getId() {
return _id;
}
public void setId(String _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(String _id, String firstName, String lastName,
String emailId) {
super();
this._id = _id;
this.firstName = firstName;
this.lastName = lastName;
this.emailId = emailId;
}
public User() {
super();
}
}
Creating UserRepository
package com.knf.dev.demo.crudapplication.repository;
import com.knf.dev.demo.crudapplication.model.User;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends MongoRepository<User, String> {
}
Create CustomErrorResponse
package com.knf.dev.demo.crudapplication.exception;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.time.LocalDateTime;
public class CustomErrorResponse {
@JsonFormat(shape = JsonFormat.Shape.STRING,
pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime timestamp;
private int status;
private String error;
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
}
Create ResourceNotFoundException
package com.knf.dev.demo.crudapplication.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
private static final long serialVersionUID = 1L;
public ResourceNotFoundException(String message) {
super(message);
}
}
Create GlobalExceptionHandler
package com.knf.dev.demo.crudapplication.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import java.time.LocalDateTime;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<CustomErrorResponse> globalExceptionHandler
(Exception ex, WebRequest request) {
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
}
Create UserController
package com.knf.dev.demo.crudapplication.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.knf.dev.demo.crudapplication.model.User;
import com.knf.dev.demo.crudapplication.exception.ResourceNotFoundException;
import com.knf.dev.demo.crudapplication.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "*")
@RestController
@RequestMapping("/api/v1")
public class UserController {
@Autowired
private UserRepository userRepository;
@GetMapping("/users")
public List<User> getAllUsers() {
return userRepository.findAll();
}
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable(value = "id")
String id) throws ResourceNotFoundException {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not found for this id :: " + id));
return ResponseEntity.ok().body(user);
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
@PutMapping("/users/{id}")
public ResponseEntity<User> updateUser(@PathVariable(value = "id")
String id, @RequestBody User userDto)
throws ResourceNotFoundException {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not found for this id :: " + id));
user.setEmailId(userDto.getEmailId());
user.setLastName(userDto.getLastName());
user.setFirstName(userDto.getFirstName());
user.setId(id);
final User updateUser = userRepository.save(user);
return ResponseEntity.ok(updateUser);
}
@DeleteMapping("/users/{id}")
public Map<String, Boolean> deleteUser(@PathVariable(value = "id")
String id) throws ResourceNotFoundException {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException
("User not found for this id :: " + id));
userRepository.delete(user);
Map<String, Boolean> response = new HashMap<>();
response.put("deleted", Boolean.TRUE);
return response;
}
}
Main Driver
package com.knf.dev.demo.crudapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CrudApplication {
public static void main(String[] args) {
SpringApplication.run(CrudApplication.class, args);
}
}
Run the application and verify REST APIs
Step 1: mvn clean install
Step 2: Run the Spring Boot application - mvn spring-boot:run
Add User:
PART 2 - UI development using Vue JS 3
Frontend Project Directory
package.json
{ "name": "vue-3-crud-application", "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.21.1", "bootstrap": "^4.6.0", "core-js": "^3.6.5", "jquery": "^3.6.0", "popper.js": "^1.16.1", "vue": "^3.0.0", "vue-router": "^4.0.6" }, "devDependencies": { "@vue/cli-plugin-babel": "^5.0.8", "@vue/cli-plugin-eslint": "^5.0.8", "@vue/cli-service": "^5.0.8", "@vue/compiler-sfc": "^3.0.0", "babel-eslint": "^10.1.0", "eslint": "^7.32.0", "eslint-plugin-vue": "^7.0.0" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/vue3-essential", "eslint:recommended" ], "parserOptions": { "parser": "babel-eslint" }, "rules": {} }, "browserslist": [ "> 1%", "last 2 versions", "not dead" ]}
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 Id</label> <input type="text" class="form-control" v-model="emailId" /> </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: "", emailId: "", 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.emailId = res.data.emailId; }); }, validateAndSubmit(e) { e.preventDefault(); this.errors = []; if (!this.firstName) { this.errors.push("Enter valid values"); } else if (this.firstName.length < 5) { this.errors.push("Enter atleast 5 characters in First Name"); } if (!this.lastName) { this.errors.push("Enter valid values"); } else if (this.lastName.length < 5) { this.errors.push("Enter atleast 5 characters in Last Name"); } if (this.errors.length === 0) { if (this.id == -1) { UserDataService.createUser({ firstName: this.firstName, lastName: this.lastName, emailId: this.emailId, }).then(() => { this.$router.push("/"); }); } else { UserDataService.updateUser(this.id, { id: this.id, firstName: this.firstName, lastName: this.lastName, emailId: this.emailId, }).then(() => { this.$router.push("/"); }); } } }, }, 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 Id</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.emailId }}</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:8080/api/v1'
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="#" >Spring Boot 3+ Mongo DB + Vue 3 CRUD Application</a ><br /><br /> </div> <router-view /> </div></template>
<script>export default { name: "app",};</script>
router.js
import { createWebHistory, createRouter } from "vue-router";
const routes = [ { path: "/", name: "Users", component: () => import("./components/Users"), }, { path: "/user/:id", name: "User", component: () => import("./components/User"), },];
const router = createRouter({ history: createWebHistory(), routes,});
export default router;
main.js
import { createApp } from 'vue'import App from './App.vue'import 'bootstrap'import 'bootstrap/dist/css/bootstrap.min.css'import router from './router'
createApp(App).use(router).mount('#app')
Local Setup and Run the application
Step1: Download or clone the source code from GitHub to the local machine - Click here
Backend
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Frontend
Step 4: npm install
Step 5: npm run serve -- --port 8082
Step1: Download or clone the source code from GitHub to the local machine - Click here
Backend
Step 2: mvn clean install
Step 3: Run the Spring Boot application - mvn spring-boot:run
Frontend
Step 4: npm install
Step 5: npm run serve -- --port 8082