# 3.1.1 : MVC

## Introduction

MVC stands for Model View Controller, these represent 3 logical component of web applications. We use the MVC mental model to refactor our code into multiple files and folders. The MVC concept helps us separate concerns in our web apps but does not strictly define what logic goes in which files, because different web frameworks have slightly different connections.

### Example Code for Grocery Application&#x20;

We will give you the steps to develop an example grocery store application that contains a single model. You can find a repository containing the example application [here](https://github.com/rocketacademy/MVC_Grocery_Example).&#x20;

This application is composed of a backend and a frontend, the backend will contain the Model and Controllers while the frontend handles the View.&#x20;

Here are some of the functionalities of the backend of the application.&#x20;

1. Create a consumable GET API that sends a list of the product stored within the database
2. Create a consumable POST API that allows users to add new products into the database
3. Create a consumable GET API that  retrieves a single product from the database

When  using the MVC setup for your application to add a new product into the database through the API,  our applications will preform these actions:

1. The React view captures the users input and sends this data to the controller as a POST request.&#x20;
2. The controller (which is based on the server) will alter the current model (database) to insert the new product into the database.
3. Once the update is completed, the controller retrieves the recently added product and sends it back to the view as a JSON response.&#x20;
4. The view then is able to update its internal state such that the most current information represented in the server model will be rendered onto the screen.&#x20;

## Implementing the Grocery Application

Now let's get our hands dirty and start to develop a grocery application that follows the MVC setup. We are going to need to set up a nodeJs project for our application, with this in mind we will also need to implement Sequelize database, complete with a migration, model and seed file. If you want a refresher, please look at this [material](https://bc.rocketacademy.co/3-backend/3.e-exercises/3.e.2-bigfoot-sql#reference-only-sequelize-setup). You will need to create and alter a .env file so you can protect your sensitive data. [Look here](https://bc.rocketacademy.co/2-full-stack/2.2-advanced-react/2.2.5-environmental-variables) if you want to remember how to use the .env. We will also be setting up the configuration slightly differently so please, follow closely.&#x20;

### Setting up the Backend &#x20;

Firstly, find the place on your machine where you want to develop, and create a directory there. When we are setting up the backend we are developing the Model and Controller of the application. We will be using Reactjs to develop our frontend and View. In this current directory make a new folder named `grocery_back` to store your backend and `cd` into it.&#x20;

### Setting up the Database

Before you attempt to setup the database with the sequelize-cli you will need to ensure your Postgresql server is running.

Windows users run these commands:

```
sudo service postgresql start
```

```
sudo su postgres
```

```
psql postgres
```

Macos, make sure your Postgres application is running.&#x20;

Now you can run these commands to set up your backend:

`npm init -y`

`npm i sequelize pg dotenv`

`npm i -D sequelize-cli`

Next create a new file inside `grocery_back` named .sequelizerc

The purpose of the .sequelizerc is to configure your application's connection to your database as well as setup the CLI such that files are created in the correct directories. If you would like to see the other configurations that are possible look [here](https://sequelize.org/docs/v6/other-topics/migrations/#the-sequelizerc-file). &#x20;

Make the file appear as below:

```javascript
const path = require("path");

module.exports = {
  config: path.resolve("config", "database.js"),
  "models-path": path.resolve("db", "models"),
  "seeders-path": path.resolve("db", "seeders"),
  "migrations-path": path.resolve("db", "migrations"),
};
```

Within the `grocery_back` directory run the command:

`npx sequelize init`

The db folder that is generated will contain these folders:

* `config`, contains config file, which tells CLI how to connect with database
* `models`, contains all models for your project
* `migrations`, contains all migration files
* `seeders`, contains all seed files

Alter the database.js that is stored within the config folder. You will need to reference the .env that you setup earlier in this file.&#x20;

The database.js should look like below:

{% code title="db/config/database.js" %}

```javascript
require("dotenv").config();

module.exports = {
  development: {
    username: process.env.DB_USERNAME,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME,
    host: process.env.DB_HOST,
    dialect: process.env.DB_DIALECT,
  },
};
```

{% endcode %}

Now you can actually create your database, we will need to set up migrations, models and seeds before we can interact with a real database.&#x20;

At this stage we can create a database within the  `grocery_back`  directory run this command:

`npx sequelize db:create`

### &#x20;Create Database Migrations:

New let's set up our migration file which will be used to create our table in Sequelize. Within the  `grocery_back` directory run this command:

`npx sequelize migration:generate --name products`

The command above should create a new migration file within the db directory, inside the migration folder, `db/migrations/...`. Edit the newly generated file so it looks like below:

{% code title="" %}

```javascript
"use strict";

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.createTable("products", {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: Sequelize.INTEGER,
      },
      name: {
        type: Sequelize.STRING,
      },
      price: {
        type: Sequelize.INTEGER,
      },
      created_at: {
        type: Sequelize.DATE,
        allowNull: false,
      },
      updated_at: {
        type: Sequelize.DATE,
        allowNull: false,
      },
    });
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.dropTable("products");
  },
};
```

{% endcode %}

After completing this set up we will be able to run our migration file and create a table within our database, within the  `grocery_back`  directory run this command:

`npx sequelize db:migrate`

After running our migration we should develop our product model, which will enable our controller to easily interface with the data stored within the table.&#x20;

### Create Database Model:

Create a file named product.js within the `db/models` folder.&#x20;

The product.js file should look similar to below:

{% code title="db/models/product.js" %}

```javascript
"use strict";

const { Model } = require("sequelize");
module.exports = (sequelize, DataTypes) => {
  class Product extends Model {}
  Product.init(
    {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        type: DataTypes.STRING,
      },
      price: {
        type: DataTypes.INTEGER,
      },
      createdAt: {
        type: DataTypes.DATE,
        allowNull: false,
        defaultValue: new Date(),
      },
      updatedAt: {
        type: DataTypes.DATE,
        allowNull: false,
        defaultValue: new Date(),
      },
    },
    {
      sequelize,
      modelName: "product",
      underscored: true,
    }
  );
  return Product;
};
```

{% endcode %}

We will also need to make sure that we have an index.js that will be used to process all of your models and give their Sequelize context to the application.&#x20;

The index.js will need to be within the models directory that should be implemented as below:

{% code title="db/models/index.js" %}

```javascript
"use strict";

const fs = require("fs");
const path = require("path");
const Sequelize = require("sequelize");
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || "development";
const config = require("../../config/database.js")[env];
const db = {};

let sequelize;
if (config.use_env_variable) {
  sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
  sequelize = new Sequelize(
    config.database,
    config.username,
    config.password,
    config
  );
}

fs.readdirSync(__dirname)
  .filter((file) => {
    return (
      file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
    );
  })
  .forEach((file) => {
    const model = require(path.join(__dirname, file))(
      sequelize,
      Sequelize.DataTypes
    );
    db[model.name] = model;
  });

Object.keys(db).forEach((modelName) => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;
```

{% endcode %}

Now that we have developed our Model, for product we can create our seed file to help populate our database. Within the  `grocery_back`  directory run the command below:

### Create Database Seeders:

`npx sequelize seed:generate --name products`

The command above should create a new seed file within the folder `db/seeders/`, we will use this file to create some sample data for the application.&#x20;

Edit the newly generated file so that it looks like below:

```javascript
"use strict";

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.bulkInsert("products", [
      {
        name: "Doritos",
        price: 15,
        created_at: new Date(),
        updated_at: new Date(),
      },
      {
        name: "Banana",
        price: 10,
        created_at: new Date(),
        updated_at: new Date(),
      },
      {
        name: "Apple",
        price: 10,
        created_at: new Date(),
        updated_at: new Date(),
      },
      {
        name: "Iphone",
        price: 11500,
        created_at: new Date(),
        updated_at: new Date(),
      },
      {
        name: "Cheese",
        price: 50,
        created_at: new Date(),
        updated_at: new Date(),
      },
    ]);
  },

  async down(queryInterface, Sequelize) {
    await queryInterface.bulkDelete("products", null, {});
  },
};
```

After completing this set up we will be able to run our seeder file and populate our product table within our database, within the  `grocery_back`  directory run this command:

`npx sequelize db:seed:all`

Now that we have setup and populated our database we will need to develop an express server that can interact with it.&#x20;

#### <mark style="color:red;">Note</mark>

The code below will implement classes within our applications. If it looks unfamiliar have a look at the [Rocket curriculum](https://bc.rocketacademy.co/0-foundations/0.4-javascript/0.4.4-classes) to touch up your understanding.

### Setting up Routes:

We should develop router files to keep HTTP method and URL path matching outside of the index.js. In this current project we will develop one router file, but generally every router and controller refer to a single type of data stored in your database. In this case we only have a product so only the product router and controller are required.&#x20;

ProductRouter.js is a file that will bind the controller methods such that they are given the express http request and response context. Meaning we can link an API call to our controller to update our database.&#x20;

Please create a Routers directory in the  `grocery_back` directory and create a ProductRouter.js file within, it should be similar to the code below:

{% code title="/Routers/ProductRouter.js" %}

```javascript
class ProductsRouter {
  constructor(express, controller) {
    this.express = express;
    this.controller = controller;
  }

  routes() {
    const router = this.express.Router();

    router.get("/", this.controller.getAll.bind(this.controller));
    router.get("/:productId", this.controller.getOne.bind(this.controller));
    router.post("/", this.controller.insertOne.bind(this.controller));
    return router;
  }
}

module.exports = ProductsRouter;

```

{% endcode %}

### Setting Up Controllers:

At this point in development we have setup our database, and express application with routes. Now we need to develop the controller linked up to the API routes defined above. Lets develop some controller methods to handle our requests and send back proper responses.&#x20;

Each feature or data source can have its own controller. Create a new folder inside the  `grocery_back` directory  named Controllers, create two files within this folder, named ProductController.js and BaseController.js.

We will setup the BaseController.js first as we will be creating a class template that can be used for every subsequent controller we need to develop. Please make the file similar to the code below:

{% code title="/Controllers/BaseController.js" %}

```javascript
class BaseController {
  constructor(model) {
    this.model = model;
  }

  async getAll(req, res) {
    console.log(this.model);
    try {
      const output = await this.model.findAll();
      return res.json(output);
    } catch (err) {
      console.log(err);
      return res.status(400).json({ error: true, msg: err });
    }
  }
}

module.exports = BaseController;
```

{% endcode %}

We can model the ProductsController class on the BaseController class, please implement the file ProductsController.js as below:

{% code title="/Controllers/ProductsController.js" %}

```javascript
const BaseController = require("./baseController");

class ProductsController extends BaseController {
  constructor(model) {
    super(model);
  }

  async insertOne(req, res) {
    const { name, price } = req.body;
    try {
      const newProduct = await this.model.create({
        updated_at: new Date(),
        created_at: new Date(),
        name: name,
        price: price,
      });
      return res.json(newProduct);
    } catch (err) {
      return res.status(400).json({ error: true, msg: err });
    }
  }

  async getOne(req, res) {
    const id = req.params.productId;
    try {
      const output = await this.model.findByPk(id);
      return res.json(output);
    } catch (err) {
      console.log(err);
      return res.status(400).json({ error: true, msg: err });
    }
  }
}

module.exports = ProductsController;

```

{% endcode %}

### Setup Express Js Server:

Ensure that your CLI is within the  `grocery_back` directory and run this command:

`npm i express cors`

This will install the packages, `express` and `cors`, express will be used to power our application and cors is used to facilitate communication between our front and backend.&#x20;

Lets make a new file within the  `grocery_back`  directory named `index.js` the document should be as below:

{% code title="index.js" %}

```javascript
const express = require("express");
const cors = require("cors");
require("dotenv").config();

const db = require("./db/models/index");
const { product } = db;

const ProductsRouter = require("./routers/productsRouter");
const ProductsController = require("./controllers/productsController");

const PORT = process.env.PORT || 3000;

const app = express();

const productsController = new ProductsController(product);
const productsRouter = new ProductsRouter(express, productsController).routes();

app.use(cors());
app.use(express.json());

app.use("/products", productsRouter);

app.listen(PORT, () => {
  console.log("Application listening to port 3000");
});

```

{% endcode %}

As you can see from the file above we still need to implement a few files in order to make our MVC application work. We will define a router system as well as a Controller within our express application. We try to reduce the size of the index.js, only initialising what is required and implementing middleware. This file structure enables multiple developers to work concurrently with minimal interference. If we require additional middleware like auth middleware we can import it and bind it to the application within the index.js.&#x20;

### Running Backend Application

Provided the you have installed all of the required dependencies and you have implemented the backend of this application by following the steps above, we should be able to run the application, from the  `grocery_back`  directory, run this command:

`nodemon index.js`

OR (if you don't have nodemon installed

`node index.js`

You can test your 'Model' and 'Controller' by using [ThunderClient](https://bootcamp.rocketacademy.co/2-full-stack/2.1-internet-101/2.1.2-http-requests-and-responses#thunder-client), please test out your routes and ensure you can send and retrieve data from your database before moving to the next section. \
To test out the GET request we need to fire off a request to the URL <http://localhost:3000/products>, this will respond with a list of all of the products from the backend. It is able to do this because the route handler fires off the getAll method within the controller and returns the data which is sent back to the client, in this case ThunderClient.\
In order to test out the POST request you need to alter the request within ThunderClient to send a POST request not a GET request. We can do this at the top of the window, with this in mind as the POST request is mocking a form submission we will need to attach the form data you can use a JSON object to achieve this. Remember that the data you add will interface with your database and thus the data's keys need to match column names. When ThunderClient sends this request the insertOne method within the controller is fired off, adding a new fruit into the database, that being said, the response is the newly updated products list.&#x20;

### Model

The Model logical component in the MVC refers to the structure of data in our application, and is the component responsible for manipulating data in the database. In this Coding Bootcamp we will use the Sequelize library to power our model architecture, though it should be noted business logic is tired to the controller.  ‘Model’ in MVC refers to the structure of data, as well as how it is stored and queried. Other non-sql database have a variation of Sequelize's model that is used to query tables and data.

### View

View refers to application UI. We’ve already defined views in the ‘views’ folder with JS files. MVC distinguishes between “view logic” and “application logic”. View logic determines how data should be rendered and formatted, e.g. transforming data format without changing the underlying value. Application logic determines how data should be calculated and stored. Views typically contain view logic, and controllers typically contain application logic. We are already developing the frontend of our applications, using the Create-React-App, which is our 'View' within the MVC model.&#x20;

The following are examples of view logic.

1. Uppercasing a post title
2. Shortening post content to fit into a table
3. Transforming a boolean value in the database to a contextual visual element, for example a heart icon for where a user has liked a post.&#x20;

### Controller

Controller refers to the business logic. Controllers are the glue between the model and view, and handle HTTP requests and responses. For example, a controller would determine if, when and how an app would respond with a 404 error message. In Bootcamp, controllers will contain the majority of out applications business logic, and generally everything not a model or view will go into a controller.&#x20;

### Routes

Other than model, views and controllers, we will also develop a router file or files that only connect requests to controllers via the requests’ HTTP method and URL path. This is what we have been doing with methods such as `app.get` and `app.post`. We can imagine route files as a directory of our server’s response logic.

### Setting up the React Application

The React application will be representative of the view that we are creating with the MVC application setup, it will communicate with the Controller through the backend server that was created earlier.&#x20;

The frontend React application will consume the  API’s served by the expressJS backend. It will make a GET request to retrieve all of the available products currently stored within the database. The application will allow users to send a POST request that will create a new item within the database.&#x20;

### Generating boilerplate with Create-React-App:

To develop the frontend of this application we will be using Vitejs. Make sure you are in the root of the project directory and not the `grocery_back` directory. Run this command:

```
npm create vite@latest
```

You will then be prompted for a name, we will call it `grocery_front`. Then choose `React`, then choose `JavaScript`, now follow the rest of the setup commands.&#x20;

Following this install `axios`.&#x20;

This will create a new React application on your machine within the `grocery_front` directory. We will need to alter files within this directory to implement our View.&#x20;

### Creating your View:

Let's first alter the App.jsx that is stored within the src directory. Make the file appear as below:&#x20;

{% code title="/src/App.js" %}

```javascript
import logo from "/logo.png";
import "./App.css";
import AddProduct from "./Components/AddProduct";
import SingleProduct from "./Components/SingleProduct";
import axios from "axios";
import { useState, useEffect } from "react";

export default function App() {
  const [openSingle, setOpenSingle] = useState(false);
  const [products, setProducts] = useState([]);
  const [currentId, setCurrentId] = useState("");

  const getInitialData = async () => {
    let initialAPICall = await axios.get(
      `${process.env.REACT_APP_API_SERVER}/products`
    );
    setProducts(initialAPICall.data);
  };

  useEffect(() => {
    getInitialData();
  }, []);

  const toggleView = (product) => {
    setOpenSingle(!openSingle);
    setCurrentId(product.id);
  };

  const createNewProduct = async (name, price) => {
    let product = {
      name,
      price,
    };
    let response = await axios.post(
      `${process.env.REACT_APP_API_SERVER}/products`,
      product
    );
    let newArray = [...products];
    newArray.push(response.data);
    setProducts(newArray);
  };

  return (
    <div className="App">
      <header className="App-header">
        {openSingle ? (
          <div>
            <SingleProduct toggle={toggleView} id={currentId} />
          </div>
        ) : (
          <div>
            <img src={logo} className="logo" alt="logo" />
            <h3>Grocery Store</h3>
            <h6>Products</h6>
            <div className="products-container">
              {products && products.length > 0 ? (
                products.map((product) => (
                  <div
                    className="product"
                    key={product.id}
                    onClick={() => toggleView(product)}
                  >
                    <h4>{product.name}</h4>
                    <h5>${product.price}</h5>
                  </div>
                ))
              ) : (
                <p>Failure</p>
              )}
            </div>
            <AddProduct addProduct={createNewProduct} />
          </div>
        )}
      </header>
    </div>
  );
}
```

{% endcode %}

Next create a Components folder within the src directory.

Inside this directory create two files, AddProduct.jsx and SingleProduct.jsx

Make the AddProduct.jsx appear as below:

{% code title="/src/Components/AddProduct.jsx" %}

```javascript
import { useState } from "react";

export default function AddProduct(props) {
  const [name, setName] = useState("");
  const [price, setPrice] = useState(1);

  const submit = () => {
    props.addProduct(name, price);
    setName("");
    setPrice("");
  };

  return (
    <div>
      <h3>Add Product Form</h3>
      <label>Product Name:</label>
      <br />
      <input
        type="text"
        value={name}
        placeholder="Add in product name"
        onChange={(e) => setName(e.target.value)}
      />
      <br />
      <label>Product Price:</label>
      <br />
      <input
        type="number"
        onChange={(e) => setPrice(e.target.value)}
        value={price}
      />
      <br />
      <button onClick={submit}>Add Product</button>
    </div>
  );
}

```

{% endcode %}

Now make the SingleProduct.jsx appear as below:

{% code title="/src/Components/SingleProduct.jsx" %}

```javascript
import React from "react";
import { useState, useEffect } from "react";
import axios from "axios";

export default function SingleProduct(props) {
  const [product, setProduct] = useState({});

  const getProduct = async () => {
    let response = await axios.get(
      `${process.env.REACT_APP_API_SERVER}/products/${props.id}`
    );
    setProduct(response.data);
  };

  useEffect(() => {
    getProduct();
  }, []);

  return (
    <>
      <h1>Single</h1>
      <h2>{product.name}</h2>
      <h3>{product.price}</h3>
      <button onClick={() => props.toggle(product)}>Go Back</button>
    </>
  );
}
```

{% endcode %}

We will also need to create a .env file that will be stored within the folder `grocery_front`. It should appear as below:

{% code title=".env" %}

```
VITE_SOME_API_SERVER=http://localhost:3000
```

{% endcode %}

### Running your React Application

At this stage you should be able to run your frontend react application. Run the command within grocery\_front directory:

`npm run dev`

You need to open a browser of your choice and navigate to `http://localhost:5173`.

You should see the project open itself within your default browser, you should be able to add a product as well as click into a single product.&#x20;

## Exercise

Implement the project above, once you understand how it works and you've run both the frontend and backend add in some additional features.

On the frontend, in the SingleProduct Component make it so you can edit the selected item's name or price, capture user input and send an API request. In the App.jsx make it so you can delete an item from the frontend, send an API request to alter the 'model'.

To add additional consumable API's you will also need to set up new methods within the controller as well as new routes in the router.&#x20;

## Fruit Application Controller Creation

{% embed url="<https://youtu.be/ci80xB97L3o>" %}
Sequelize and Controllers (1)
{% endembed %}

{% embed url="<https://youtu.be/0j2fZqhRX4M>" %}
Sequelize and Controller (2)
{% endembed %}

## Testing Fruit Application with Thunder Client

{% embed url="<https://youtu.be/q_9qjLDU84A>" %}
Testing Application (1)
{% endembed %}

{% embed url="<https://youtu.be/CuuZylrvScU>" %}
Testing Application (2)
{% endembed %}

Please checkout the finished code in this [repository](https://github.com/rocketacademy/m3_sequelize_repo/tree/sequelize_controller), ensure that you're on the `sequelize_controller` branch. To test out this repo, you will need to setup your `.env`, install the required dependencies with `npm install`,  then run all migrations and seeders such that you can run the application while its connected to your local database. Then you can run `node index.js .`

## Building Fruit Application Frontend (Legacy CRA)&#x20;

{% embed url="<https://youtu.be/zmsso0wTwVU>" %}
Create React App Fruit Application
{% endembed %}

{% embed url="<https://youtu.be/l7GMh-mXgts>" %}
React Componnets: Fruit && FruitCard
{% endembed %}

{% embed url="<https://youtu.be/nzxAOJl75xo>" %}
Calling API and adding CORS to backend
{% endembed %}

{% embed url="<https://youtu.be/Hs0OSkVNJ04>" %}
Frontend Form Component
{% endembed %}

{% embed url="<https://youtu.be/Y13ydLgeKRs>" %}
Finsihing Frontend Form Component
{% endembed %}

{% embed url="<https://youtu.be/mGnnamK6RTI>" %}
Testing Application and Framing Challenge&#x20;
{% endembed %}

Please checkout the finished frontend code in this [repository](https://github.com/rocketacademy/3.2_react_repo), ensure that you're on the `main` branch if you want to test the code on your machine you will need to install the dependencies with the command `npm install` after the installation you can run the application with `npm run dev`. To test it with a backend please checkout this [repository](https://github.com/rocketacademy/m3_sequelize_repo/tree/cors), ensure that you're on the `cors` branch if you want to test the code on your machine you will need to install the dependencies with the command `npm install` after the installation, then implement you `.env.` If you've not setup the database previously, run your migrations and seeders and once this is completed you can run the application with `node index.js`.&#x20;
