AppDividend
Latest Code Tutorials

MEVN Stack Tutorial With Example From Scratch

453

Get real time updates directly on you device, subscribe now.

MEVN Stack Tutorial With Example From Scratch is the topic, we will discuss today. We will generate FullStack Single Page Application(SPA) using MongoDB, Express, Vue, and Node.js CRUD Example Tutorial with step by step. For this example, we will not use Vuex for state management because this app will be simple and not complicated. We will create an application where the user can create, update, read, and delete the post. So, let us start the MEVN Stack Tutorial with an example.

MEVN Stack Tutorial With Example From Scratch

We install Vue.js project using Vue CLI. Vue CLI requires the Node.js version 8.9 or above (8.11.0+ recommended). You can manage multiple versions of Node on the same machine with nvm or nvm-windows

Type the following command to install Vue CLI, if you have not installed.

npm install -g @vue/cli

# OR

yarn global add @vue/cli

You can check the version of Vue.js using this command.

vue --version

#1: Create a Vue.js project

Type the following command to install Vue.js.

vue create mevnexample

 

MEVN Stack Tutorial With Example From Scratch

Now, go inside the vue project.

cd mevnexample

Open the project inside code editor. I am using Visual Code.

code .

Start the vue dev server using the following command.

npm run serve

Go to the http://localhost:8080

You can see that we have successfully installed the Vue.js.

#2: Install Vue dependencies.

Type the following command to install the axiosvue-router and vue-axios dependencies. The vue-router is used for routing our Vue.js application to use the different components and the vue-axios for sending the network request to the server.

npm install axios vue-router vue-axios --save

Install the Bootstrap CSS Framework version 4 using the following command.

npm install bootstrap --save

Now, import the Bootstrap 4 CSS inside the main.js file.

// main.js

import Vue from 'vue'
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.min.css'

Vue.config.productionTip = false

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

#3: Create the vue components.

Go to the src >> components folder and remove the HelloWorld.vue file and create the following components.

  1. HomeComponent.vue
  2. CreateComponent.vue
  3. EditComponent.vue
  4. IndexComponent.vue

Now, add the following code inside the HomeComponent.vue file.

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Home Component</div>

              <div class="card-body">
                  I'm the Home Component component.
              </div>
          </div>
      </div>
  </div>
</template>
<script>
export default {
}
</script>

Next step is to import the HomeComponent.vue file inside the App.vue file.

// App.vue

<template>
  <div>
    <HomeComponent />
  </div>
</template>

<script>
import HomeComponent from './components/HomeComponent.vue'

export default {
  name: 'app',
  components: {
    HomeComponent
  }
}
</script>

Save the file, and if the server is running, then you can go to the http://localhost:8080 and see that HomeComponent is rendering.

Now, create the remaining three components.

// CreateComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Create Component</div>

              <div class="card-body">
                  I'm the Create Component component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
  export default {
  }
</script>
// EditComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Edit Component</div>

              <div class="card-body">
                  I'm an Edit component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
  export default {
  }
</script>
// IndexComponent.vue

<template>
  <div class="row justify-content-center">
      <div class="col-md-8">
          <div class="card card-default">
              <div class="card-header">Index Component</div>

              <div class="card-body">
                  I'm an Index component.
              </div>
          </div>
      </div>
  </div>
</template>

<script>
  export default {
  }
</script>

#4: Configure the vue-router

Inside the main.js file, write the following code.

Related Posts
1 of 15
// main.js

import Vue from 'vue'
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.min.css'

import VueRouter from 'vue-router';
Vue.use(VueRouter);

Vue.config.productionTip = false;

import HomeComponent from './components/HomeComponent.vue';
import CreateComponent from './components/CreateComponent.vue';
import IndexComponent from './components/IndexComponent.vue';
import EditComponent from './components/EditComponent.vue';

