In this tutorial, we show you Vue.js Http Client & Node.js Server example that uses Mongoose ODM to do CRUD with MongoDB and Vue.js as a front-end technology to make request and receive response.
Related Posts:
– Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
– Vue Router example – with Nav Bar, Dynamic Route & Nested Routes
Technologies
– Node.js/Express
– Mongoose
– Vue 2.5.17
– Vue Router 3
– Axios 0.18.0
– MongoDB
Overview
This is full-stack Architecture:
1. Node.js Server
2. Vue.js Client
Practice
1. Node.js Backend
– Project structure:
Setting up Nodejs/Express project
Init package.json
by cmd:
npm init
Install express, mongoose & cors:
$npm install express cors body-parser mongoose --save
-> now package.json
file:
{ "name": "nodejs-mongodb", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [ "Nodejs", "Express", "Mongoose", "MongoDB", "RestAPIs", "CRUD" ], "author": "grokonez.com", "license": "ISC", "dependencies": { "cors": "^2.8.5", "express": "^4.16.4", "mongoose": "^5.4.1" } } |
Setting up MongoDB connection
– Create a file app/config/mongodb.config.js
as below content:
module.exports = { url: 'mongodb://localhost:27017/nodejs-demo' } |
Create Mongoose model
– ./app/models/customer.model.js
file:
const mongoose = require('mongoose'); const CustomerSchema = mongoose.Schema({ name: String, age: { type: Number, min: 18, max: 65, required: true }, active: { type: Boolean, default: false } }); module.exports = mongoose.model('Customer', CustomerSchema); |
Express RestAPIs
Route
– Define Customer’s routes in ./app/routes/customer.routes.js
file:
module.exports = function(app) { var customers = require('../controllers/customer.controller.js'); // Create a new Customer app.post('/api/customer', customers.create); // Retrieve all Customer app.get('/api/customers', customers.findAll); // Retrieve a single Customer by Id app.get('/api/customer/:customerId', customers.findOne); // Update a Customer with Id app.put('/api/customer/:customerId', customers.update); // Retrieve Customers Age app.get('/api/customers/age/:age', customers.findByAge); // Delete a Customer with Id app.delete('/api/customer/:customerId', customers.delete); } |
Controller
– Implement Customer’s controller in ./app/controllers/customer.controller.js
file:
const Customer = require('../models/customer.model.js'); // POST a Customer exports.create = (req, res) => { // Create a Customer const customer = new Customer({ name: req.body.name, age: req.body.age }); // Save a Customer in the MongoDB customer.save() .then(data => { res.send(data); }).catch(err => { res.status(500).send({ message: err.message }); }); }; // FETCH all Customers exports.findAll = (req, res) => { Customer.find() .then(customers => { res.send(customers); }).catch(err => { res.status(500).send({ message: err.message }); }); }; // FIND a Customer exports.findOne = (req, res) => { Customer.findById(req.params.customerId) .then(customer => { if(!customer) { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } res.send(customer); }).catch(err => { if(err.kind === 'ObjectId') { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } return res.status(500).send({ message: "Error retrieving Customer with id " + req.params.customerId }); }); }; exports.findByAge = (req, res) => { Customer.find({ age: req.params.age}) .then( customers => { res.send(customers) } ) .catch(err => { res.status(500).send("Error -> " + err); }) } // UPDATE a Customer exports.update = (req, res) => { // Find customer and update it Customer.findOneAndUpdate({ _id: req.params.customerId }, { name: req.body.name, age: req.body.age, active: req.body.active }, {new: true}) .then(customer => { if(!customer) { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } res.send(customer); }).catch(err => { if(err.kind === 'ObjectId') { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } return res.status(500).send({ message: "Error updating customer with id " + req.params.customerId }); }); }; // DELETE a Customer exports.delete = (req, res) => { Customer.findByIdAndRemove(req.params.customerId) .then(customer => { if(!customer) { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } res.send({message: "Customer deleted successfully!"}); }).catch(err => { if(err.kind === 'ObjectId' || err.name === 'NotFound') { return res.status(404).send({ message: "Customer not found with id " + req.params.customerId }); } return res.status(500).send({ message: "Could not delete customer with id " + req.params.customerId }); }); }; |
Server.js
– server.js
file:
var express = require('express'); var app = express(); var bodyParser = require('body-parser'); app.use(bodyParser.json()) const cors = require('cors') const corsOptions = { origin: 'http://localhost:4200', optionsSuccessStatus: 200 } app.use(cors(corsOptions)) // Configuring the database const dbConfig = require('./app/config/mongodb.config.js'); const mongoose = require('mongoose'); mongoose.Promise = global.Promise; // Connecting to the database mongoose.connect(dbConfig.url) .then(() => { console.log("Successfully connected to MongoDB."); }).catch(err => { console.log('Could not connect to MongoDB.'); process.exit(); }); require('./app/routes/customer.routes.js')(app); // Create a Server var server = app.listen(8080, function () { var host = server.address().address var port = server.address().port console.log("App listening at http://%s:%s", host, port) }) |
2. Vue 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
2.0 Setup Vue Project & Router
Init Project
Point cmd to the folder you want to save Project folder, run command:
vue create vue.js-client
You will see 2 options, choose default:
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 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 } ] }); |
src/App.vue:
<template> <div id="app" class="container-fluid"> <div class="site-info"> <h1>grokonez</h1> <h3>Vue Nodejs 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> |
2.1 Initialize 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", } }); |
2.2 Components
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> |
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> |
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> |
2.3 Configure Port for Vue App
vue.config.js
module.exports = { devServer: { port: 4200 } } |
Run
– Node.js Server: npm start
.
– Vue.js Client: npm run serve
.
Open Browser with Url: http://localhost:4200/
.
Add Customers
-> MongoDB’s documents:
Search Customers
Load All Customers
-> Details a Customer:
Update Customers
– Update Katherin customer from inactive
to active
->
Delete Customer
– MongoDB’s records:
SourceCode
– Nodejs-MongoDB
– Vue.js-Client
how to call api specially they aren’t in the same folder ?