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

Why does it have to be '/quotes/quote/random' and not 'quotes/random'?

For the random quote generator route, she has it done like this:

// Send a GET request to /quotes/quote/random to READ a random quote
router.get('/quotes/quote/random', asyncHandler( async (req, res, next) => {
    const quote = await records.getRandomQuote();
    res.json(quote);
}));

and in postman, we have to use this path: localhost:3000/api/quotes/quote/random

I tried changing it to:

// Send a GET request to /quotes/random to READ a random quote
router.get('/quotes/random', asyncHandler( async (req, res, next) => {
    const quote = await records.getRandomQuote();
    res.json(quote);
}));

and in postman, I tried using the path: localhost:3000/api/quotes/random and I received a 404 Not Found error. Can someone explain why this is? Thank you.

EDIT: Here are the files and yes the error was probably a custom error handle and not Express:

Router.js

const express = require('express');
const router = express.Router();
const records = require('./records');

//takes a function, wraps it in a try/catch block
//and passes any errors to the global error handler
function asyncHandler(cb){
    return async (req, res, next)=>{
      try {
        await cb(req,res, next);
      } catch(err){
        next(err);
      }
    };
}

// Send a GET request to /quotes to READ a list of quotes
router.get('/quotes', async (req, res) => {
    const quotes = await records.getQuotes();
    res.json(quotes);
});

// Send a GET request to /quotes/:id to READ a quote
router.get('/quotes/:id', asyncHandler( async (req, res) => {
    const quote = await records.getQuote(req.params.id);
    if (quote) {
            res.json(quote);
    } else {
        res.status(404).json({message: "Quote not found."});
    }
}));

// Send a GET request to /quotes/quote/random to READ a random quote
router.get('/quotes/random', asyncHandler( async (req, res, next) => {
    const quote = await records.getRandomQuote();
    res.json(quote);
}));

// Send a POST request to /quotes to CREATE a new quote
router.post('/quotes', asyncHandler( async (req, res) => {
    if (req.body.author && req.body.quote) {
        const quote = await records.createQuote({
            quote: req.body.quote,
            author:req.body.author
        });
        res.status(201).json(quote);
    } else {
        res.status(400).json({message: "Quote and author required."});
    }
}));

// Send a PUT request to /quotes/:id to UPDATE a quote
router.put('/quotes/:id', asyncHandler( async (req, res) => {
    const quote = await records.getQuote(req.params.id);
    if (quote) {
        quote.quote = req.body.quote;
        quote.author = req.body.author;
        await records.updateQuote(quote);
        res.status(204).end();
    } else {
        res.status(400).json({message: "Quote not found."});
    }
}));

// Send a DELETE request /quotes/:id to DELETE a quote
router.delete('/quotes/:id', asyncHandler( async (req,res, next) => {
    const quote = await records.getQuote(req.params.id);
    if (quote) {
        await records.deleteQuote(quote);
        res.status(204).end();
    } else {
        res.status(400).json({message: "Quote not found."});
    }
}));

module.exports = router;

Records.js

const fs = require('fs');

function generateRandomId(){
  return Math.floor(Math.random() * 10000);
}

function save(data){
  return new Promise((resolve, reject) => {
    fs.writeFile('data.json', JSON.stringify(data, null, 2), (err) => {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
}

/**
 * Gets all quotes
 * @param None
 */
function getQuotes(){
  return new Promise((resolve, reject) => {
    fs.readFile('data.json', 'utf8', (err, data) => {
      if (err) {
        reject(err);
      } else {
        const json = JSON.parse(data);
        resolve(json);
      }
    });
  });
}

/**
 * Gets a specific quote by ID
 * @param {number} id - Accepts the ID of the specified quote.
 */
async function getQuote(id){
  const quotes = await getQuotes();
  return quotes.records.find(record => record.id == id);
}
/**
 * Gets a random quote 
 * @param None
 */
async function getRandomQuote(){
  const quotes = await getQuotes();
  const randNum = Math.floor(Math.random() * quotes.records.length);
  return quotes.records[randNum];
}

/**
 * Creates a new quote record 
 * @param {Object} newRecord - Object containing info for new quote: the quote text, author and year 
 */
async function createQuote(newRecord) {
  const quotes = await getQuotes(); 

  newRecord.id = generateRandomId(); 
  quotes.records.push(newRecord);
  await save(quotes); 
  return newRecord; 
}

/**
 * Updates a single record 
 * @param {Object} newQuote - An object containing the changes to quote: quote, author, year (all optional)
 */
async function updateQuote(newQuote){
  const quotes = await getQuotes();
  let quote = quotes.records.find(item => item.id == newQuote.id);

  quote.quote = newQuote.quote;
  quote.author = newQuote.author;
  quote.year = newQuote.year;

  await save(quotes);
}

/**
 * Deletes a single record
 * @param {Object} record - Accepts record to be deleted. 
 */
async function deleteQuote(record){
  const quotes = await getQuotes();
  quotes.records = quotes.records.filter(item => item.id != record.id);
  await save(quotes);
}

module.exports = {
  getQuotes,
  getQuote, 
  createQuote, 
  updateQuote, 
  deleteQuote,
  getRandomQuote
}

App.js

const express = require('express');
const app = express();
const routes = require('./routes');

//to be able to access values from the request body
app.use(express.json());
app.use('/api', routes);

//for invalid requests
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.status(err.status || 500);
    res.json({error: {
        message: err.message
    }})
});

app.listen(3000, () => console.log('Quote API listening on port 3000!'));

1 Answer

Simon Coates
Simon Coates
8,377 Points

Is the 404 error coming from express or is it your customer error handling? If you can't post a workspace, are you able to post your full code? I threw together a demo to see if you'd done something weird with the routes. However, with a slight modified version of your code, I was able to access json at the URL you specified.