const routes = [
  {
      name: 'home',
      path: '/',
      component: HomeComponent
  },
  {
      name: 'create',
      path: '/create',
      component: CreateComponent
  },
  {
      name: 'posts',
      path: '/posts',
      component: IndexComponent
  },
  {
      name: 'edit',
      path: '/edit/:id',
      component: EditComponent
  }
];

const router = new VueRouter({ mode: 'history', routes: routes});

new Vue(Vue.util.extend({ router }, App)).$mount('#app');

What we have done is that, first, imported the vue-router module and then create an array of routes which has a name, path, and components as the properties.

Then we have created the router object and passed the mode history and the routes array. So when the application boot, we will give these routes to the vue application.

Now, the next step is to define the <router-view></router-view> element. It renders the component according to the routing path in the Vue application.

So, add the <router-view> inside the App.vue file.

// App.vue

<template>
  <div>
    <router-view></router-view>
  </div>
</template>

<script>


export default {
  name: 'app'
}
</script>

Save the file and check the following route paths to see if we are getting the exact routes.

  1. http://localhost:8080/create
  2. http://localhost:8080/posts
  3. http://localhost:8080/edit/21

#5: Create a Navigation bar

Write the following code inside the App.vue file. I have added the navbar.

// App.vue

<template>
  <div class="container">
    <nav class="navbar navbar-expand-sm bg-dark navbar-dark">
      <ul class="navbar-nav">
        <li class="nav-item">
          <router-link to="/" class="nav-link">Home</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/create" class="nav-link">Create Post</router-link>
        </li>
        <li class="nav-item">
          <router-link to="/posts" class="nav-link">Posts</router-link>
        </li>
      </ul>
    </nav><br />
    <transition name="fade">
      <router-view></router-view>
    </transition>
  </div>
</template>

<style>
    .fade-enter-active, .fade-leave-active {
      transition: opacity .5s
    }
    .fade-enter, .fade-leave-active {
      opacity: 0
    }
</style>

<script>

    export default{
    }
</script>

So, what we have done is that add the navigation bar and add some animation effect like transition. So when we navigate the routes, the component is changing according to their path.

 

Vue Node CRUD Example

#6: Create a Form

We need to create a form to enter the details of form. So let us create a form inside the CreateComponent.vue file.

// CreateComponent.vue

<template>
  <div>
    <h1>Create A Post</h1>
    <form @submit.prevent="addPost">
      <div class="row">
        <div class="col-md-6">
          <div class="form-group">
            <label>Post Title:</label>
            <input type="text" class="form-control" v-model="post.title">
          </div>
        </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label>Post Body:</label>
              <textarea class="form-control" v-model="post.body" rows="5"></textarea>
            </div>
          </div>
        </div><br />
        <div class="form-group">
          <button class="btn btn-primary">Create</button>
        </div>
    </form>
  </div>
</template>

<script>
    export default {
        data(){
        return {
          post:{}
        }
    },
    methods: {
      addPost(){
        console.log(this.post);
      }
    }
  }
</script>

Also, we have defined the object called post. So here, we have used the two-way data binding. The post object has two properties. 1) title 2) body.

We have made one method called addPost().  So, when a user submits the form, we will get the input inside the addPost() method. From then, we will send a POST request to the Laravel server and to save the data into the database.

I am skipping the validation of each field because this article is getting long and long. So we will do it in another post.

Save the file and go to this URL: http://localhost:8080/create or /create. You can see the form like below.

 

Vue Express CRUD Tutorial Example

#7: Create a Node.js backend.

Create one folder inside the vue project root called api and go inside that folder.

Now, initialize the package.json file.

npm init -y

Now, install the following node.js dependencies. 

yarn add express body-parser cors mongoose

# or

npm install express body-parser cors mongoose --save

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

We have installed the express web framework which is built on top of the Node.js server. Also, installed the body-parser module to parse the data coming from the request and then installed the cors module which will help us to ignore the cross-site request origin warning from the browser. It is security provided by the browser when we request them from a different server.

