Micronaut + Vue.js + MongoDB CRUD Example
After completing this tutorial what we will build?
Add User:
View All Users:
Update User:
We divided this tutorial into two parts.
PART 1 - Rest APIs Development using Micronaut
Project Directory:
Maven[pom.xml]
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.knf.dev.demo</groupId> <artifactId>micronaut-mongodb-crud</artifactId> <version>0.1</version> <packaging>${packaging}</packaging>
<parent> <groupId>io.micronaut</groupId> <artifactId>micronaut-parent</artifactId> <version>3.1.1</version> </parent>
<properties> <packaging>jar</packaging> <jdk.version>11</jdk.version> <release.version>11</release.version> <micronaut.version>3.1.1</micronaut.version> <exec.mainClass>com.knf.dev.demo.Application </exec.mainClass> <micronaut.runtime>netty</micronaut.runtime> </properties>
<repositories> <repository> <id>central</id> <url>https://repo.maven.apache.org/maven2</url> </repository> </repositories>
<dependencies> <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-inject</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-validation</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-http-server-netty</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-http-client</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.micronaut</groupId> <artifactId>micronaut-runtime</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>javax.annotation</groupId> <artifactId>javax.annotation-api</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.micronaut.mongodb</groupId> <artifactId>micronaut-mongo-sync</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.micronaut.test</groupId> <artifactId>micronaut-test-junit5</artifactId> <scope>test</scope> </dependency> </dependencies>
<build> <plugins> <plugin> <groupId>io.micronaut.build</groupId> <artifactId>micronaut-maven-plugin</artifactId> </plugin>
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <annotationProcessorPaths> <path> <groupId>io.micronaut</groupId> <artifactId>micronaut-inject-java</artifactId> <version>${micronaut.version}</version> </path> <path> <groupId>io.micronaut</groupId> <artifactId>micronaut-validation</artifactId> <version>${micronaut.version}</version> </path> </annotationProcessorPaths> <compilerArgs> <arg>-Amicronaut.processing.group=com.knf.dev.demo</arg> <arg>-Amicronaut.processing.module=micronaut-mongodb-crud</arg> </compilerArgs> </configuration> </plugin> </plugins> </build>
</project>
application.yaml
micronaut: application: name: micronautMongodbCrudmongodb.uri: mongodb://localhost:27017
micronaut: server: port: 8081 cors: enabled: true configurations: web: allowedOrigins: - http://localhost:8080
Create Document - User.java
package com.knf.dev.demo.entity;
import io.micronaut.core.annotation.Introspected;
@Introspectedpublic 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(); }}
Create UserRepository.java
package com.knf.dev.demo.repository.impl;
import java.util.ArrayList;import java.util.List;import java.util.Optional;import org.bson.Document;import org.bson.conversions.Bson;import org.bson.types.ObjectId;import com.knf.dev.demo.entity.User;import com.mongodb.client.MongoClient;import com.mongodb.client.MongoCollection;import com.mongodb.client.MongoCursor;import com.mongodb.client.model.Filters;import jakarta.inject.Singleton;
@Singletonpublic class UserRepository {
private final MongoClient mongoClient;
public UserRepository(MongoClient mongoClient) {
this.mongoClient = mongoClient; }
private MongoCollection<Document> getCollection() {
return mongoClient.getDatabase("user-test"). getCollection("user-test"); }
public User saveUser(User user) {
Document document = new Document(). append("firstName", user.getFirstName()) .append("lastName", user.getLastName()). append("emailId", user.getEmailId()); getCollection().insertOne(document); user.setId(document.getObjectId("_id").toString()); return user; }
public User updateUser(String id, User user) { Optional<Document> optionalDocument = findDocumentById(id); if (optionalDocument.isPresent()) { Document document = optionalDocument.get(); document.put("firstName", user.getFirstName()); document.put("lastName", user.getLastName()); document.put("emailId", user.getEmailId()); Bson bsonFilter = Filters.eq("_id", new ObjectId(id)); getCollection().replaceOne(bsonFilter, document); } return user; }
public List<User> findAll() {
List<User> users = new ArrayList<>(); MongoCursor<Document> cursor = getCollection(). find().iterator();
try { while (cursor.hasNext()) { Document document = cursor.next(); User user = new User(); user.setLastName(document.getString("lastName")); user.setFirstName(document.getString("firstName")); user.setEmailId(document.getString("emailId")); user.setId(String. valueOf(document.getObjectId("_id"))); users.add(user); } } finally { cursor.close(); } return users; }
public Optional<User> findUserById(String id) { return findDocumentById(id).map(this::mapUser); }
public void deleteUserById(String id) { Bson bsonFilter = Filters.eq("_id", new ObjectId(id)); getCollection().deleteOne(bsonFilter); }
private Optional<Document> findDocumentById(String id) { Bson bsonFilter = Filters.eq("_id", new ObjectId(id)); try (MongoCursor<Document> iterator = getCollection(). find(bsonFilter).iterator()) { return iterator.hasNext() ? Optional. of(iterator.next()) : Optional.empty(); } }
private User mapUser(Document document) { User user = new User(); user.setId(document.getObjectId("_id").toString()); user.setEmailId(document.getString("emailId")); user.setLastName(document.getString("lastName")); user.setFirstName(document.getString("firstName")); return user; }}
Create UserController.java
package com.knf.dev.demo.contoller;
import java.util.List;import java.util.Optional;import com.knf.dev.demo.entity.User;import com.knf.dev.demo.repository.impl.UserRepository;import io.micronaut.http.HttpResponse;import io.micronaut.http.annotation.Body;import io.micronaut.http.annotation.Controller;import io.micronaut.http.annotation.Delete;import io.micronaut.http.annotation.Get;import io.micronaut.http.annotation.Post;import io.micronaut.http.annotation.Put;import jakarta.inject.Inject;
@Controller("/api/v1/users")public class UserController {
@Inject UserRepository userRepository;
@Get public List<User> getUsers() {
return userRepository.findAll(); }
@Get("/{id}") public Optional<User> getUser(String id) {
return userRepository.findUserById(id); }
@Post public HttpResponse<User> addUser(@Body User user) {
userRepository.saveUser(user); return HttpResponse.created(user); }
@Put("/{id}") public HttpResponse<User> updateUser(String id, @Body User user) {
userRepository.updateUser(id, user); return HttpResponse.created(user); }
@Delete("/{id}") public HttpResponse<Void> delete(String id) {
userRepository.deleteUserById(id); return HttpResponse.noContent(); }}
Create Main Class
package com.knf.dev.demo;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) { Micronaut.run(Application.class, args); }}
Run the backend application
mvn mn:run or run as java application
PART 2 - UI Development using Vue.js
Package structure - Front end
package.json
{ "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 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("/users"); }); } else { UserDataService.updateUser(this.id, { id: this.id, firstName: this.firstName, lastName: this.lastName, emailId: this.emailId, }).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 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:8081/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="#"> Micronaut + Mongo DB + 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;
vue.config.js
module.exports = { devServer: { port: 8080 }}
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>Micronaut-vuejs-mongodb-crud</title></head>
<body> <noscript> <strong>We're sorry but Micronaut-vuejs-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>
Download Source code - Local environment setup
Step1: Install JDK 17 - click here
Step2: Install eclipse - click here
Step3: Install VueJS - click here
Step4: Install VS code - click here
Step5: Download/clone source code - click here
Step6: Install Apache Maven - click here
Step7: Install MongoDB - click here
Step1: Install JDK 17 - click here
Step2: Install eclipse - click here
Step3: Install VueJS - click here
Step4: Install VS code - click here
Step5: Download/clone source code - click here
Step6: Install Apache Maven - click here
Step7: Install MongoDB - click here
Backend setup
Step8: Execute the command - mvn clean install
Step9: Run the app as a Java application or execute mvn mn:run
Step8: Execute the command - mvn clean install
Step9: Run the app as a Java application or execute mvn mn:run
Frontend setup
Step10: The npm install installs all modules that are listed on package.json file and their dependencies - npm install
Step11: Run the Frontend application - npm run serve
Step10: The npm install installs all modules that are listed on package.json file and their dependencies - npm install
Step11: Run the Frontend application - npm run serve