Vue 2|3 MEVN Stack Tutorial – Build Full Stack Vue.js CRUD App

Last updated on by Digamber
This is a step by step MEVN stack tutorial, in this tutorial, we are going to learn how to create MEVN stack app. (MongoDB, Express.js, Vue.js, Node.js).

Build MEVN Stack CRUD Application

I will show you how to create a Full-stack single page application with Vue from scratch.

We will create a simple yet best Student Record Management system. This system efficiently allows users to perform CRUD (CREATE, READ, UPDATE & DELETE) operations.

We will create our server using Node and Express.js and store student records. We will use MongoDB. We will manage the front-end of the application with Vue.js.

So, let us start coding the MEVN Stack app with a practical example.

Create a New Vue Project

To install Vue project, we must have Vue CLI installed on our development system.

Run the following command to install Vue CLI:

# npm
npm install -g @vue/cli

# yarn
yarn global add @vue/cli

Use the following command to install the vue project.

vue create vue-mevn-stack-app

Head over to vue project:

cd vue-mevn-stack-app

Run command to start the app on the browser:

npm run serve

Adding Bootstrap in Vue 2

Let us run the below command to install the Bootstrap 4 UI Framework.

npm install bootstrap

# or

yarn add bootstrap

Import the Bootstrap framework path inside the main.js file. Now, you can use Bootstrap UI components in your vue app.

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

import 'bootstrap/dist/css/bootstrap.min.css'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

Build Vue Components

Head over to src/components directory, here we have to create the following components. These components will handle the data in our full-stack Vue.Js application.

  • CreateComponent.vue
  • EditComponent.vue
  • ListComponent.vue

Open src/CreateComponent.vue file and add the following code inside of it.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <!-- Content goes here -->
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
            }
        }
    }
</script>
</div>

Open src/EditComponent.vue file and place code inside of it.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <!-- Update Student content -->
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
            }
        }
    }
</script>

Open src/ListComponent.vue file and add the below code in it.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <!-- Display Student List -->
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
            }
        }
    }
</script>

Enable Vue Router

Open src/router/index.js and replace the existing code with the following code.

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'home',
    component: () => import('../components/CreateComponent')
  },
  {
    path: '/view',
    name: 'view',
    component: () => import('../components/ListComponent')
  },
  {
    path: '/edit/:id',
    name: 'edit',
    component: () => import('../components/EditComponent')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

Setting Up Navigation with Bootstrap and Router View in Vue

Go to src/App.vue file, here we define the Bootstrap Navigation component, router-view directive and the router-link directive.

The <router-link> is the component for facilitating user navigation in a router-enabled vue app.

The <router-view> component is the main component that is responsible for rendering the matched component for the provided path.

<template>
  <div>
    <!-- Nav bar -->
    <nav class="navbar navbar-dark bg-primary justify-content-between flex-nowrap flex-row">
      <div class="container">
        <a class="navbar-brand float-left">MEVN Stack Example</a>
        <ul class="nav navbar-nav flex-row float-right">
          <li class="nav-item">
            <router-link class="nav-link pr-3" to="/">Create Student</router-link>
          </li>
          <li class="nav-item">
            <router-link class="nav-link" to="/view">View Students</router-link>
          </li>
        </ul>
      </div>
    </nav>

    <!-- Router view -->
    <div class="container mt-5">
      <router-view></router-view>
    </div>
  </div>
</template>

Add Axios in Vue.js 2 to Handle HTTP Requests

Run command to install Axios:

npm install axios

# or

yarn add axios

Axios is a promise based HTTP client for the browser and node.js, You can check out our detailed article on: Handling AJAX Requests in Vue.js 2+ with Axios & fetch API

Build Form in Vue with Bootstrap 4

We require to store the data in the MEVN stack app, so we need to build a vue form using Bootstrap Form component.

Check out our previous tutorial detailed tutorial on – Form Validation in Vue with Vuelidate.

Open components/CreateComponent.vue file, then place the following code in it.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <h3 class="text-center">Create Student</h3>
            <form @submit.prevent="handleSubmitForm">
                <div class="form-group">
                    <label>Name</label>
                    <input type="text" class="form-control" v-model="student.name" required>
                </div>

                <div class="form-group">
                    <label>Email</label>
                    <input type="email" class="form-control" v-model="student.email" required>
                </div>

                <div class="form-group">
                    <label>Phone</label>
                    <input type="text" class="form-control" v-model="student.phone" required>
                </div>

                <div class="form-group">
                    <button class="btn btn-danger btn-block">Create</button>
                </div>
            </form>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                student: {
                   name: '',
                   email: '',
                   phone: ''
                }
            }
        },
        methods: {
            handleSubmitForm() { }
        }
    }
</script>

We created a basic form with name, email and phone number field. We created a beautiful form using Bootstrap form component.

The student object works with two-way data binding approach; it merely means that any data-related changes affecting the model are immediately propagated to the matching view.

Here is the form which you can check in the browser.

Build Form in Vue with Bootstrap 4

Setting up Node Server Environment

