We will add controllers and use MVC. You can have one to one mapping between routes and controllers but you can split it also differently.
We will create controller for admin and controller for frontend. We will store products in filesystem.


Model

  • responsible for representing your data
  • responsible for managing your data (saving, fetching)
  • doesnt matter if you manage data in memory, files, db
  • contains data-related logic


View

  • what the user sees
  • shouldnt contain too much logic


Controller

  • connects Model and View
  • should only make sure that the two can communicate (in both directions)


Routes - using Router and Path - Serving HTML pages

-routes
    |
    |---admin.js
    |---shop.js

-controllers
    |
    |---products.js

-models
    |
    |---product.js

-views
    |
    |---includes
    |       |
    |       |---head.ejs
    |       |---navigation.ejs
    |
    |---admin
            |
            |---shop.ejs
            |---add-product.ejs

-public
    |
    |---css
        |
        |---main.css
-app.js



models/product.js

//const products = []; //old approach saving products to array
const fs = require('fs');
const path = require('path');

const p = path.join(path.dirname(process.mainModule.filename), 'data', 'products.json');

const getProductsFromFile = (cb) => {
    fs.readFile(p, (err, fileContent) => {
        if (err) {
            return cb([]);
        }
        cb(JSON.parse(fileContent));
    })
}

module.exports = class Product {
    constructor(title) {
        this.title = title;
    }

    save() {
        //products.push(this);
        getProductsFromFile(products => {
            products.push(this);
            fs.writeFile(p, JSON.stringify(products), (err) => {
                console.log(err);
            });
        });
    }

    //static means that it will be not called on concrete instance of product model, but will return all products
    //cb = callback function
    static fetchAll(cb) {
        getProductsFromFile(cb);
    }
}



controllers/products.js

const Product = require('../models/product' );

exports.getAddProduct = (req, res, next) => {
     res.render('add-product', {
        pageTitle: 'Add product',
        path: '/admin/add-product',
        activeAddProduct: true
    });
}

exports.postAddProduct = (req, res, next) => {
    const product = new Product(req.body.title);
    product.save();
    res.redirect('/');
}

export.getProducts =  (req, res, next) => {
    Product.fetchAll(products => {
        res.render('shop', {
            prods: products,
            pageTitle: 'shop',
            path: '/',
            hasProducts: products.length > 0,
            activeShop: true
        });
    });
}



routes/admin.js

const path = require('path');
const express = require('express');
const router = express.Router()
const productsController = require('../controllers/products');

router.get('/add-product', productsController.getAddProduct);

//this will trigger for only POST requests
router.post('/add-product', productsController.postAddProduct );

module.exports = router;



routes/shop.js

const path = require('path');
const express = require('express');
const productsController = require('../controllers/products');

router.get('/', productsController.getProducts);

module.exports = router;



views/includes/head.ejs

<!DOCTYPE html>
<html>
<head>
    <!-- we are using express.static in app.js to serve static css file from public dir -->
    <link rel="stylesheet" href="/css/main.css">



views/includes/navigation.ejs

<header>
    <nav>
        <ul>
            <li><a class="<%= path === '/' ? 'active' : '' %>" href="/">Shop</a></li>
            <li><a class="<%= path === '/admin/add-product' ? 'active' : '' %>" href="/admin/add-product">Add product</a></li>
        </ul>
    </nav>
</header>



views/add-product.ejs

<%- include('includes/head.ejs') %>
</head>
</head>
<body>
    <%- include('includes/navigation.ejs') %>

    <main>
        <form action="/admin/add-product" method="post">
            <input type="text" name="title">
            <button type="submit">Add product</button>
        </form>
    </main>
</body>
</html>



views/404.ejs

<%- include('includes/head.ejs') %>
</head>
<body>
    <%- include('includes/navigation.ejs') %>
    <h1>Page not found</h1>
</body>
</html>



views/shop.ejs

<%- include('includes/head.ejs') %>
</head>
</head>
<body>
    <%- include('includes/navigation.ejs') %>
    <main>
        <% if (prods.length > 0) { %>
            //render products
            <% for (let product of prods) { %>
                 <div>
                    <%= product.title %>
                 </div>
            <% } %>
        <% } else { %>
            <h1>No products found</h1>
        <% }  %>
    </main>
</body>
</html>



app.js

const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
const app = express();

//set template engine to EJS
app.set('view engine', 'ejs');
app.set('views', 'views');

const adminRoutes = require('./routes/admin');
const shopRoutes = require('./routes/shop');

app.use(bodyParser.urlencoded({exteneded: false}));

//add this to make public folder available to serve static files, like css
app.use(express.static(path.joing(__dirname, 'public')));

app.use('/admin', adminRoutes);
app.use(shopRoutes);

//404 error page, when i make request to path which doesnt exists
app.use((req, res, next) => {
    res.status(404).render('404', { pageTitle: 'Page Not Found' });
});

app.listen(3000);