npm install nodemon --save-dev

Now create three files called server.js, DB.js, post.model.js, and post.route.js. All these files are inside the root of the api folder. Write the following code in the server.js file.

// server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const PORT = 4000;
const cors = require('cors');

app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.listen(PORT, function(){
  console.log('Server is running on Port:',PORT);
});

Now, we need to start a node server using the following command.

nodemon server

So, our node.js server is running at port: 4000

 

Vue Node Tutorial Example

#8: Setup and connect MongoDB database.

If you are the beginner in the MongoDB database, then please check out my below tutorial.

Related Post: NoSQL MongoDB Tutorial

I have already installed the MongoDB on Mac. So I am starting the MongoDB server by the following command. You can also use any MongoDB, database client. I am just using the terminal for this demo because it is a simple application.

mongod

Inside an api folder, create one file called the DB.js which is responsible for connecting our node application to the mongodb database. So add the following line of code.

// DB.js

module.exports = {
    DB: 'mongodb://localhost:27017/mevncrud'
}

In my local MongoDB database, username and password are empty, but in the production database, you need to create one user and assign that user to the database

Now, import this DB.js file into the server.js file.

// server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const PORT = 4000;
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./DB.js');

mongoose.Promise = global.Promise;
mongoose.connect(config.DB, { useNewUrlParser: true }).then(
  () => {console.log('Database is connected') },
  err => { console.log('Can not connect to the database'+ err)}
);

app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.listen(PORT, function(){
  console.log('Server is running on Port:',PORT);
});

Save the file, and you can see inside the terminal that our node.js application is connected to the mongodb database.

#9: Create a Mongoose Schema

Write the following code inside the post.model.js file.

// post.model.js

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

// Define collection and schema for Post
let Post = new Schema({
  title: {
    type: String
  },
  body: {
    type: String
  }
},{
    collection: 'posts'
});

module.exports = mongoose.model('Post', Post);

We have taken three fields called title and body with String datatype.

#10: Define a route for Node.js Express application

Write the CRUD operation code inside the post.route.js file.

// post.model.js

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

// Require Post model in our routes module
let Post = require('./post.model');

// Defined store route
postRoutes.route('/add').post(function (req, res) {
  let post = new Post(req.body);
  post.save()
    .then(() => {
      res.status(200).json({'business': 'business in added successfully'});
    })
    .catch(() => {
      res.status(400).send("unable to save to database");
    });
});

// Defined get data(index or listing) route
postRoutes.route('/').get(function (req, res) {
    Post.find(function(err, posts){
    if(err){
      res.json(err);
    }
    else {
      res.json(posts);
    }
  });
});

// Defined edit route
postRoutes.route('/edit/:id').get(function (req, res) {
  let id = req.params.id;
  Post.findById(id, function (err, post){
      if(err) {
        res.json(err);
      }
      res.json(post);
  });
});

//  Defined update route
postRoutes.route('/update/:id').post(function (req, res) {
    Post.findById(req.params.id, function(err, post) {
    if (!post)
      res.status(404).send("data is not found");
    else {
        post.title = req.body.title;
        post.body = req.body.body;
        post.save().then(() => {
          res.json('Update complete');
      })
      .catch(() => {
            res.status(400).send("unable to update the database");
      });
    }
  });
});

// Defined delete | remove | destroy route
postRoutes.route('/delete/:id').delete(function (req, res) {
    Post.findByIdAndRemove({_id: req.params.id}, function(err){
        if(err) res.json(err);
        else res.json('Successfully removed');
    });
});

module.exports = postRoutes;

Here, we have defined the CRUD operations in Node.js, which is backend server side. So when the request from the clientside hits the node express server, it maps the URI and according to URI, the above function will be executed, and database operation will be performed and send the response to the client, and in our case, it is vue.js frontend.