Now, we need to create REST APIs using Node + Express & MongoDB in Vue application. Create backend folder at the root of Vue project.

mkdir backend && cd backend

Generate separate package.json for node server.

npm init

Run command to install the following dependencies for Node/Express js.

npm i body-parser cors express mongoose 

Also, install a nodemon server as a development dependency. So that we do not need to restart every time, we change our server code.

Install nodemon as a development dependency, It automates the server starting process.

npm install nodemon

# or

yarn add nodemon

Configure & Start MongoDB Database

Create backend/database.js file at the root of your node application. Next, add the given code in it.

module.exports = {
   db: 'mongodb://localhost:27017/vuecrudmevn'
}

In this tutorial, we are working with MongoDB to store students data, so you must set up MongoDB on your development system.

You can follow this tutorial for the installation guidance at Install MongoDB on macOS, Linux and Windows.

Open the terminal window and run the following command to start the MongoDB on your local machine.

mongod --config /usr/local/etc/mongod.conf

brew services start mongodb-community@4.2

mongo

Create Mongoose Model

Create models/Student.js and paste the below code inside the file. We will define name, email and phone values inside the Student model.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

let studentSchema = new Schema({
  name: {
    type: String
  },
  email: {
    type: String
  },
  phone: {
    type: Number
  },
}, {
  collection: 'students'
})

module.exports = mongoose.model('Student', studentSchema)

Create Route in Node/Express App

Create a backend/routes directory; here, we have to create student.route.js file and place all the given below code inside of it.

const express = require('express');
const studentRoute = express.Router();

// Student model
let StudentModel = require('../models/Student');

studentRoute.route('/').get((req, res) => {
    StudentModel.find((error, data) => {
     if (error) {
       return next(error)
     } else {
       res.json(data)
     }
   })
 })

 studentRoute.route('/create-student').post((req, res, next) => {
    StudentModel.create(req.body, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
});

studentRoute.route('/edit-student/:id').get((req, res) => {
   StudentModel.findById(req.params.id, (error, data) => {
    if (error) {
      return next(error)
    } else {
      res.json(data)
    }
  })
})

// Update student
studentRoute.route('/update-student/:id').post((req, res, next) => {
  StudentModel.findByIdAndUpdate(req.params.id, {
    $set: req.body
  }, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.json(data)
      console.log('Student successfully updated!')
    }
  })
})

// Delete student
studentRoute.route('/delete-student/:id').delete((req, res, next) => {
  StudentModel.findByIdAndRemove(req.params.id, (error, data) => {
    if (error) {
      return next(error);
    } else {
      res.status(200).json({
        msg: data
      })
    }
  })
})

module.exports = studentRoute;

We defined the routes these routes will communicate with the server using the Axios library. We can perform CRUD operation using the GET, POST, UPDATE & DELETE methods.

Create the backend/app.js file and place the following code that contains the Node server settings.

let express = require('express'),
  cors = require('cors'),
  mongoose = require('mongoose'),
  database = require('./database'),
  bodyParser = require('body-parser');

// Connect mongoDB
mongoose.Promise = global.Promise;
mongoose.connect(database.db, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}).then(() => {
    console.log("Database connected")
  },
  error => {
    console.log("Database could't be connected to: " + error)
  }
)

const studentAPI = require('../backend/routes/student.route')
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
  extended: false
}));
app.use(cors());

// API
app.use('/api', studentAPI)

// Create port
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
  console.log('Connected to port ' + port)
})

// Find 404
app.use((req, res, next) => {
  next(createError(404));
});

// error handler
app.use(function (err, req, res, next) {
  console.error(err.message);
  if (!err.statusCode) err.statusCode = 500;
  res.status(err.statusCode).send(err.message);
});

Also, don’t forget to register the app.js value inside the “main” property in package.json file.

{
  "name": "vue-mevn-stack",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "mongoose": "^5.9.10"
  },
  "devDependencies": {
    "nodemon": "^2.0.3"
  }
}

Start Node/Express App

We have built following API using Node and Express in Vue.js.

MethodAPI
GEThttp://localhost:4000/api
POST/api/create-student
GET/api/edit-student/:id
POST/api/update-student/:id
DELETE/api/delete-student/:id

Open a new terminal window and start the MongoDB:

mongo

Open a new terminal window and start the nodemon server:

nodemon

You can view server running on http://localhost:4000/api

Open another terminal window and start the vue app:

npm run serve

You can view vue app running on http://localhost:8080

Create Student Data with Axios POST

The Axios post method takes the REST API and makes the POST request to the server. It creates the student data that we are adding in the mongoDB database.

Once the data is sent to the server, you can then check the stored data on http://localhost:4000/api

Add the given below code inside the components/CreateComponent.vue file.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <h3 class="text-center">Create Student</h3>
            <form @submit.prevent="handleSubmitForm">
                <div class="form-group">
                    <label>Name</label>
                    <input type="text" class="form-control" v-model="student.name" required>
                </div>

                <div class="form-group">
                    <label>Email</label>
                    <input type="email" class="form-control" v-model="student.email" required>
                </div>

                <div class="form-group">
                    <label>Phone</label>
                    <input type="text" class="form-control" v-model="student.phone" required>
                </div>

                <div class="form-group">
                    <button class="btn btn-danger btn-block">Create</button>
                </div>
            </form>
        </div>
    </div>
