Vue.js + Node.js/Express RestAPIs – Mongoose ODM + MongoDB CRUD example

vue-nodejs-express-restapi-mongoose-mongodb---feature-image

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:

Vue-nodejs-express-restapi-mongoose-mongodb-full-stack-architecture

1. Node.js Server

vue-nodejs-express-restapi-mongoose-mongodb---nodejs-backend-mysql-architecture

2. Vue.js Client

vue-nodejs-express-restapi-mongoose-mongodb---front-vue.js

Practice

1. Node.js Backend

– Project structure:

vue-nodejs-express-restapi-mongoose-mongodb---nodejs-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

vue-nodejs-express-restapi-mongoose-mongodb---Vue-project-structure

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:

vue-nodejs-express-restapi-mongoose-mongodb---vue-cli

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
    }
  ]
});
App template with Navbar and router-view

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

vue-nodejs-express-restapi-mongoose-mongodb---add-customers

-> MongoDB’s documents:

vue-nodejs-express-restapi-mongoose-mongodb---add-customers-select

Search Customers

vue-nodejs-express-restapi-mongoose-mongodb---search-customers

Load All Customers

vue-nodejs-express-restapi-mongoose-mongodb---load-all-customers

-> Details a Customer:

vue-nodejs-express-restapi-mongoose-mongodb---details-customers

Update Customers

– Update Katherin customer from inactive to active ->

vue-nodejs-express-restapi-mongoose-mongodb---update-customers-front

vue-nodejs-express-restapi-mongoose-mongodb---update-customers

Delete Customer

vue-nodejs-express-restapi-mongoose-mongodb---delete-customers-front

– MongoDB’s records:

vue-nodejs-express-restapi-mongoose-mongodb---delete-customers

SourceCode

Nodejs-MongoDB
Vue.js-Client



By grokonez | December 28, 2018.

Last updated on May 3, 2021.



Related Posts


1 thought on “Vue.js + Node.js/Express RestAPIs – Mongoose ODM + MongoDB CRUD example”

Got Something To Say:

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

*