DEV Community

Cover image for MongoDB, Express, Vue, and Node.
Kevin Odongo
Kevin Odongo

Posted on

MongoDB, Express, Vue, and Node.

In our previous tutorial, we wanted to practically learn and understand the serverless, container, and server approaches https://dev.to/kevin_odongo35/serverless-container-or-server-approach-4mh5. This tutorial will be fun, a step by step guide.

In today's tutorial, we are going to handle the first approach of using the following:

  1. Express
  2. MongoDB // we can use MySQL, PostgreSQL etc
  3. Node

This is the architecture we are trying to achieve:

Alt Text

On the root folder of the directory (blog-vue-application) that we created in the first tutorial.

Alt Text

Run the following commands in the root folder:

$ mkdir blog-vue-back-end && cd blog-vue-back-end
$ npm init
package name: blog-vue-back-end
version: // press enter
description: Tutorial
entry point: index.js
test command: // press enter
git repository: // press enter
keywords: Mongo DB, Node, Passport and Express
author: your name
licence: MIT
// save
Enter fullscreen mode Exit fullscreen mode

Once done install the following packages

  1. express
  2. cors
  3. node
  4. body-parser
  5. mongoose
  6. mongodb // will be using for testing purposes
  7. jest // you can use a different testing
  8. dotenv
$ npm install express mongoose mongodb cors body-parser node express dotenv
Enter fullscreen mode Exit fullscreen mode

Install the nodemon package as a dependency so you don't have to restart your server.

$ npm install -D nodemon
Enter fullscreen mode Exit fullscreen mode

These are APIs that Node.js Express App will export:

Methods URL Actions
GET api/blog get many blogs
GET api/blog/:id get a single blog
POST api/blog create blog
PUT api/blog update blog
DELETE api/blog remove many blogs
DELETE api/blog/:id remove single blog

This is how your application structure should be:

Alt Text

package.json

Once we have installed everything, update the sections of the script. This is how your package.js file should be:

{
  "name": "blog-vue-back-end",
  "version": "1.0.0",
  "description": "Tutorial Backend for Blog Application",
  "main": "index.js",
  "scripts": {
    "start": "node server/index.js",
    "dev": "nodemon server/index.js",
    "test-dev": "jest"
  },
  "keywords": [
    "Mongo",
    "DB",
    "Express",
    "Node"
  ],
  "author": "Kevin Odongo",
  "license": "MIT",
  "dependencies": {
    "@shelf/jest-mongodb": "^1.2.3",
    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "jest": "^26.6.3",
    "mongodb": "^3.6.3",
    "mongoose": "^5.11.14"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}

Enter fullscreen mode Exit fullscreen mode

Create an index.js file in the backend folder. This will be the entry point for our application.

$ touch index.js
Enter fullscreen mode Exit fullscreen mode

index.js

const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
require('dotenv').config()

const app = express();

// parse application/json
app.use(bodyParser.json())

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }))

var corsOptions = {
  origin: 'http://localhost:3000'
}
// use cors options
app.use(cors(corsOptions))

// 
const db = require("./app/models");
db.mongoose
  .connect(db.url, {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
    useCreateIndex: true
  })
  .then(() => {
    console.log("Connected to the database!");
  })
  .catch(err => {
    console.log("Cannot connect to the database!", err);
    process.exit();
  });


// routes
const blog = require('./app/routes/blog')
app.use('/api/blog', blog)

