Photo by Azharul Islam on Unsplash

Create a CRUD app using Vue.js and Node.js (MEVN stack)

This tutorial will guide you through the steps of creating a simple CRUD app using MEVN stack (MongoDB, Express.js, Vue.js and Node.js). Axios is used for making HTTP requests.

Vue.js is a JavaScript framework for building user interfaces. The advantage of vue over other front-end frame works is its easiness to learn.

Node.js is an open-source, cross-platform, JavaScript runtime environment that executes JavaScript code outside of a browser

Express.js is a web application framework for Node.js.

MongoDB is an opensource, cross-platform NoSQL database used by most modern apps. MongoDB Compass will provide a GUI for MongoDB

Axios is a promise based HTTP client for the browser and Node.js

In this tutorial we are going to create a simple To-do app with basic CRUD functionality.

Before getting started with this tutorial, make sure that you have installed Node.js, Vue CLI and MongoDB in your system.

Building the App

  1. Create a directory named ‘Todo_tuts’ and navigate to the directory
mkdir Todo_tuts
cd Todo_tuts

Let’s start by creating back-end

  1. Create a directory named ‘server’ inside ‘Todo_tuts’.
  2. Navigate to the ‘server’ directory and run the following command.
npm init

Select default options by pressing enter key.

Install dependencies ‘Express.js’, ‘BodyParser’ and ‘Mongoose’. BodyParser is used for parsing request from client, Mongoose for object modeling and cors is used to prevent Cross-Origin Request Block error while we try to communicate from front-end to back-end

Run the following command to install dependencies.

npm install express body-parser mongoose cors --save

A package.json file will be created in Todo_tuts/server which look like this

Now its time to create back-end server

Create a file called index.js inside server directory. Inside the index.js file paste the below code

const express = require('express');
const cors = require('cors')
const app = express();
app.use(cors())
app.listen(3000, () => {
Console.log("Server started on port 3000")
})

express and cors are imported first. Then we are initializing an express app. app.use(cors()) enables cross-origin resource sharing so that incoming requests will not be blocked by cors policy. app.listen creates a back-end server on http://localhost:3000

To start the sever, run the following command in the terminal.

node index

You will see the following output in the terminal if server is started

Database connection

Before creating the routes, let us connect our app to MongoDB. For that we first need to import mongoose.

const mongoose = require('mongoose')

For connecting to MongoDB, paste the following code in index.js

mongoose.connect("mongodb://localhost:27017/todoapp", {
useNewUrlParser: true,
useUnifiedTopology: true
});
var db = mongoose.connection;db.on('open', () => {
console.log('Connected to mongoDB');
});
db.on('error', (error) => {
console.log(error)
})

mongoose.connect is used for connecting to MongoDB. 27017 is default port for MongoDB and todoapp is our database name.

Upon successful connection to MongoDB, you will get following message in terminal

Schema creation

Now we need to create schema for our database. Schema define the structure of the data stored in the database. Create a file called todo_schema.js inside server directory.

Our To-do schema has following fields:

  • Title
  • Completed

First we should import mongoose. Then create a schema using mongoose.schema. Add the following code to todo_schema.js file.

const mongoose = require("mongoose");
const todoSchema = mongoose.Schema({
title: {
type: String,
required: true
},
completed: {
type: Boolean,
required: true
}
});
const todo = (module.exports = mongoose.model("todo", todoSchema));

At last we export the schema as a schema model using mongoose.model().

Defining Routes

Routes is used for handling the client request. We need to set routes for adding a to-do, updating to-do as completed, fetching created todos and delete to-dos.

We need to import body-parser for parsing body of the request to fetch to-do details whenever any post request comes,For that add following code to index.js

const bodyParser = require('body-parser') app.use(bodyParser.urlencoded({ extended: false }));

app.use(bodyParser.json());

First we need to import the to-do schema model. For that, add the following code to index.js

let todoModel = require('./todo_schema')

Creating a To-do

For creating a to-do, copy the following code to index.js

