Quote API Challenge Project (JavaScript)

Congratulations on completing your project!

Compare your project to our solution code and share your project below! Your solution might not look exactly like ours, and that’s okay! The most important thing right now is to get your code working as it should (you can always refactor more later). There are multiple ways to complete these projects and you should exercise your creative abilities in doing so.

This is a safe space for you to ask questions about any sample solution code and share your work with others! Simply reply to this thread to get the conversation started. Feedback is a vital component in getting better with coding and all ability levels are welcome here, so don’t be shy!

About community guidelines: This is a supportive and kind community of people learning and developing their skills. All comments here are expected to keep to our community guidelines


How do I share my own solutions?

  • If you completed the project off-platform, you can upload your project to your own GitHub and share the public link on the relevant project topic.
  • If you completed the project in the Codecademy learning environment, use the share code link at the bottom of your code editor to create a gist, and then share that link here.

Do I really need to get set up on GitHub?
Yes! Both of these sharing methods require you to get set up on GitHub, and trust us, it’s worth your time. Here’s why:

  1. Once you have your project in GitHub, you’ll be able to share proof of your work with potential employers, and link out to it on your CV.
  2. It’s a great opportunity to get your feet wet using a development tool that tech workers use on the job, every day.

Not sure how to get started? We’ve got you covered - read this article for the easiest way to get set up on GitHub.

Best practices for asking questions about the sample solution

  • Be specific! Reference exact line numbers and syntax so others are able to identify the area of the code you have questions about.
2 Likes

Any idea about the solution’s post request - that if input is empty : should be 400 response but hit the submit button nothing happens ?

image

Solution code :

app.post(‘/api/quotes’, (req, res) => {
const newQuote = {
quote: req.query.quote,
person: req.query.person
};
if (newQuote.quote && newQuote.person) {
quotes.push(newQuote);
res.send({ quote: newQuote });
} else {
res.status(400).send();
}
});

Look in the browsers console

Challenge completed,
https://gist.github.com/069fe3bfab79ffcc6e0da0a06dc60c5c

1 Like

I had a bunch of (I’m sure remedial) questions and would truly appreciate answers. Apologies in advance for their obviousness, if that’s the case.

  1. why is the array quotes described as const { quotes } ? why wouldn’t it be just const quotes?

  2. why is the path /api.quotes/random? What does each piece of the path signify? I’m confused for two reasons. First, I can’t attribute the api in the path to anything. Secondly, I would have thought the path would end at quotes, as we’re just looking to take the quotes array from the data.js file. Obviously I don’t understand paths.

  3. I was used to including next in the arguments for the function along with res and req. This seemed to work but wasn’t in the solution. When does one use next?

  4. I just don’t understand the syntax of
    res.send({
    quote: getRandomElement(quotes)
    });

That is, I don’t understand the curly brackets and the quote: array. I would have thought it would have been ok to just do res.send(getRandomElement(quotes));

  1. In this code: const newQuote = { quote: req.query.quote, person: req.query.person}
    How do I know to use req.query instead of req.params?
  1. Lastly, why does the path not have a colon?

Again sorry if these questions are super dumb. And thanks in advance.

6 Likes

Challenge completed. Now I will add the remaining HTTP verbs to have a complete CRUD Server. :smile:

2 Likes

Hi everyone !
Challenge completed, but i’m not sure if it really works.
Here my code:

On Postman, everything works fine, but on the front end of the project i only get " undefined - undefined" messages.
Here an image of what i get when i click on the random quote.

Does anyone have a clue of what’s going on ?

Thanks !

1 Like

Challege completed .
In addition with the first two optional feature suggested by Codecademy (Adding the PUT and DELETE route), I added some mine :

  • a method that gets each item of the quotes array a unique id when the server starts (instead of hard-coding it) and when a new item is added.
  • a function that will make a request to the Wikipedia Api that gets a short bio about each person (or character) in the array . This will be called when the servers starts and when some data is updated .
5 Likes
  1. When requiring from another file you use curly braces to deconstruct the object for what exactly you want, with { quotes } you are requiring exactly quotes from './data'. If you don’t use the curly braces you are requiring the whole file so const data = require('./data') to get quotes you would use data.quotes because quotes lives within data.
    You can use curly braces to deconstruct an object, take example object example
