JavaScript Express Basics Parameters, Query Strings, and Modularizing Routes Using Data and Route Parameters

Benjamin Farnham
Benjamin Farnham
8,055 Points

Not getting 404 error when I use a route with an ID that doesn't exist.

So maybe I am just trying to be overly thorough here but I was able to follow the instructions and honestly understand what was being presented but my variable routes (/:id) don't give a 404 error when given a route that doesn't exist (i.e. /cards/8).

My console is logging an error (RangeError [ERR_HTTP_INVALID_STATUS_CODE]: Invalid status code: undefined) but I really want to make sure the application is rock solid so I can move forward with best practices.

Am I missing something?

app.js

const express = require("express");
const bodyParser = require("body-parser"); // Middleware that parses form data
const cookieParser = require("cookie-parser"); // Middleware that parses cookie data

const app = express();

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

const mainRoutes = require('./routes/index.js');
const cardRoutes = require('./routes/cards.js');
app.use(mainRoutes);
app.use('/cards',cardRoutes);

app.set("view engine", "pug");

// 404 message
app.use((req, res, next) => {
    const err = new Error(`Not found`);
    err.status = 404;
    next(err);
});

// Error handler
app.use((err, req, res, next) => {
    res.locals.error = err;
    res.status(err.status);
    res.render('error', err);
});

app.listen(1337, () => {
    console.log("The app is running on 'localhost:1337'");
});

cards.js (with route in question that doesn't give the 404)

const express = require("express");
const router = express.Router();
const { data } = require('../data/flashcardData.json');
const { cards } = data;

router.get("/:id", (req, res) => {
    res.render("card", {
                        the requests params key. (req.params.varName)
                        prompt: cards[req.params.id].question,
                        hint: cards[req.params.id].hint
                        });
});

module.exports = router;

1 Answer

Greg Kaleka
Greg Kaleka
38,983 Points

Hey Benjamin,

Great question. You're not doing anything wrong.

Let's think about what's happening:

  1. User goes to /cards/9999
  2. Express looks for a route that matches that pattern
  3. Express find /cards/:id, which matches perfectly, so no 404 - it calls get("/:id")
  4. Inside of get("/:id"), your code looks for cards[9999].question

What happens on step 4? TypeError: cards[9999] is undefined. And that error has a status of undefined. So, back in app.js, you have an error handler that returns the err.status as the res.status, and undefined is not a valid HTTP response status!

How can we handle this? Add error handling to your cards.js code. This may be a bit beyond what you've seen before, so don't worry if it isn't familiar. you don't necessarily need to learn this now, but this is the basic idea.

We can wrap our card lookup in a try-catch block. That would look something like this (important note - I know nothing about express.js, so it is very likely this will not actually work. Hopefully it gives you the general idea, though):

cards.js
const express = require("express");
const router = express.Router();
const { data } = require('../data/flashcardData.json');
const { cards } = data;

router.get("/:id", (req, res) => {
    res.render("card", {
                        try {
                            card = cards[req.params.id];
                        }
                        catch(err) {
                            if err.status === "undefined" {
                                req.status = 404;
                            }
                        }
                        prompt: card.question,
                        hint: card.hint
                    });
});

module.exports = router;

Hope that's helpful! Let me know if you have any conceptual questions about this. Again, I'm not at all sure this will actually work, but it's the right idea of how to approach it.

Edit:

OK, I couldn't help myself, so I figured it out. What I was doing was pretty substantially wrong hah. This version works:

cards.js
const express = require('express');
const router = express.Router();
const { data } = require('../data/flashcardData.json');
const { cards } = data;

router.get('/:id', (req, res) => {
    try {
        selected_card = cards[req.params.id];
        if (selected_card === undefined) {
            throw 404;
        }
        res.render('card', {
            prompt: selected_card.question,
            hint: selected_card.hint
        });
    }
    catch(err) {
        console.log('caught error ' + err);
        if (err === 404) {
            res.sendStatus(404);
        }
    }
});

module.exports = router;
Benjamin Farnham
Benjamin Farnham
8,055 Points

I ended up testing the try catch block and was able to get some success. Still some kinks to work out though. Thanks for responding! As crazy as it may be, I am just determined to get the best practices down as fast as possible. ;)

Greg Kaleka
Greg Kaleka
38,983 Points

That's awesome - totally understand that attitude, and it will take you far!

Check out my edit above; I got it working.