Express JS session - remembering/logging every product that has been viewed while the session is active

I’m creating a store simulator that displays a list of products, as well as reviews for each product and users who bought the product. The initial data for the server is read from the products.json, reviews.json, and users.json files.

Right now, running localhost:3000 and then clicking on the products link leads me to a page that looks like this.

Screenshot

Clicking on a product (etc. the first one in the list) takes me to a page that looks like this.
Screenshot (1)

As you can see, it displays information about the product provided in the json file, and a list of user profile pages who have bought the specific product.

I’m trying to make the server remember the IDs of products the client has viewed while the session is active. To do this, I’m supposed to modify the sendSingleProduct middleware function in the products-router.js file to update the req.session object so that it remembers a list of product IDs that have
been viewed. For now, I am able to just print the list / log the list of the current session’s products every time a request is made.

I’m a little confused as to how I would edit the sendSingleProduct() function to achieve this. In the store-server.js page I added an express-session module and also try logging the session data for each request in your server by adding the following middleware function before the router code:

app.use(function(req, res, next){
   console.log(req.session);
   next();
})

Right now my cmd bar just updates with this new session line every time I click on a different page in the application (products, users, a specific product, etc).

I want the cmd bar to log something like this when I click on a specific product. So for example, clicking on the product “Unbranded Frozen Shirt” prints out all the associated information about the product, as found in the products.json file.

pic2

The products.json file is formatted below, but I only included 2 products for the sake of shortening this post:

{"0":
    {"id":0,
    "name":"Practical Concrete Shirt",
    "price":"635.00",
    "reviews":[],
    "buyers":["http://localhost:3000/users/62", "http://localhost:3000/users/154"],
    "url":"http://localhost:3000/products/0"},

"1":
    {"id":1,
    "name":"Sleek Concrete Tuna",
    "price":"376.00",
    "reviews":["http://localhost:3000/reviews/779"],
    "buyers":["http://localhost:3000/users/56","http://localhost:3000/users/94","http://localhost:3000/users/149","http://localhost:3000/users/212"],
    "url":"http://localhost:3000/products/1"}
}

store-server.js:

const session = require('express-session')
const express = require('express');
const app = express();
const port = 3000;

app.use(session({ secret: 'some secret key here'}))
app.use(express.json());


app.set("view engine", "pug");
app.locals.products = require("./products.json");
app.locals.users = require("./users.json");
app.locals.reviews = require("./reviews.json");

app.use(function(req, res, next){
  console.log(req.session);
  next();
})


//Require and mount the various routers
//There is one router for each main type of resource
let userRouter = require("./user-router");
app.use("/users", userRouter);
let productsRouter = require("./products-router");
app.use("/products", productsRouter);
let reviewsRouter = require("./reviews-router");
app.use("/reviews", reviewsRouter);

//Respond with home page data if requested
app.get("/", (req, res, next)=> { res.render("pages/index"); });

app.listen(port);
console.log(`Server listening at http://localhost:${port}`);

The function I’m supposed to edit to achieve this is in the sendSingleProduct() function which is the last function in products-router.js:

const express = require('express');
const faker = require('faker');
let router = express.Router();
let nextProductID = 1000;

//Requests for /products
//Specify three functions to handle in order
router.get("/", queryParser);
router.get("/", loadProducts);
router.get("/", respondProducts);

//You can also specify multiple functions in a row
router.post("/", express.json(), createProduct);

//Requests for a specific product
router.get("/:id", getProduct, sendSingleProduct);
router.put("/:id", express.json(), saveProduct);