let example = {foo:"bar", baz:true, qux:1}

If we wanted to deconstruct this object and just get baz and qux we can use an object deconstructor

let { baz, qux } = example
console.log(baz, qux) //=> true 1
  1. The path is /api/quotes/random and this just means that if you go to http://localhost:4001/api/quotes/random you will see the output of that endpoint, i.e. {"quote":{"quote":"The city’s central computer told you? R2D2, you know better than to trust a strange computer!","person":"C-3PO"}}

  2. You use next when you want to pass a request onto the next middleware, in this project we handled everything within a single middleware. You would use middleware to DRY (Don’t Repeat Yourself) your code, if you are repeating yourself you can turn that step into middleware add the data you need onto the req object then call next() i.e.

//Validate req.query.person, if invalid return an error and do not call next, otherwise set the value to the req body
req.person = req.query.person
next()

In the next middleware the validated data is then ready to use.

  1. Curly braces are used in that instance to signify an object, an object contains key and value pairs of any type. When you use
{
  quote: getRandomElement(quotes)
}

You are creating an object with the key quote and value of the returned item from the function, the function is run as it is called and the value assigned, it will return something like this

{
   quote:{
      quote:"Understand well as I may, my comprehension can only be an infinitesimal fraction of all I want to understand.",
      person:"Ada Lovelace"
   }
}

This is converted to JSON by Express automatically

  1. You are told in the question that you should expect to get data through a query string: /api/quotes?quote=foo&person=bar, the part after the ? is a query where keys=values and separated by &, so you should get data from req.query; the data in that query is {quote: "foo", person: "bar"}.
    If however the api looked like /api/quotes/:person then the person value will be in req.params as it is a URL parameter.
    Query was more than likely used in this example to make sure you verify data, you can never assume that a client is sending good data; if you run the code you have and go to http://localhost:4001/add-quote.html and don’t enter anything for the quote or person you will get the result
Congrats, your quote was added!
-
Go to the home page to request and view all quotes.

Make sure to check that the queries exist before doing adding them

app.post('/api/quotes', (req, res, next) => {
	const quote = req.query.quote
	const person = req.query.person
	if(quote && person) {
		quotes.push({quote, person})
		res.send({quote:{quote, person}})
	} else {
		res.sendStatus(400)
	}
})

By getting the values from the query then putting them in an if statement they will be checked if they are not undefined, null, or empty. If either are undefined, null, or empty a 400 error will be sent.
I am also using object creation short hand, {quote, person} is the same as {quote: quote, person: person}

  1. None of the endpoints have colons because none of them are parameters, try modifying the example code you have to turn /api/quotes?person=foo into /api/quotes/:person.
    You will need to edit public/script.js line 64, change
fetch(`/api/quotes?person=${author}`)
//to ↓
fetch(`/api/quotes/${author}`)
//and save

To accept the new endpoint. Work off from this project to add your own features.

5 Likes

The wording is slightly ambiguous, by the quote object they mean

{
  quote: {
    quote: 'We build our computer (systems) the way we build our cities: over time, without a plan, on top of ruins.',
    person: 'Ellen Ullman'
  }
}

rather than

{
  quote: 'We build our computer (systems) the way we build our cities: over time, without a plan, on top of ruins.'
}

Change line 22 from

const randomQuote = quotes[random].quote;
//to ↓
const randomQuote = quotes[random];

This error persists with GET ‘/api/quotes’, you just need to send this if there is a valid req.query.person

res.send({quotes:quotes.filter(x => x.person === req.query.person)})

and if there is not a req.query.person send

res.send({quotes})

On another note you have massively over-engineered this challenge, there is a function from utils.js called getRandomElement(), pass the quotes array as a parameter to get a random element (go to utils.js to see how it works).
This is the intended solution for this project, it’s a lot simpler than you are trying to do https://pastebin.com/KZPGanwc

Looking good, but consider moving filterQuotes on lines 24-26 into the if statement so it only runs if there is a person query

