Understanding MVC pattern in Nodejs
Tags: node, javascript, webdev, mongodb
Published: 2021-01-06 18:52:44


This is part two of the [Test-driven Development with Nodejs, Express, Mongoose & Jest](https://dev.to/eaetukudo/test-driven-development-with-nodejs-express-mongoose-jest-1l42), in part one, we set up our development environment and ran our first test. In this tutorial, we will focus on building the endpoints using the MVC Architecture.

### Definition of terms

**MVC - Model View Controler**

Model View Controller is a software architectural pattern that involves the separation of the application logic into three interconnected elements the Model, View, and Controller.

**Restful API**

REST is an acronym for Representation State Transfer, API on the other hand is an acronym for Application Programme Interface. A RESTful API is an architectural style for an application program interface (API) that uses HTTP requests to access and use data. 

I surmised you are familiar with the terms involved in this tutorial. Let's get started.

In the previous tutorial, we had a very basic folder structure, with few dependencies. Let's install the required dependencies for our API development.


```bash
$ npm i --save-dev body-parser dotenv nodemon
``` 

After installation, your package.json file should look like the one below.

```json
{
  "name": "tdd-with-nodejs",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest",
    "start": "nodemon index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "body-parser": "^1.19.0",
    "dotenv": "^8.2.0",
    "express": "^4.17.1",
    "jest": "^26.6.3",
    "mongoose": "^5.11.9",
    "nodemon": "^2.0.6"
  }
}

``` 

You notice we have configured our initialization script to run with nodemon, nodemon will keep track of every change made to our index.js file and refresh our application accordingly. Next, Let's set up a server. create a new file within the root directory of your application named** index.js** and paste the code below.
 
```javascript
require('dotenv').config();
const mongoose =  require("mongoose");
//const articles = require("./routes/article.routes");
const bodyParser =  require("body-parser");

const app = exepress();
const port = 8000;

mongoose.connect(process.env.mongoURI, {useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true })
.then(res => console.log(`Connection Succesful ${res}`))
.catch(err => console.log(`Error in DB connection ${err}`));

//body-parser config;
app.use(exepress.json());
app.use(bodyParser.urlencoded({extended: true }));
app.use(bodyParser.json());

app.get("/", (req, res) => {
    res.send(`<h1>Hello!</h1>`)
});

app.listen(port, () => {
    console.log(`Application is listening at port ${port}`);
});

//register the enpoints
//app.use("/api/v1/articles", articles);

``` 

Don't forget to create a **.env** file and add your database URI like so: **mongoURI=mongodb+srv://your-db-uri**. Next, start the application by typing the command below on your terminal. 

```bash
$ npm run start
``` 

You should get a response on your terminal that reads: **Application is listening at port 8000** & **Connection Succesful [object Object]**.  If you open **http://localhost:8000** you should get **"Hello!"** logged to your screen as well. 

This tutorial intends to teach you how to properly structure your **Nodjs** application to fit into the MVC pattern, so, we would be separating our business logic from our controller and routes files. We will learn more about this in the Layered Structure tutorial which is the final tutorial for this series.

### Building the Model (Article.js)

Next, let's create our Model. Create a new folder within the root directory of the project, navigate into the folder, and create a new file named **Article.js**, and copy-paste the code below to create the model for our API.


```javascript
const mongoose = require("mongoose");
const Schema = mongoose.Schema;


const articleSchema = Schema({

    title:{
        type: String,
        required: true,
    },

    body:{
        type: String,
        required: true,
    },

    article_image: {
        type: String,
        required: false,
    },

    date:{
        type: Date,
        default: Date.now(),
    }

});


module.exports = Article = mongoose.model("Article", articleSchema);
```
Our model is very **basic**, it has a **title**,  **body**, and **date** object. You can learn more about building **MongoDB** Schemas using Mongoose by reading the official doc  [here](https://mongoosejs.com/docs/guide.html).

### Building the Article Service (ArticleService.js)

To build the **ArticleService.js** you need to create a folder named services to house our **ArticleService.js** file. Copy-paste the code below into your **ArticleService.js**.

```javascript
const Article = require("../models/Article");

module.exports = class ArticleService{
    static async getAllArticles(){
        try {
            const allArticles = await  Article.find();
            return allArticles;
        } catch (error) {
            console.log(`Could not fetch articles ${error}`)
        }
    }

    static async createArticle(data){
        try {

            const newArticle = {
                title: data.title,
                body: data.body,
                article_image: data.article_image
            }
           const response = await new Article(newArticle).save();
           return response;
        } catch (error) {
            console.log(error);
        } 
        
    }
    static async getArticlebyId(articleId){
        try {
            const singleArticleResponse =  await Article.findById({_id: articleId});
            return singleArticleResponse;
        } catch (error) {
            console.log(`Article not found. ${error}`)
        }
    }

    static async updateArticle(title, body, articleImage){
            try {
                const updateResponse =  await Article.updateOne(
                    {title, body, articleImage}, 
                    {$set: {date: new Date.now()}});

                    return updateResponse;
            } catch (error) {
                console.log(`Could not update Article ${error}` );
                
        }
    }

    static async deleteArticle(articleId){
        try {
            const deletedResponse = await Article.findOneAndDelete(articleId);
            return deletedResponse;
        } catch (error) {
            console.log(`Could  ot delete article ${error}`);
        }

    }
}
```

### Building the controller (article.controller.js)

Next, lets' start writing our API endpoints, create a new folder within your root directory named **controllers**, navigate into the folder, and create a new file named **article.controller.js**. copy-paste the code below.


```javascript
const ArticleService = require("../services/ArticleService");

module.exports = class Article{

   static async apiGetAllArticles(req, res, next){
       try {
         const articles = await ArticleService.getAllArticles();
         if(!articles){
            res.status(404).json("There are no article published yet!")
         }
         res.json(articles);
       } catch (error) {
          res.status(500).json({error: error})
       }
        
   }

   static async apiGetArticleById(req, res, next){
      try {
         let id = req.params.id || {};
         const article = await ArticleService.getArticlebyId(id);
         res.json(article);
      } catch (error) {
         res.status(500).json({error: error})
      }
   }

   static async apiCreateArticle(req, res, next){
      try {
         const createdArticle =  await ArticleService.createArticle(req.body);
         res.json(createdArticle);
      } catch (error) {
         res.status(500).json({error: error});
      }
   }

   static async apiUpdateArticle(req, res, next){
      try {
         const comment = {}
         comment.title        = req.body.title;
         comment.body         = req.body.body;
         comment.articleImage = req.body.article_image
         
         const updatedArticle = await ArticleService.updateArticle(comment);
         
         if(updatedArticle.modifiedCount === 0){
            throw new Error("Unable to update article, error occord");
         }

         res.json(updatedArticle);

      } catch (error) {
         res.status(500).json({error: error});
      }
   }

   static async apiDeleteArticle(req, res, next){
         try {
            const articleId = req.params.id;
            const deleteResponse =  await ArticleService.deleteArticle(articleId)
            res.json(deleteResponse);
         } catch (error) {
            res.status(500).json({error: error})
         }
   }

}

``` 
### Building the routes (article.routes.js)

To communicate with our endpoints we need to set up our routes with corresponding requests. Create a new folder named **routes**, inside the folder, create a new file named **article.routes.js**. You can choose to name the folder whatever you like but it is always nice to maintain meaningful directory and file names.

```javascript
const  express =  require("express");
const router = express.Router();
const ArticleCtrl = require("../controllers/article.controller");


router.get("/", ArticleCtrl.apiGetAllArticles);
router.post("/", ArticleCtrl.apiCreateArticle);
router.get("/article/:id", ArticleCtrl.apiGetArticleById);
router.put("/article/:id", ArticleCtrl.apiUpdateArticle);
router.delete("/article/:id", ArticleCtrl.apiDeleteArticle);

module.exports =  router;
```

If you've followed the tutorial up to this point, your folder structure should actually look like the snippet below.

```
├── tdd-with-nodejs
 ├── controllers
     ├── article.controller.js
 ├── models
     ├── Article.js
 ├── routes
     ├── article.routes.js
 ├── services
     ├── articleService.js
├── test
``` 

### Putting it all together

When working with the MVC pattern always ensure you maintain the Separation of Concern (SoC) technique. Separation of concerns (SoC) is a design principle for separating software applications into distinct sections such that each section addresses a separate concern. A concern is a set of information that affects the code of a Software Application. We will dive deeper into this topic in the next tutorial which is the last for this series.  

Before we conclude let's test one of the endpoints. Send a **POST** request to the **/api/v1/articles** endpoint using an API testing tool of your choice. In my case, **Postman**  You should get the response object as a response for your newly created article similar to the snippet below. 

![Screenshot 2021-01-06 at 18.25.03.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1609954092824/Ye7AcjIb9.png)

That's all for this tutorial. congratulations on making it thus far in this series. See you in the next tutorial.