//Parse the query parameters
//limit: integer specifying maximum number of results to send back
//page: the page of results to send back (start is (page-1)*limit)
//name: string to find in product name to be considered a match
//minprice: the minimum price to find
//maxprice: the maximum price to find
function queryParser(req, res, next){
    const MAX_PRODUCTS = 50;

    //build a query string to use for pagination later
    let params = [];
    for(prop in req.query){
        if(prop == "page"){
            continue;
        }
        params.push(prop + "=" + req.query[prop]);
    }
    //Add it to the request object so we can access in later middleware
    req.qstring = params.join("&");

    try{
        if(!req.query.limit){
            req.query.limit = 10;
        }

        req.query.limit = Number(req.query.limit);

        if(req.query.limit > MAX_PRODUCTS){
            req.query.limit = MAX_PRODUCTS;
        }
    }catch{
        req.query.limit = 10;
    }

    //Parse page parameter
    try{
        if(!req.query.page){
            req.query.page = 1;
        }

        req.query.page = Number(req.query.page);

        if(req.query.page < 1){
            req.query.page = 1;
        }
    }catch{
        req.query.page = 1;
    }

    if(req.query.minprice){
        try{
            req.query.minprice = Number(req.query.minprice);
        }catch(err){
            req.query.minprice = undefined;
        }
    }

    if(req.query.maxprice){
        try{
            req.query.maxprice = Number(req.query.maxprice);
        }catch{
            req.query.maxprice = undefined;
        }
    }

    if(!req.query.name){
        req.query.name = "*";
    }

    next();
}

//Loads a product by ID and adds it to request object
function getProduct(req, res, next){
    let id = req.params.id;
    if(req.app.locals.products.hasOwnProperty(id)){
        req.product = req.app.locals.products[id];
        next();
    }else{
        res.status(404).send("Could not find product.");
    }
}

//Saves a product using the request body
//Used for updating products with a PUT request
function saveProduct(req, res, next){
    let id = req.params.id;
    if(req.app.locals.products.hasOwnProperty(id)){
        req.app.locals.products[id] = req.body; //Really want to check validity here
        res.status(200).send("Product saved.");
    }else{
        res.status(404).send("Could not find product.");
    }
}

//Helper function for determining whether a product
// matches the query parameters. Compares the name,
// min price, and max price. All must be true.
//Different applications may have different logic
function productMatch(product, query){
    let nameCheck = query.name == "*" || product.name.toLowerCase().includes(query.name.toLowerCase());
    let minPriceCheck = (!query.minprice) || product.price >= query.minprice;
    let maxPriceCheck = (!query.maxprice) || product.price <= query.maxprice;
    return nameCheck && minPriceCheck && maxPriceCheck;
}

//Load the correct products into the result object
//Works similar to user router, but has different checks
// for product matching (min price, max price)
function loadProducts(req, res, next){
    let products = req.app.locals.products;
    let results = [];
    let startIndex = (req.query.page-1) * Number(req.query.limit);
    let endIndex = startIndex + Number(req.query.limit);
    let countLoaded = 0;
    let failed = false;

    let count = 0;

    for(let i = 0; i < Object.keys(products).length; i++){
        let product = products[Object.keys(products)[i]];
        //If the product matches the query parameters
        if(productMatch(product, req.query)){
            //Add to results if we are at the correct index
            if(count >= startIndex){
                results.push(product);
            }

            //Stop if we have the correct number of results
            if(results.length >= req.query.limit){
                break;
            }

            count++;
        }
    }

    //Set the property to be used in the response
    res.products = results;
    next();
}

//Sends an array of products in response to a request
//Uses the products property added by previous middleware
//Sends either JSON or HTML
function respondProducts(req, res, next){
    res.format({
        "text/html": () => {res.render("pages/products", {products: res.products, qstring: req.qstring, current: req.query.page } )},
        "application/json": () => {res.status(200).json(res.products)}
    });
    next();
}

//Create a new random product in response to POST /products
//In a real system, we would likely provide a page
// to specify a new products information
function createProduct(req, res, next){
    //Generate a random product
    let p = {};
    p.id = nextProductID;
    p.name = faker.commerce.productName();
    p.price = faker.commerce.price();
    p.reviews = [];
    p.buyers = [];

    nextProductID++;
    req.app.locals.products[p.id] = p;
    res.status(201).send(p);
}

//Send representation of a single product
//Sends either JSON or HTML
function sendSingleProduct(req, res, next){
    res.format({
        "application/json": function(){
            res.status(200).json(req.product);
        },
        "text/html": () => {
            res.render("pages/product", {product: req.product});
        }
    });
    next();
}

//Export the router so it can be mounted in the main app
module.exports = router;

Any help would be appreciated!! There’s no logging in / creating accounts for the users. I’m basically logging every product that has been clicked on once I run localhost:3000.

So is this some kind of assignment?


Did you look at the example given for req.session on the express-session page?