Here, we have used a Mongoose ORM to create, read, update, delete the data from the mongodb databaseMongoose is an ORM(Object Relational Mapping) used in the MongoDB database. Now, we have all the CRUD operations set up on the post.route.js file; we need to import inside the server.js file.

So, our final server.js file looks like this.

// server.js

const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const PORT = 4000;
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./DB.js');
const postRoute = require('./post.route');

mongoose.Promise = global.Promise;
mongoose.connect(config.DB, { useNewUrlParser: true }).then(
  () => { console.log('Database is connected') },
  err => { console.log('Can not connect to the database'+ err)}
);

app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());

app.use('/posts', postRoute);

app.listen(PORT, function(){
  console.log('Server is running on Port:',PORT);
});
If the linter is showing error on console statement inside the Visual Studio Code editor or other editors, then ignore it because it is warning provided by eslint configuration. It is not an actual error and you can able to see the output inside the browser.

#11: Use axios to send a network request

Go to the frontend vue project and import the axios and vue-axios inside the main.js file.

// main.js

import Vue from 'vue'
import App from './App.vue'
import 'bootstrap/dist/css/bootstrap.min.css'

import VueRouter from 'vue-router';
Vue.use(VueRouter);

import VueAxios from 'vue-axios';
import axios from 'axios';

Vue.use(VueAxios, axios);

Vue.config.productionTip = false;

import HomeComponent from './components/HomeComponent.vue';
import CreateComponent from './components/CreateComponent.vue';
import IndexComponent from './components/IndexComponent.vue';
import EditComponent from './components/EditComponent.vue';

const routes = [
  {
      name: 'home',
      path: '/',
      component: HomeComponent
  },
  {
      name: 'create',
      path: '/create',
      component: CreateComponent
  },
  {
      name: 'posts',
      path: '/posts',
      component: IndexComponent
  },
  {
      name: 'edit',
      path: '/edit/:id',
      component: EditComponent
  }
];

const router = new VueRouter({ mode: 'history', routes: routes});

new Vue(Vue.util.extend({ router }, App)).$mount('#app');

Now, we have created the backend. Next step is to send the POST request to the node.js api server. Remember, now we have now three servers are running.

  1. Vue development server
  2. Node.js server
  3. MongoDB server

Remember, all are running fine otherwise the project won’t work.

Write the following code inside the CreateComponent.vue file’s addPost() function.

// CreateComponent.vue

addPost(){
    let uri = 'http://localhost:4000/posts/add';
    this.axios.post(uri, this.post).then(() => {
       this.$router.push({name: 'posts'});
    });
}

You can now create the post and see in the mongodb database.

To check from the terminal, you need to open the mongoshell using the following command.

mongo

 

MongoDB database example

#12: Display the backend data

Write the following code inside the IndexComponent.js file.

// IndexComponent.js

<template>
  <div>
      <h1>Posts</h1>
        <div class="row">
          <div class="col-md-10"></div>
          <div class="col-md-2">
            <router-link :to="{ name: 'create' }" class="btn btn-primary">Create Post</router-link>
          </div>
        </div><br />

        <table class="table table-hover">
            <thead>
            <tr>
              <th>Title</th>
              <th>Body</th>
              <th>Actions</th>
            </tr>
            </thead>
            <tbody>
                <tr v-for="post in posts" :key="post._id">
                  <td>{{ post.title }}</td>
                  <td>{{ post.body }}</td>
                  <td><router-link :to="{name: 'edit', params: { id: post._id }}" class="btn btn-primary">Edit</router-link></td>
                  <td><button class="btn btn-danger">Delete</button></td>
                </tr>
            </tbody>
        </table>
  </div>
</template>

<script>
  export default {
      data() {
        return {
          posts: []
        }
      },
      created() {
      let uri = 'http://localhost:4000/posts';
      this.axios.get(uri).then(response => {
        this.posts = response.data;
      });
    }
  }
