Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

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

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;

2 Answers

Greg Kaleka
Greg Kaleka
39,021 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;

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
39,021 Points

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

Check out my edit above; I got it working.

Courtney Wilson
Courtney Wilson
8,031 Points

For anyone else wondering the same, another way to do what Greg is doing is to use an if/else statement and if the array index doesn't exist, redirect to '/not-found' so that the 404 middleware function will pick it up:

router.get('/:id', (req, res) => {
    if (req.params.id) {
        res.render('card', { 
            prompt: cards[req.params.id].question,
            hint: cards[req.params.id].hint
        });
    } else {
        res.redirect('/not-found');
    }
});
router.get('/:id', (req, res) => {
    if (req.params.id) {  // this line item
        res.render('card', { 
            prompt: cards[req.params.id].question,
            hint: cards[req.params.id].hint
        });
    } else {
        res.redirect('/not-found');
    }
});

Should be

router.get('/:id', (req, res) => {
    if (cards[req.params.id]) {   // should be this line item
        res.render('card', { 
            prompt: cards[req.params.id].question,
            hint: cards[req.params.id].hint
        });
    } else {
        res.redirect('/not-found');
    }
});