</template>

<script>
    import axios from "axios";

    export default {
        data() {
            return {
                student: {
                   name: '',
                   email: '',
                   phone: ''
                }
            }
        },
        methods: {
            handleSubmitForm() {
                let apiURL = 'http://localhost:4000/api/create-student';
                
                axios.post(apiURL, this.student).then(() => {
                  this.$router.push('/view')
                  this.student = {
                    name: '',
                    email: '',
                    phone: ''
                  }
                }).catch(error => {
                    console.log(error)
                });
            }
        }
    }
</script>

Show Data List & Delete Data in Vue

Now, we will show the data in the tabular form using Bootstrap Table components, and we will make the axios.get() request to render the data from the server.

Add the given below code inside the components/ListComponent.vue file.

<template>
    <div class="row">
        <div class="col-md-12">
            <table class="table table-striped">
                <thead class="thead-dark">
                    <tr>
                        <th>Name</th>
                        <th>Email</th>
                        <th>Phone</th>
                        <th>Actions</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="student in Students" :key="student._id">
                        <td>{{ student.name }}</td>
                        <td>{{ student.email }}</td>
                        <td>{{ student.phone }}</td>
                        <td>
                            <router-link :to="{name: 'edit', params: { id: student._id }}" class="btn btn-success">Edit
                            </router-link>
                            <button @click.prevent="deleteStudent(student._id)" class="btn btn-danger">Delete</button>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
    import axios from "axios";

    export default {
        data() {
            return {
                Students: []
            }
        },
        created() {
            let apiURL = 'http://localhost:4000/api';
            axios.get(apiURL).then(res => {
                this.Students = res.data;
            }).catch(error => {
                console.log(error)
            });
        },
        methods: {
            deleteStudent(id){
                let apiURL = `http://localhost:4000/api/delete-student/${id}`;
                let indexOfArrayItem = this.Students.findIndex(i => i._id === id);

                if (window.confirm("Do you really want to delete?")) {
                    axios.delete(apiURL).then(() => {
                        this.Students.splice(indexOfArrayItem, 1);
                    }).catch(error => {
                        console.log(error)
                    });
                }
            }
        }
    }
</script>

<style>
    .btn-success {
        margin-right: 10px;
    }
</style>

To delete the student object from the database, we defined the deleteStudent() function and bound it to click event with an id parameter.

The apiURL contains the api/delete-student/:id API, to find out the index of the clicked student data index from the Students array, we took the help of findIndex() method.

Show Data List & Delete Data in Vue

Update Data with POST Request

Add the following code inside the components/EditComponent.vue file.

<template>
    <div class="row justify-content-center">
        <div class="col-md-6">
            <h3 class="text-center">Update Student</h3>
            <form @submit.prevent="handleUpdateForm">
                <div class="form-group">
                    <label>Name</label>
                    <input type="text" class="form-control" v-model="student.name" required>
                </div>

                <div class="form-group">
                    <label>Email</label>
                    <input type="email" class="form-control" v-model="student.email" required>
                </div>

                <div class="form-group">
                    <label>Phone</label>
                    <input type="text" class="form-control" v-model="student.phone" required>
                </div>

                <div class="form-group">
                    <button class="btn btn-danger btn-block">Update</button>
                </div>
            </form>
        </div>
    </div>
</template>

<script>
import axios from "axios";

export default {
    data() {
        return {
            student: { }
        }
    },
    created() {
        let apiURL = `http://localhost:4000/api/edit-student/${this.$route.params.id}`;

        axios.get(apiURL).then((res) => {
            this.student = res.data;
        })
    },
    methods: {
        handleUpdateForm() {
            let apiURL = `http://localhost:4000/api/update-student/${this.$route.params.id}`;

            axios.post(apiURL, this.student).then((res) => {
                console.log(res)
                this.$router.push('/view')
            }).catch(error => {
                console.log(error)
            });
        }
    }
}
</script>

Here we have to deal with two things, and we have to render the data when the user lands on student details component and make the post request to update the information on the server.

To get the current id from the existing url, use this.$route.params.id vue API.

Using the same API we can get the id from the url and can update the database values by consuming the api/update-student/:id API.

Summary

We have finished MEVN Stack tutorial. You can check out the complete code of this tutorial on my GitHub repository.

# Start the MEVN Stack project.
```
Git clone https://github.com/SinghDigamber/vue-mevn-stack-app.git

Get inside the project
cd vue-mevn-stack-app

Install the required packages:
npm install

Start the vue app on http://localhost:8080
npm run serve

Get inside the Node server folder:
cd backend

Install the required packages:
npm install

Start the mongodb server.
mongo

Start node server on http://localhost:4000/api 
nodemon

```