Spring Boot + Vue.js example | Spring Data MongoDB + RestApi CRUD

spring-boot-vue-example-spring-data-rest-mongodb-crud-example-feature-image-n

[no_toc]In this Spring Boot Vue.js tutorial, we show you Vue.js Http Client & Spring Boot Server example that uses Spring Data to do CRUD with MongoDB and Vue.js as a front-end technology to make request and receive response.

Related Posts:
Spring MongoOperations to access MongoDB
How to use SpringData MongoRepository to interact with MongoDB
How to build SpringBoot MongoDb RestfulApi

Vue Router example – with Nav Bar, Dynamic Route & Nested Routes

Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.4.RELEASE
– Spring Boot: 2.0.5.RELEASE

– Vue 2.5.17
– Vue Router 3
– Axios 0.18.0

Overview

This is full-stack Architecture:

spring-boot-vue-example-spring-data-mongodb-rest-api-mongodb-architecture

Demo

Spring Boot Server

spring-boot-vue-example-spring-data-mongodb-rest-api-mongodb-architecture-server

Vue.js Client

spring-boot-vue-example-spring-data-mongodb-rest-api-mongodb-client

Spring Boot Server

spring-boot-vue-example-spring-data-mongodb-rest-api-mongodb-project-structure-server

Customer class corresponds to entity and table customer.
CustomerRepository is an interface extends MongoRepository, will be autowired in CustomerController for implementing repository methods and custom finder methods.
CustomerController is a REST Controller which has request mapping methods for RESTful requests such as: getAllCustomers, postCustomer, deleteCustomer, findByAge, updateCustomer.
– Configuration for Spring Datasource and Spring Data properties in application.properties
Dependencies for Spring Boot and MongoDb in pom.xml

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-web</artifactId>
</dependency>

Data Model

model/Customer.java


package com.ozenero.spring.restapi.mongodb.model;

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

@Document(collection = "customer")
public class Customer {
	@Id
	private String id;

	private String name;
	private int age;
	private boolean active;

	public Customer() {
	}

