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).
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.
Table of Contents
Create a New Vue Project
Adding Bootstrap in Vue
Build Vue Components
Enable Vue Router
Setting Up Navigation and Router View
Add Axios in Vue.js
Build Form in Vue with Bootstrap 4
Setting up Node Server Environment
Configure MongoDB Database
Create Mongoose Model
Create Route in Node/Express App
Start Node/Express App
Create Student Data with Axios POST
Show Data List & Delete Data in Vue
Update Data with POST Request
Summary
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
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')
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.
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>
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
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>
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,
We require to store the data in the MEVN stack app, so we need to build a vue form using Bootstrap Form component.
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.
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
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 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 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"
}
}
We have built following API using Node and Express in Vue.js.
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
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>
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.
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.
We have finished MEVN Stack tutorial.
# 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
#mongodb #express #vue-js #node-js #javascript