Expresso routes work but tests fail

I’m working on the capstone project for the SQL/Express portion of the Web Development path: https://www.codecademy.com/paths/web-development/tracks/building-a-persistent-api/modules/persistent-api-cumulative-projects/informationals/cumulative-project-expresso

I’ve tested my routes on Postman and in the front-end of the project, and they do all work, even with edge cases or missing information. What I can’t figure out is how to get (some of) them to pass the tests. For instance, with my PUT /api/menus/:menuId/menu-items/:menuItemId route, I get this error: PUT /api/menus/1/menu-items/2 404 2.032 ms - 9 5) should update the menu item with the given ID 6) "before each" hook for "should return a 200 status code after menuItem update"

This is my code:

menuItemsRouter.param('menuItemId', (req, res, next, menuItemId) => {
    db.get("SELECT * FROM MenuItem WHERE MenuItem.id = $menuItemId", 
    {
        $menuItemId: menuItemId
    }, 
    (err, row) => {
        if(err) {
            next(err); 
        } else if (row) {
            req.menuItem = row; 
            next(); 
        } else {
            res.sendStatus(404); 
        }
    })
})

menuItemsRouter.put('/:menuItemId', (req, res, next) => {
    const menuItemId = req.params.menuItemId; 
    const name = req.body.menuItem.name; 
    const description = req.body.menuItem.description; 
    const inventory = req.body.menuItem.inventory; 
    const price = req.body.menuItem.price; 
    const menuId = req.params.menuId; 
    if (!name || !inventory || !price || !menuId) {
        res.sendStatus(400); 
    } else {
        const menuSql = 'SELECT * FROM Menu WHERE Menu.id = $menuId'; 
        const menuValues = { $menuId: menuId }; 
        db.get(menuSql, menuValues, (err, menu) => {
            if(err) {
                res.sendStatus(404); 
                next(err); 
            } else if (menu) {
                const sql = 'UPDATE MenuItem SET name = $name, description = $description, inventory = $inventory, price = $price, menu_id = $menuId WHERE MenuItem.id = $menuItemId'; 
                const values = {
                    $name: name, 
                    $description: description, 
                    $inventory: inventory, 
                    $price: price, 
                    $menuId: menuId, 
                    $menuItemId: menuItemId
                }; 
                db.run(sql, values, (err) => {
                    if(err) {
                        res.sendStatus(404); 
                        next(err); 
                    } else {
                        db.get('SELECT * FROM MenuItem WHERE MenuItem.id = $menuItemId', 
                        {
                            $menuItemId: req.params.menuItemId
                        }, 
                        function(err, menuItem) {
                            if(err) {
                                res.sendStatus(404);
                                next(err); 
                            } else {
                                res.status(200).json({ menuItem: menuItem })
                            }
                        }
                        )
                    }
                })
            }
        })
    }
})

I’ve tried setting timeout on the it statements in the testing suite but this hasn’t made a difference.

Another question I have about the solution code is when writing our router.params, why don’t we attach the retrieved row to the request body, like we normally do with Express routers? Mine seemed to work both with and without attaching it, but I’m curious as to why that is and how we know when we should attach it to the request body.

For example:

menuRouter.param('menuId', (req, res, next, menuId) => {
    db.get("SELECT * FROM Menu WHERE Menu.id = $menuId", 
    {
        $menuId: menuId
    }, 
    (err, row) => {
        if(err) {
            next(err);
        } else if (row) {
            req.menu = row; // Why do we not attach the retrieved row?
            next(); 
        } else {
            res.sendStatus(404); 
        }
    }); 
}); 

Many thanks in advance!

Can’t help you here but curious if you ever found out the answer to your questions?
I have the same question about the router.param(..)

Did you use {mergeParams: true} when you started the menuItemsRouter? I think this allows access to both req.params.menuItemId and req.params.menuId when a router is nested under the other.

Hi, I know this was a while ago but I hope I can help as I’ve just completed this task and passed on all

On your put request I’ve noticed your if statement might be what’s causing the issue

if (!name || !inventory || !price || !menuId) {
        res.sendStatus(400); 

I can see that you haven’t checked whether a description has been sent in the request body and you have already checked to see whether a valid menuId exists with your menuRouter.param(‘menuId’ …) so before the end point is hit your code has already checked to see whether a valid menu id has been entered and called next() from your middleware function to proceed if it’s not a valid menuId then a 404 response will be sent as long as you have called mergeParams: true when declaring your menuItemsRouter at the top of your file - it will give you access to the menuId middleware function as long as you called menuRouter.use(’/:menuId/menu-items’, menuItemsRouter) after the middleware function in menu.js - this caught me out for a while!

your SQL query is correct, however you shouldn’t send a 404 response as your middleware function handles that logic you can simply just call next(err) - I’ll copy my code for this below if you want to just compare it! hope you did get it sorted however :slight_smile:

menu.js

menuRouter.param('menuId', (req, res, next, menuId) => {
    db.get(`SELECT * FROM Menu WHERE Menu.id = $menuId`, {
        $menuId: menuId
    }, (err, menu) => {
        if (err){
            next(err);
        } else if (menu) {
            req.menu = menu;
            next();
        } else {
            res.sendStatus(404);
        }
    });
});

menuRouter.use('/:menuId/menu-items', menuItemsRouter);

menu-items.js

const express = require('express');
const menuItemsRouter = express.Router({mergeParams: true});
const sqlite3 = require('sqlite3');
const db = new sqlite3.Database(process.env.TEST_DATABASE || './database.sqlite');


menuItemsRouter.param('menuItemId', (req, res, next, menuItemId) => {
    db.get(`SELECT * FROM MenuItem WHERE MenuItem.id = $menuItemId`, {
        $menuItemId: menuItemId
    }, (err, menuItem) => {
        if(err) {
            next(err);
        } else if(menuItem) {
            req.menuItem = menuItem;
            next();
        } else {
            res.sendStatus(404);
        }
    });
});


menuItemsRouter.put('/:menuItemId', (req, res, next) => {
    const name = req.body.menuItem.name;
    const description = req.body.menuItem.description;
    const inventory = req.body.menuItem.inventory;
    const price = req.body.menuItem.price;

    if(!name || !description || !inventory || !price) {
        res.sendStatus(400);
    };

    const sql = `UPDATE MenuItem SET name=$name, description=$description, inventory=$inventory, price=$price, menu_id=$menuId WHERE MenuItem.id = ${req.params.menuItemId}`;
    const values = {
        $name: name,
        $description: description,
        $inventory: inventory,
        $price: price,
        $menuId: req.params.menuId
    };

    db.run(sql, values, (err) => {
        if(err){
            next(err);
        } else {
            db.get(`SELECT * FROM MenuItem WHERE MenuItem.id = ${req.params.menuItemId}`, (err, menuItem) => {
                if(err){
                    next(err);
                } else {
                    res.status(200).json({menuItem: menuItem});
                }
            });
        }
    });
});