	public Customer(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getId() {
		return id;
	}

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

	public String getName() {
		return this.name;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getAge() {
		return this.age;
	}

	public boolean isActive() {
		return active;
	}

	public void setActive(boolean active) {
		this.active = active;
	}

	@Override
	public String toString() {
		return "Customer [id=" + id + ", name=" + name + ", age=" + age + ", active=" + active + "]";
	}
}

SpringJPA Repository

repo/CustomerRepository.java


package com.ozenero.spring.restapi.mongodb.repo;

import java.util.List;

import org.springframework.data.mongodb.repository.MongoRepository;

import com.ozenero.spring.restapi.mongodb.model.Customer;

public interface CustomerRepository extends MongoRepository{
	List findByAge(int age);
}

SpringBoot REST Controller

controller/CustomerController.java


package com.ozenero.spring.restapi.mongodb.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
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;

import com.ozenero.spring.restapi.mongodb.model.Customer;
import com.ozenero.spring.restapi.mongodb.repo.CustomerRepository;

@CrossOrigin(origins = "http://localhost:4200")
@RestController
@RequestMapping("/api")
public class CustomerController {

	@Autowired
	CustomerRepository repository;

	@GetMapping("/customers")
	public List getAllCustomers() {
		System.out.println("Get all Customers...");

		List customers = new ArrayList<>();
		repository.findAll().forEach(customers::add);

		return customers;
	}

	@PostMapping("/customer")
	public Customer postCustomer(@RequestBody Customer customer) {

		Customer _customer = repository.save(new Customer(customer.getName(), customer.getAge()));
		return _customer;
	}

	@DeleteMapping("/customer/{id}")
	public ResponseEntity deleteCustomer(@PathVariable("id") String id) {
		System.out.println("Delete Customer with ID = " + id + "...");

		repository.deleteById(id);

		return new ResponseEntity<>("Customer has been deleted!", HttpStatus.OK);
	}

	@GetMapping("customers/age/{age}")
	public List findByAge(@PathVariable int age) {

		List customers = repository.findByAge(age);
		return customers;
	}

	@PutMapping("/customer/{id}")
	public ResponseEntity updateCustomer(@PathVariable("id") String id, @RequestBody Customer customer) {
		System.out.println("Update Customer with ID = " + id + "...");

		Optional customerData = repository.findById(id);

		if (customerData.isPresent()) {
			Customer _customer = customerData.get();
			_customer.setName(customer.getName());
			_customer.setAge(customer.getAge());
			_customer.setActive(customer.isActive());
			return new ResponseEntity<>(repository.save(_customer), HttpStatus.OK);
		} else {
			return new ResponseEntity<>(HttpStatus.NOT_FOUND);
		}
	}
}

Configuration for Spring Datasource & Data MongoDb properties

application.properties


spring.data.mongodb.database=jsa_mongodb
spring.data.mongodb.port=27017

Vue.js Client

spring-boot-vue-example-spring-data-mongodb-rest-api-mongodb-project-structure-client

package.json with 3 main modules: vue, vue-router, axios.
– 4 components: CustomersList, Customer, AddCustomer, SearchCustomer.
router.js defines routes, each route has a path and maps to a component.
http-common.js initializes HTTP Client with baseUrl and headers for axios HTTP methods.
vue.config.js configures port for Vue App.

For more details about how to use Vue Router in this example, please visit:
Vue Router example – with Nav Bar, Dynamic Route & Nested Routes

Init Vue Project

Point cmd to the folder you want to save Project folder, run command:
vue create vue-springboot

You will see 2 options, choose default:

vue-create-project-config

Add Vue Router to Project

– Run command: npm install vue-router.
– Import router to src/main.js:

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

Vue.config.productionTip = false;

new Vue({
  router, // inject the router to make whole app router-aware
  render: h => h(App)
}).$mount("#app");

Define Vue Routes

src/router.js:

import Vue from "vue";
import Router from "vue-router";
import CustomersList from "./components/CustomersList.vue";
import AddCustomer from "./components/AddCustomer.vue";
import SearchCustomers from "./components/SearchCustomers.vue";
import Customer from "./components/Customer.vue";

Vue.use(Router);

export default new Router({
  mode: "history",
  routes: [
    {
      path: "/",
      name: "customers",
      alias: "/customer",
      component: CustomersList,
      children: [
        {
          path: "/customer/:id",
          name: "customer-details",
          component: Customer,
          props: true
        }
      ]
    },
    {
      path: "/add",
      name: "add",
      component: AddCustomer
    },
    {
      path: "/search",
      name: "search",
      component: SearchCustomers
    }
  ]
});

App template with Navbar and router-view

src/App.vue:

<template>
    <div id="app" class="container-fluid">
        <div class="site-info">
            <h1>ozenero</h1>
            <h3>Vue SpringBoot example</h3>
        </div>
        <nav>
            <router-link class="btn btn-primary" to="/">Customers</router-link>
            <router-link class="btn btn-primary" to="/add">Add</router-link>
            <router-link class="btn btn-primary" to="/search">Search</router-link>
        </nav>
        <br/>
        <router-view/>
    </div>
</template>

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

<style>
.site-info {
  color: blue;
  margin-bottom: 20px;
}

.btn-primary {
  margin-right: 5px;
}

.container-fluid {
  text-align: center;
}
</style>

Initialize Vue HTTP Client

Install axios with command: npm install axios.
Then create http-common.js file:

import axios from "axios";

export default axios.create({
  baseURL: "http://localhost:8080/api",
  headers: {
    "Content-type": "application/json",
  }
});

Vuejs List of Items

components/CustomersList.vue

<template>
    <div class="list row">
        <div class="col-md-6">
            <h4>Customers List</h4>
            <ul>
                <li v-for="(customer, index) in customers" :key="index">
                    <router-link :to="{
                            name: 'customer-details',
                            params: { customer: customer, id: customer.id }
                        }">
                            {{customer.name}}
                    </router-link>
                </li>
            </ul>
        </div>
        <div class="col-md-6">
            <router-view @refreshData="refreshList"></router-view>
        </div>
    </div>
</template>

<script>
import http from "../http-common";

export default {
  name: "customers-list",
  data() {
    return {
      customers: []
    };
  },
  methods: {
    /* eslint-disable no-console */
    retrieveCustomers() {
      http
        .get("/customers")
        .then(response => {
          this.customers = response.data; // JSON are parsed automatically.
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },
    refreshList() {
      this.retrieveCustomers();
    }
    /* eslint-enable no-console */
  },
  mounted() {
    this.retrieveCustomers();
  }
};
</script>

<style>
.list {
  text-align: left;
  max-width: 450px;
  margin: auto;
}
</style>

Vue Item Details

components/Customer.vue

<template>
  <div v-if="this.customer">
    <h4>Customer</h4>
    <div>
      <label>Name: </label> {{this.customer.name}}
    </div>
    <div>
      <label>Age: </label> {{this.customer.age}}
    </div>
    <div>
      <label>Active: </label> {{this.customer.active}}
    </div>
  
    <span v-if="this.customer.active"
      v-on:click="updateActive(false)"
      class="button is-small btn-primary">Inactive</span>
    <span v-else
      v-on:click="updateActive(true)"
      class="button is-small btn-primary">Active</span>
  
    <span class="button is-small btn-danger" v-on:click="deleteCustomer()">Delete</span>
  </div>
  <div v-else>
    <br/>
    <p>Please click on a Customer...</p>
  </div>
</template>

<script>
import http from "../http-common";

export default {
  name: "customer",
  props: ["customer"],
  methods: {
    /* eslint-disable no-console */
    updateActive(status) {
      var data = {
        id: this.customer.id,
        name: this.customer.name,
        age: this.customer.age,
        active: status
      };

      http
        .put("/customer/" + this.customer.id, data)
        .then(response => {
          this.customer.active = response.data.active;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    },
    deleteCustomer() {
      http
        .delete("/customer/" + this.customer.id)
        .then(response => {
          console.log(response.data);
          this.$emit("refreshData");
          this.$router.push('/');
        })
        .catch(e => {
          console.log(e);
        });
    }
    /* eslint-enable no-console */
  }
};
</script>

Vue Add Item

components/AddCustomer.vue

<template>
  <div class="submitform">
    <div v-if="!submitted">
        <div class="form-group">
          <label for="name">Name</label>
          <input type="text" class="form-control" id="name" required v-model="customer.name" name="name">
        </div>
    
        <div class="form-group">
          <label for="age">Age</label>
          <input type="number" class="form-control" id="age" required v-model="customer.age" name="age">
        </div>
    
        <button v-on:click="saveCustomer" class="btn btn-success">Submit</button>
    </div>
    
    <div v-else>
      <h4>You submitted successfully!</h4>
      <button class="btn btn-success" v-on:click="newCustomer">Add</button>
    </div>
  </div>
</template>

<script>
import http from "../http-common";

export default {
  name: "add-customer",
  data() {
    return {
      customer: {
        id: 0,
        name: "",
        age: 0,
        active: false
      },
      submitted: false
    };
  },
  methods: {
    /* eslint-disable no-console */
    saveCustomer() {
      var data = {
        name: this.customer.name,
        age: this.customer.age
      };

      http
        .post("/customer", data)
        .then(response => {
          this.customer.id = response.data.id;
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });

      this.submitted = true;
    },
    newCustomer() {
      this.submitted = false;
      this.customer = {};
    }
    /* eslint-enable no-console */
  }
};
</script>

<style>
.submitform {
  max-width: 300px;
  margin: auto;
}
</style>

Search Items

components/SearchCustomers.vue


<template>
  <div class="searchform">
    <h4>Find by Age</h4>
    <div class="form-group">
      <input type="number" class="form-control" id="age" required v-model="age" name="age">
    </div>
 
    <div class="btn-group">
      <button v-on:click="searchCustomers" class="btn btn-success">Search</button>
    </div>

    <ul class="search-result">
      <li v-for="(customer, index) in customers" :key="index">
        <h6>{{customer.name}} ({{customer.age}})</h6>
      </li>
    </ul>
  </div>
</template>

<script>
import http from "../http-common";

export default {
  name: "search-customer",
  data() {
    return {
      age: 0,
      customers: []
    };
  },
  methods: {
    /* eslint-disable no-console */
    searchCustomers() {
      http
        .get("/customers/age/" + this.age)
        .then(response => {
          this.customers = response.data; // JSON are parsed automatically.
          console.log(response.data);
        })
        .catch(e => {
          console.log(e);
        });
    }
    /* eslint-enable no-console */
  }
};
</script>

<style>
.searchform {
  max-width: 300px;
  margin: auto;
}
.search-result {
  margin-top: 20px;
  text-align: left;
}
</style>

Configure Port for Vue App

vue.config.js


module.exports = {
  devServer: {
    port: 4200
  }
}

Run

– Spring Boot Server: mvn clean install and mvn spring-boot:run.
– Vue.js Client: npm run serve.

Open Browser with Url: http://localhost:4200/.

Source Code

SpringBootRestMongoDB
vue-springboot

43 thoughts on “Spring Boot + Vue.js example | Spring Data MongoDB + RestApi CRUD”

  1. Hi Grokonez,
    When I issued a command npm create , it did not do anything , instead it prompted me “Didi you mean this? : Update”. was there a typos error ?

    1. Never mind, I got it after dealing some configuration issue on Windows 10.
      instead of “npm create vue-springboot” , it should be “vue create vue-springboot”.

  2. Magnificent items from you, man. I have have in mind your stuff previous to and you’re simply too great. I actually like what you’ve received right here, really like what you are stating and the best way wherein you say it. You are making it enjoyable and you continue to care for to stay it smart. I cant wait to learn much more from you. That is really a great site.

  3. Aw, the labyrinth was an especially good post. With presumed I must make a note of something like this similarly ; slacking and simply real perform produce a solid article… anyhow assist with While i say… A person put things a decent amount never ever rrn any way apparently go written.

  4. My brother suggested I would possibly like this blog.
    He was entirely right. This publish actually made my day.

    You can not consider simply how so much time I had
    spent for this info! Thanks!

  5. At this time it seems like Movable Type is the preferred blogging platform available right now.
    (from what I’ve read) Is that what you’re using on your blog?

  6. Asking questions are truly fastidious thing if
    you are not understanding anything entirely, however this
    post gives pleasant understanding yet.

  7. whoah this blog is wonderful i really like reading your posts.

    Keep up the great work! You know, many individuals are hunting around for this info, you can help them
    greatly.

  8. Fantastic site. Lots of useful information here. I’m sending
    it to a few buddies ans additionally sharing in delicious.
    And naturally, thank you for your effort!

  9. This design is incredible! You obviously know how to keep a reader
    amused. Between your wit and your videos, I was almost
    moved to start my own blog (well, almost…HaHa!) Wonderful job.
    I really loved what you had to say, and more than that, how you presented it.
    Too cool!

  10. Everything is very open with a precise explanation of the challenges.
    It was really informative. Your website is useful. Thanks
    for sharing!

  11. I really like your blog.. very nice colors & theme. Did you design this website
    yourself or did you hire someone to do it for you? Plz respond as I’m looking to create my own blog and would
    like to find out where u got this from. kudos

  12. I always used to study paragraph in news papers but now as I am a user
    of web thus from now I am using net for articles or reviews, thanks to web.

  13. Right here is the right webpage for everyone who really wants
    to find out about this topic. You understand so much its almost tough to
    argue with you (not that I really will need to…HaHa). You definitely put a new spin on a topic which has been written about for years.

    Wonderful stuff, just wonderful!

  14. Hi there everyone, it’s my first visit at this web page, and
    article is really fruitful in favor of me, keep up posting these articles.

  15. I will right away grab your rss as I can not to find your e-mail subscription link or e-newsletter service.

    Do you’ve any? Kindly allow me realize so that I may just subscribe.
    Thanks.

  16. I all the time used to study post in news papers but now as I
    am a user of net so from now I am using net for articles or
    reviews, thanks to web.

  17. Thanks for sharing your info. I really appreciate your efforts
    and I will be waiting for your next post thanks once again.

  18. Woah! I’m really digging the template/theme of this blog.

    It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between usability and visual appeal.
    I must say you’ve done a excellent job with this.

    In addition, the blog loads super fast for me on Chrome.
    Exceptional Blog!

  19. hello there and thank you for your information – I have certainly picked up something new from right here.
    I did however expertise several technical points using this site,
    since I experienced to reload the website a lot of times previous to I could get it to load correctly.
    I had been wondering if your hosting is OK? Not that I am complaining, but slow loading instances times will sometimes affect your placement in google and can damage your high-quality score if advertising and marketing with Adwords.
    Anyway I am adding this RSS to my email and can look out
    for a lot more of your respective fascinating content.

    Make sure you update this again very soon.

  20. Hey there! This post couldn’t be written any better!
    Reading this post reminds me of my old room mate!
    He always kept talking about this. I will forward this write-up to him.

    Pretty sure he will have a good read. Thank you for sharing!

  21. Excellent article. Keep writing such kind
    of information on your site. Im really impressed by your site.

    Hello there, You’ve performed an excellent job. I’ll definitely digg
    it and in my opinion recommend to my friends. I am confident they will be benefited from this web site.

  22. Write more, thats all I have to say. Literally, it seems as though you
    relied on the video to make your point. You obviously
    know what youre talking about, why waste your intelligence
    on just posting videos to your weblog when you could be giving us something informative to read?

  23. A person necessarily help to make significantly posts I’d state.

    That is the first time I frequented your web page and thus far?
    I amazed with the analysis you made to make this
    particular post amazing. Fantastic job!

  24. always i used to read smaller posts that as well clear their
    motive, and that is also happening with this article which I am reading here.

  25. Thanks , I have recently been looking for info approximately
    this topic for ages and yours is the best I have found out so far.
    However, what concerning the conclusion? Are you sure concerning
    the source?

  26. What’s up, the whole thing is going perfectly here and ofcourse every one is sharing facts, that’s in fact good,
    keep up writing.

  27. Hi exceptional website! Does running a blog like this require a massive amount work?
    I have no understanding of computer programming however I was hoping to
    start my own blog soon. Anyway, if you have any recommendations or
    techniques for new blog owners please share. I know this is off topic however I simply had to ask.
    Kudos!

Leave a Reply

Your email address will not be published. Required fields are marked *