app.post('/todo/add', (req, res) => {  let newTodo = new todoModel;  newTodo.title = req.body.todo;  newTodo.completed = false;  newTodo.save((err) => {    if(err){      res.send("Error while adding Todo");    }else{      res.send("Todo added");    }})

The above code creates a new to-do model called ‘newTodo’ and saves the to-do details by parsing the request and then saves to database.

Fetching To-do

Fetching the to-dos and sending to client is done as two parts. i.e we need to fetch both completed and uncompleted to-dos. For the easiness of understanding we will create two different routes for this. i.e one route for completed to-dos and the other for uncompleted to-dos.

Route for completed to-dos

app.get('/todo/completed', (req, res) => {  todoModel.find({ completed: true }, (err, todos) => {    if (err) {      res.send("Error while fetching Todos");    } else {      res.json(todos)    }  })})

Route for uncompleted to-dos

app.get('/todo/uncompleted', (req, res) => {  todoModel.find({completed:false},(err, todos) => {    if(err){      res.send("Error while fetching Todos");    }else{      res.json(todos)    }  })})

Updating To-Do

For updating a to-do as completed, we change the completed field of to-do to true. For every document in MongoDB, MongoDB will provide an object id by itself. We will pass this object id to the back-end API and update the complete field of the to-do as true.

app.post('/todo/complete/:id',(req, res) => {  todoModel.findByIdAndUpdate(req.params.id, {completed: true},         (err, todo) =>{    if(!err){      res.send("Good Work");    }  })})

Delete To-Do

For deleting the to-do, we will receive the object id of the to-do which is to be deleted from the front-end and will delete the to-do having the corresponding object id.

app.delete('/todo/:id', (req, res) => {  let query = { _id: req.params.id }  todoModel.deleteOne(query, (err) => {    if(err){      res.send("Error while deleting todo")    }else{      res.send("Todo deleted")    }  })})

Our back-end server is ready. Now lets move to front-end

Let’s start creating front-end

  1. Inside the ‘Todo_tuts’ directory execute the following command in the terminal
vue create client

It will ask for a preset. Just select the default preset. Then vue will create ‘client’ folder for our front-end code.

To see our app

cd client
npm run serve

let’s start by installing dependencies

For front-end we are using axios and vuetify. axios is for making HTTP request to back-end and vuetify is a CSS framework.Inside the client folder, run the following commands in a terminal

First we will install vuetify using the below command

vue add vuetify

Then install axios using the below command

npm install axios --save

To communicate with back-end, we will be making all our requests to http://localhost:3000 with different endpoints. Because our back-end server will be running at http://localhost:3000 . For this our back-end server should be running whenever any request happens.

Create To-do

Paste the following code in Todo_tuts/client/src/App.vue

<template>  <v-app>    <div class="d-flex justify-center">      <h1 id="addTodo">Add ToDo</h1>    </div>    <div class="d-flex justify-center">      <v-col cols="6" style="margin: 0px auto;">        <v-text-field v-model="newTodo" label="Add Todo" solo></v-text-field>      </v-col>    </div>    <div class="d-flex justify-center">      <v-btn @click="addToDo()" color="primary">Add ToDo</v-btn>    </div>  </v-app></template><script>import axios from "axios";export default {  data: () => ({    newToDo: "",  }),  methods: {    addToDo() {      axios.post("http://localhost:3000/todo/add", {        todo: this.newTodo     }).then(response => {       this.message = response.data;     });  }};</script>

On clicking the Add ToDo button, addTodo function will be called. Inside the function we are making a post request to http://localhost:3000/todo/add using axios.

Fetch To-do

Add the following code to App.vue inside v-app tag below previously added code.

<!-- uncompleted todos --><div class="d-flex justify-center">  <h1>Unompleted ToDo</h1></div><div v-for="todo in uncompletedTodos" :key="todo._id">  <v-card class="mx-auto" color="white" dark max-width="800">    <v-card-text class="font-weight-bold title blue--text">    {{ todo.title }}      <v-list-item id="todo-list-item" class="grow">        <v-btn @click="completeTodo(todo._id)" class="mx-2" small        color="green"> Done </v-btn>       <v-btn @click="deleteTodo(todo._id)" class="mx-2" small       color="red"> Delete </v-btn>      </v-list-item>    </v-card-text>  </v-card></div><!-- completed todos --><div class="d-flex justify-center">  <h1>Completed ToDo</h1></div><div v-for="todo in completedTodos" :key="todo._id">  <v-card class="mx-auto" color="blue" dark max-width="800">    <v-card-text class="font-weight-bold title white--text">    {{ todo.title }}      <v-list-item id="todo-list-item" class="grow">        <v-btn @click="deleteTodo(todo._id)" class="mx-2" small         color="red"> Delete </v-btn>      </v-list-item>    </v-card-text>  </v-card></div>

Here we are rendering completed and uncompleted to-dos separately by storing it in two different arrays .

Add this inside data in script tag

uncompletedTodos: [],
completedTodos: []

These two arrays will store our completed and uncompleted to-dos. We will render these using v-for directive.

Add this below methods in script tag

created() {  // fetch uncompleted task  axios.get("http://localhost:3000/todo/uncompleted")  .then(response => (this.uncompletedTodos = response.data))  .catch(error => console.log(error));  // fetch completed task  axios.get("http://localhost:3000/todo/completed")  .then(response => (this.completedTodos = response.data))  .catch(error => console.log(error));}

This is created hook which is one the life-cycle hook in vue. When our app is created this axios get requests will hit the back-end API and fetch completed and uncompleted tasks. We will add these to-dos to two arrays, one for completed and other for uncompleted tasks, and using v-for directive we will render both the completed and uncompleted tasks.

Delete To-do

Add the following code below methods in script tag in App.vue

deleteTodo(todoID) {  axios  .delete("http://localhost:3000/todo/" + todoID).then(response => {   console.log(response.data);  });}

We are making a delete request to http://localhost:3000/todo/'+todoID . The deleteTodo function will be called when delete button is pressed. The todoID is the mongodb object id of each to-do.This will be passed to the function which is used to delete the corresponding to-do.

Update To-do

Add this code inside method in script tag in App.vue

completeTodo(todoID) {  axios.post("http://localhost:3000/todo/complete/" + todoID)  .then(response => {    console.log(response.data);  });}

For updating the to-do as completed, we are making a post request to http://localhost:3000/todo/complete/+todoID . The to-do with corresponding todoID will be updated in the back-end.

Please refresh the site after every change. Then only the change will reflect in the website.

Hurray…. We had created a simple CRUD app using MEVN stack.

The complete code for this tutorial can be found in here

Summary

This tutorial covered:

  1. How to create a server using express.js
  2. Create, Read, Update and Delete data stored in MongoDB
  3. Create a simple front-end using vue.js and vuetify
  4. Connect front-end to a back-end server

Hope this tutorial helped you. Let me know your doubts and suggestions in the comment section.

Good Day :)

Golang | Node.js | albinjose.me