// listening port
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server is running on port ${PORT}.`);
});
Enter fullscreen mode Exit fullscreen mode

Once we have an entry point for our application let us initialize Mongo DB.

Create a file .env in the root folder

$ touch .env
// add the following
MONGO_DB_URI = mongodb+srv://odongo:password@secretserver.e5kih.mongodb.net/blog?retryWrites=true&w=majority
Enter fullscreen mode Exit fullscreen mode

To get your mongodb url register for a free account here https://account.mongodb.com/account/register. Once you register create a cluster, user, and database.

Add node modules and .env file in the .gitignore file.

$ touch .gitignore
// add the following
node_modules
# local env files
.env.local
.env.*.local
Enter fullscreen mode Exit fullscreen mode

Create a directory called app that will hold the following files.

/app/config/db.config.js

Expose the Mongo DB URI from your environment file

module.exports = {
  url: process.env.MONGO_DB_URI
};
Enter fullscreen mode Exit fullscreen mode

/app/controller/blog.controller.js

In this directory we will define how we want to handle the following actions:

  1. create
  2. findAll
  3. findOne
  4. update
  5. delete
  6. deleteAll
const db = require("../models");
const Blog = db.blog;

// Create and Save a new blog
exports.create = (req, res) => {
    // Validate request
  if (!req.body.content) {
    res.status(400).send({ message: "Content can not be empty!" });
    return;
  }

  // Create a blog
  const blog = new Blog({
    author: req.body.author,
    content: req.body.content,
    published: req.body.published ? req.body.published : false
  });

  // Save blog in the database
  blog
    .save(blog)
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while creating the blog."
      });
    });

};

// Retrieve all blogs from the database.
exports.findAll = (req, res) => {
    const content = req.query.content;
    var condition = content ? { content: { $regex: new RegExp(content), $options: "i" } } : {};

    Blog.find(condition)
      .then(data => {
        res.send(data);
      })
      .catch(err => {
        res.status(500).send({
          message:
            err.message || "Some error occurred while retrieving blogs."
        });
      });

};

// Find a single blog with an id
exports.findOne = (req, res) => {
    const id = req.params.id;

    Blog.findById(id)
      .then(data => {
        if (!data)
          res.status(404).send({ message: "Not found blog with id " + id });
        else res.send(data);
      })
      .catch(err => {
        res
          .status(500)
          .send({ message: "Error retrieving blog with id=" + id });
      });

};

// Update a blog by the id in the request
exports.update = (req, res) => {
    if (!req.body) {
        return res.status(400).send({
          message: "Data to update can not be empty!"
        });
      }

      const id = req.params.id;

      Blog.findByIdAndUpdate(id, req.body, { useFindAndModify: false })
        .then(data => {
          if (!data) {
            res.status(404).send({
              message: `Cannot update Blog with id=${id}. Maybe Blog was not found!`
            });
          } else res.send({ message: "Blog was updated successfully." });
        })
        .catch(err => {
          res.status(500).send({
            message: "Error updating Blog with id=" + id
          });
        });

};

// Delete a blog with the specified id in the request
exports.delete = (req, res) => {
    const id = req.params.id;

    Blog.findByIdAndRemove(id)
      .then(data => {
        if (!data) {
          res.status(404).send({
            message: `Cannot delete Blog with id=${id}. Maybe Blog was not found!`
          });
        } else {
          res.send({
            message: "Blog was deleted successfully!"
          });
        }
      })
      .catch(err => {
        res.status(500).send({
          message: "Could not delete Tutorial with id=" + id
        });
      });

};

// Delete all blogs from the database.
exports.deleteAll = (req, res) => {
    Blog.deleteMany({})
    .then(data => {
      res.send({
        message: `${data.deletedCount} Blogs were deleted successfully!`
      });
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while removing all blogs."
      });
    });
};

// Find all published blogs
exports.findAllPublished = (req, res) => {
    Blog.find({ published: true })
    .then(data => {
      res.send(data);
    })
    .catch(err => {
      res.status(500).send({
        message:
          err.message || "Some error occurred while retrieving blogs."
      });
    });
};
Enter fullscreen mode Exit fullscreen mode

/app/model/index.js

In this directory, we are going to assemble the following files:

  1. /app/config/db.config
  2. /app/model/blog.model.js

Ensure you import this directory in your index file and connect to your Mongo DB.

const dbConfig = require("../config/db.config");

const mongoose = require("mongoose");
mongoose.Promise = global.Promise;

const db = {};
db.mongoose = mongoose;
db.url = dbConfig.url;
db.blog = require("./blog.model.js")(mongoose);

module.exports = db;
Enter fullscreen mode Exit fullscreen mode

/app/model/blog.model.js

Our blog schema will be in this directory.

module.exports = mongoose => {
    const Blog = mongoose.model(
      "blog",
      mongoose.Schema(
        {
          author: String,
          content: String,
          published: Boolean
        },
        { timestamps: true }
      )
    );

    // We can add a category to categorize articles

    // Incase you want to replace _.id with id 
    // schema.method("toJSON", function() {
    //   const { __v, _id, ...object } = this.toObject();
    //   object.id = _id;
    //   return object;
    // });

    // const Blog = mongoose.model("blog", schema);

    return Blog;
  };
Enter fullscreen mode Exit fullscreen mode

/app/routes/blog.js

This will handle all our routes. Ensure you export this file in the index.js file.

const express = require("express")
const router = express.Router()
const blog = require("../controller/blog.controller");

// /api/blog: GET, POST, DELETE
// /api/blog/:id: GET, PUT, DELETE
// /api/blog/published: GET

// Create a new blog
router.post("/", blog.create);

// Retrieve all blog
router.get("/", blog.findAll);

// Retrieve all published blog
router.get("/published", blog.findAllPublished);

// Retrieve a single blog with id
router.get("/:id", blog.findOne);

// Update a Tutorial with id
router.put("/:id", blog.update);

// Delete a Tutorial with id
router.delete("/:id", blog.delete);

// Create a new Tutorial
router.delete("/", blog.deleteAll);

module.exports = router
Enter fullscreen mode Exit fullscreen mode

Now that we have our backend ready we can integrate the backend and the front-end. You can test your routes using postman or any other tool out there.

Remember our routes are currently not protected therefore you can not go to production like this. We will need to protect our routes and add authentication to our application. In the next article, we are going to handle this.

Open your front-end directory in a new window of vs-code or whatever text editor you are using and run the application.

$ cd .. && cd /blog-vue-front-end
$ npm run serve
// Your front-end will be running on PORT 8080 || PORT 8081
// Your back-end will be running on PORT 3000
Enter fullscreen mode Exit fullscreen mode

Once the application is running let us create the following file in the components folder:

/components/mongo-express-script.js

This will hold all our requests to the backend for mongo-express-script.js.

Install axios in the front-end

$ yarn add axios
Enter fullscreen mode Exit fullscreen mode

In the mongo-express-script.js file add the following:

import axios from "axios";

// create new blog
export const createnewblog = async item => {
  let data = {
    author: JSON.stringify({
      name: item.author.name,
      email: item.author.email,
      about: item.author.about
    }), // replace with auth user
    content: JSON.stringify({
      title: item.content.title,
      src: item.content.src,
      text: item.content.text
    })
  };
  let request = {
    url: "http://localhost:3000/api/blog", // should be replaced after going to production with domain url
    method: "post",
    headers: {
      "Content-type": "application/json"
    },
    data: JSON.stringify(data)
  };

  const response = await axios(request);
  return response;
};

// delete blog
export const deleteblog = async item => {
  let request = {
    url: "http://localhost:3000/api/blog/" + item, // should be replaced after going to production with domain url
    method: "delete",
    headers: {
      "Content-type": "application/json"
    }
  };

  const response = await axios(request);
  return response;
};

// update blog
export const updateblog = async item => {
  let data = {
    author: JSON.stringify({
      name: item.author.name,
      email: item.author.email,
      about: item.author.about
    }), // replace with auth user
    content: JSON.stringify({
      title: item.content.title,
      src: item.content.src,
      text: item.content.text
    }),
    published: item.published
  };
  let request = {
    url: "http://localhost:3000/api/blog/" + item._id, // should be replaced after going to production with domain url
    method: "put",
    headers: {
      "Content-type": "application/json"
    },
    data: JSON.stringify(data)
  };

  const response = await axios(request);
  return response;
};

// get all blog
export const retriveallblog = async () => {
  let request = {
    url: "http://localhost:3000/api/blog", // should be replaced after going to production with domain url
    method: "get",
    headers: {
      "Content-type": "application/json"
    }
  };

  const response = await axios(request);
  return response;
};


Enter fullscreen mode Exit fullscreen mode

Here is how the blog should be working currently with your backend setup. We have completed the integration. Ensure your backend and front end are running concurrently.

What's next?. We need to protect our routes and authentication and go to production.

Here is a repo for the backend https://github.com/kevinodongo/tutorial-blog-backend.git.

Currently, we have not gone to production yet and therefore we can still use the Container or Server approach.

Thank you

Discussion (0)