</script>

When the component is created, it will send a GET request to the node.js server and fetch the data from the database and then assign that data to the posts array, and then we loop through that array and display the data in the tabular format.

MEVN CRUD Example

 

#13: Send edit and update request

Now, when our edit component loads, we need to fetch the data from the server to display the existing data.

Then, after changing the data in the textbox and textarea, we hit the update button, and we call the updatePost() function to send a post request to the server to update the data.

// EditComponent.vue

<template>
  <div>
    <h1>Edit Post</h1>
    <form @submit.prevent="updatePost">
      <div class="row">
        <div class="col-md-6">
          <div class="form-group">
            <label>Post Title:</label>
            <input type="text" class="form-control" v-model="post.title">
          </div>
        </div>
        </div>
        <div class="row">
          <div class="col-md-6">
            <div class="form-group">
              <label>Post Body:</label>
              <textarea class="form-control" v-model="post.body" rows="5"></textarea>
            </div>
          </div>
        </div><br />
        <div class="form-group">
          <button class="btn btn-primary">Update</button>
        </div>
    </form>
  </div>
</template>

<script>
    export default {

      data() {
        return {
          post: {}
        }
      },
      created() {
        let uri = `http://localhost:4000/posts/edit/${this.$route.params.id}`;
        this.axios.get(uri).then((response) => {
            this.post = response.data;
        });
      },
      methods: {
        updatePost() {
          let uri = `http://localhost:4000/posts/update/${this.$route.params.id}`;
          this.axios.post(uri, this.post).then(() => {
            this.$router.push({name: 'posts'});
          });
        }
      }
    }
</script>

You will see the edit data from the database and also you can update the database. So till now, insert, read,  and update operations are complete. Now, last, the delete is remaining.

#14: Delete or Remove the data.

Now, the only remaining thing is to delete or remove the data from the database.

So, let us write the final code inside the IndexComponent.vue file.

// IndexComponent.vue

<template>
  <div>
      <h1>Posts</h1>
        <div class="row">
          <div class="col-md-10"></div>
          <div class="col-md-2">
            <router-link :to="{ name: 'create' }" class="btn btn-primary">Create Post</router-link>
          </div>
        </div><br />

        <table class="table table-hover">
            <thead>
            <tr>
              <th>Title</th>
              <th>Body</th>
              <th>Actions</th>
            </tr>
            </thead>
            <tbody>
                <tr v-for="post in posts" :key="post._id">
                  <td>{{ post.title }}</td>
                  <td>{{ post.body }}</td>
                  <td><router-link :to="{name: 'edit', params: { id: post._id }}" class="btn btn-primary">Edit</router-link></td>
                  <td><button class="btn btn-danger" @click.prevent="deletePost(post._id)">Delete</button></td>
                </tr>
            </tbody>
        </table>
  </div>
</template>

<script>
  export default {
      data() {
        return {
          posts: []
        }
      },
      created() {
      let uri = 'http://localhost:4000/posts';
      this.axios.get(uri).then(response => {
        this.posts = response.data;
      });
    },
    methods: {
      deletePost(id)
      {
        let uri = `http://localhost:4000/posts/delete/${id}`;
        this.axios.delete(uri).then(response => {
          this.posts.splice(this.posts.indexOf(id), 1);
        });
      }
    }
  }
</script>

Save the file, and now, you can also delete the values from the MongoDB database.

So, finally, our MEVN Stack Tutorial With Example Demo is over. I have put this code on Github. So check that out as well.

Github Code

Steps to use code

  1. Clone the repository.
  2. Go inside the vue project and type this command: npm install
  3. Go inside the api folder and type this command: npm install
  4. Now, start the mongodb server.
  5. Also, start the node server using this command: nodemon server
  6. Go to the vue root project and type this command: npm run serve
  7. Go to the http://localhost:8080 and your project is running.

Leave A Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.