const filterQuotes = quotes.filter(author => {
  return author.person === req.query.person;
});
if(req.qury.person) {
  res.send({ quotes: filterQuotes });
} else ...

to ↓

if(req.qury.person) {
  const filterQuotes = quotes.filter(author => {
    return author.person === req.query.person;
  });
  res.send({ quotes: filterQuotes });
} else ...

Otherwise filterQuotes is calculated for every request even if it isn’t needed.

it not responding with 400 becoz you created newQuote with property quote and person which always be true.

I had trouble trying to match the output format for the res.send(); I took it too literally and spent too much time overthinking it but eventually got it working.

Here’s how I implemented server.js:

There is nothing noticeable to show off. But as a test report - [https://gist.github.com/553eabbb0e855aa97684d7ae382ec46b]
Looks like broken link:(

I’ve completed this project, but for step number 6, my 400 response doesn’t send if there is no quote or person entered. It does prevent the quote from being added to the array, but when I click submit, nothing happens. Does anyone know why that might be?


const express = require('express');
const morgan = require('morgan');

const app = express();

const { quotes } = require('./data');
const { getRandomElement } = require('./utils');

const PORT = process.env.PORT || 4001;

app.use(express.static('public'));

app.listen(PORT); 


const quoteRouter = express.Router(); 

app.use('/api/quotes/', quoteRouter); 

quoteRouter.get('/random', (req, res, next) => {
   const randomQuote = getRandomElement(quotes); 
   if (randomQuote) {
     res.send( { quote: randomQuote} );
   } else {
     res.status(404).send(); 
   }
   
});

quoteRouter.get('/', (req, res, next) => {
  if (!req.query.person) {
   res.send( { quotes }); 
   next(); 
   }
  else if (req.query.person) {
  let search = req.query.person; 
  const foundArray = quotes.filter(quotes =>
  quotes.person === search); 
  console.log(foundArray); 
  res.send( { quotes: foundArray } );
  next(); 
  }
}); 

// Helper function to create a new object
 
const createQuote = queryArguments => {
  if (queryArguments.hasOwnProperty('quote') && 
      queryArguments.hasOwnProperty('person')) {
        return {
          'quote': queryArguments.quote, 
          'person': queryArguments.person
        }
      } else {
        return false; 
      }
};

// Prevents the quote from being added to the array but doesn't send a 400 response 
quoteRouter.post('/', (req, res, next) => {
   if (req.query.quote.length == 0 || req.query.person.length == 0) {
    res.status(400).send('You must enter a quote and an author'); 
  } else if (req.query.quote.length > 0 && req.query.person.length > 0) {
  const receivedQuote = createQuote(req.query); 
  if (receivedQuote) {
    quotes.push(receivedQuote); 
    res.send( { quote: receivedQuote } ); 
    next(); 
  }
  } 
}); 
1 Like

Completed The Project :grin:
https://gist.github.com/80a4cefe6f1a4e50c6bdaff2d167edb3

I did mine with express.Routes()

I have completed the code challenge.
Here is a link to my gist.


Enjoyed the outcome. Showed it to my family.

Finished! Feel free to check out my code on GitHub:

https://github.com/JamesTheLessFC/Quote-API

Added features include:
-updating quotes
-deleting quotes
-displaying a year for each quote
-displaying bios (via fetch) for each author when the user clicks on a ‘read bio’ button, which is then transformed into a ‘hide bio’ button
-updating bios
-posting bios
-deleting bios.

Hello,
I managed to finish the exercise by looking peoples code, reading a good few of the comments and answers in this forum, specially answers from @osorpenke (Thanks)

Still there is something I could not understand properly so if you any of you could give me a hand before I continue, it would be very appreciated.

app.post('/api/quotes', (req, res, next) => {

  const newObj = {
    quote: req.query.quote,
    person: req.query.person
  };
  if (newObj.quote && newObj.person){
    console.log(newObj);
    quotes.push(newObj);
    res.send({quote: newObj});
  } else {
    console.log('failed')
    res.status(400).send();
  }
});

why inside the if statement does it have to be res.send({quote: newObj}); and not just or res.send({quotes: newObj}); (with S ) considering that it is quotes and not quote the main container.

Here is my link: