Quote API Challenge Project (JavaScript)

Here is my solution to this challenge. I implemented a delete route but haven’t added any of the other extra features.

Just to give a brief response .
0. const {quotes} is object destructuring, a method of assigning variables quicker and shorter

it works like this:
if we have an object like:

myObject = {
quotes : 200
}

Normally to assign a variable quotes we do:

const quotes = myObject.quotes;

but since we know quotes is a property of myObject

we do

const {quotes} = myObject;

That’s how desctructuring works, we put the name of the property as a variable name inside braces and assign it to the object that contains it.

  1. Paths are not fixed they all depend on YOU! but there just need the front end and the back end to be in agreement to be able to communicate.

For instance, you can choose to park your car in the drive way or the garage(see this as a backend decision). Let’s say you choose to park it in the garage. If your friend is looking for the car(front-end application), you have to direct him to the garage so he can find the car(see this a resource/information). Now he(front end) knows the path(garage) to find your car(resource).
Imagine you have other items your friend needs to find, let tools, lawnmower, washing machine. He has to know the paths to all these items, and you as the owner of the house CHOSE where they all are. So the lawnmower maybe in the basement(another path to locate a resource) , the washine machine may be in the laundry room

Relating this back to this project, since codecademy already designed the front end of this app, they already CHOSE THE LOCATION THE FRONT END EXPECTS TO FIND RESOURCES so our job as back end developers is to ensure the resources are in the location (PATH) the front end would be looking for it. and since we are looking for quotes and random quotes we have different paths for them.

Just a side note, if we were to redesign the front end a bit, we could also use /api/quotes?random=“true” to use the same /api/quotes route in the backend but we check for a random querystring. So these are all design choices.

  1. next is more useful in middlewares. For example if we wanted to checked if a user is signed in we can use an authentication middleware that checks that. If the user is signed in called next() moves the execution to the next function which may display a restricted page. But if the user is not signed in, we don’t call next( ) and the user can’t see the restricted page.

  2. The front end expects the response in that format.

  3. There are 3 main ways to pass data from the front end to the back end.

a. Passing data in the body of the fetch() request which we access with req.body.
b. Passing data as querystrings which look like “?person=Messi&quote=best in the world” and can be accessed by req.query
c. Passing data as a request parameter. which on the front end looks like “team/players/messi” and on the back end looks like “team/players/:playername” and can be accessed with req.params

It’s a design, and you choose what’s best for the use case. For example to send sensitive data to the back-end it’s better to pass it through the request body as the querystrings will give a chance to people near by or hackers to steal the credentials.

  1. Because we used a querystring and not a query parameter as i explained in 4 above.

Hi everyone, this is my solution.

Hi everybody, it’s my project, if somebody wants to watch, here is the link, welcome!

Hi manucr0807181683

It would be cool if you could make your MongoDB solution available. I just finished the MongoDB course on Codecademy and would love to implement it as part of the Quote API.

Atanas, great solution. I used it as inspiration to implement the delete and update features.

1 Like

For the implementation of the PUT and DELETE routes, I found inspiration in Atanas’s solution:
https://discuss.codecademy.com/t/quote-api-challenge-project-javascript/462379/41?u=text9062315027

Here are the steps I followed to create the solution.

Step 1. Set-up the project on my local machine

I choose to develop the project on my local machine. I did this because I wanted to use Postman to test the API. I feel I have better control of the development process when I use Postman. See step 5.

To set up the project on my local machine:
-create a folder on your machine for example called “quote crud api”
-create all the files in your Codecademy project in this directory
-copy-paste the code in each of your files in the Codecademy project to each file in this directory

My folder and file structure in Sublime Text:

Install Express.js in the directory on your local machine where you created the files using this command in the command line:
npm install

Then you will probably see some error messages. You will be advised to write this in the command line:
npm audit

And then this:
npm audit fix

Now you can start the Express server with this command in the command line:
node server.js

Go to the browser and write:

localhost:4001

The index.html file should load.

Step 2. Add unique ids to the array in data.js.

Start the work to create the update and delete features.

As the Codecademy team proposes you need to add a unique id for each quote in the array in data.js. You will need this to reference the quotes you like to delete or update.

Open data.js.
Add a unique id from 1 to 13 to each object in the array :

const quotes = [
 {
  id: 1,
  quote: 'We build our computer (systems) the way we build our cities: over time, without a plan, on top of ruins.',
  person: 'Ellen Ullman'
 },
 {
  id: 2,
  quote: 'The best thing about a boolean is even if you are wrong, you are only off by a bit.',
  person: 'Anonymous'
 },
…,
 …,
 {
  id: 13,
  quote: 'If you tell me precisely what it is a machine cannot do, then I can always make a machine which will do just that.',
  person: 'Jon von Neumann'
 },
];

Step 3. Implement the new PUT and DELETE routes in server.js

In server.js implement two new routes:

 app.put('/api/quotes/:id', (req,res) => {
  const index = getIndexById(req.params.id, quotes);
  if (index !== -1) {
    updateElement(req.params.id, req.query, quotes);
    res.status(201).send({quote: quotes[index]});
  } else {
    res.status(404).send();
  }
})
app.delete('/api/quotes/:id', (req,res) => {
  const index = getIndexById(req.params.id, quotes);
  if (index !== -1) {
    quotes.splice(index, 1);
    res.status(204).send();
  } else {
    res.status(404).send();
  }
})

Step 4. Create helper functions in utils.js

Implement three new helper functions in utils.js to support the delete, update and create operations:

const getIndexById = (id, elementList) => {
 return elementList.findIndex((element) => {
  return element.id === Number(id);
 });
};
const createElement = (elementType, queryArguments) => {
  if (queryArguments.hasOwnProperty('quote') &&
    queryArguments.hasOwnProperty('person')) {
    idCounter += 1;
    let currentId = idCounter;
    return {
      'id': currentId,
      'quote': queryArguments.quote,
      'person': queryArguments.person,
    };
  } else {
    return false;
  }
};
const updateElement = (id, queryArguments, elementList) => {
 const elementIndex = getIndexById(id, elementList);
 if (elementIndex === -1) {
  throw new Error('updateElement must be called with a valid id parameter');
 }
 if (queryArguments.id) {
  queryArguments.id = Number(queryArguments.id);
 }
 Object.assign(elementList[elementIndex], queryArguments);
 return elementList[elementIndex];
};

createElement() need to know what id to use for a new quote. At the beginning of the file, an idCounter is initialized to 13 and this counter is used to increase the id with 1 for each new entry. The first time a new quote is created the idCounter = 13 + 1 = 14.

To line 1 in utils.js add this: let idCounter = 13;

Remember to export the new functions like this in the end of utils.js:

module.exports = {
 getRandomElement,
 createElement,
 updateElement,
 getIndexById, 
};

And also remember to import them in the beginning of server.js:

const { getIndexById, getRandomElement, createElement, updateElement } = require('./utils');

Step 5: Test the endpoints in Postman.

Test the new update and delete endpoints using Postman.

Download here:
https://www.postman.com

Postman is an application that you can use to test the API’s.

Here you can code the GET, PUT, DELETE, and POST requests and test that they work with your application.

Remember that you must run the code locally on your own machine so Postman can access your application via localhost.

Here is a screenshot of my setup in Postman:

Pasted Graphic 1

It was a big help for me to use Postman throughout the project.

Be aware: You need to start the local web server first before using Postman.

Here is my entire set-up in Postman for all the routes.


Get all quotes
GET localhost:4001/api/quotes

Get quote by Author name
GET localhost:4001/api/quotes?person=Ellen Ullman

Get random quote
GET localhost:4001/api/quotes/random

Create new quote
POST localhost:4001/api/quotes?person=Robert Greene&quote=Impatience is the ultimate impediment to success

Update quote by quoteId
PUT localhost:4001/api/quotes/1?id=1&person=Robert Greene&quote=Impatience is the ultimate impediment to success

Delete quote by quoteId
DELETE localhost:4001/api/quotes/1


( Codecademy is using query params in the POST request in its solution. You could also implement the Post request by sending the data in the request Body as raw JSON. To do that you will have to include this in your server.js file:

app.use(express.json())
app.use(express.urlencoded())

In app.post function use req.body.quote instead of req.query.quote. )

Step 6. Implement HTML pages for the user to access the new update and delete features

Implement two new .html files with corresponding .js files:

delete-quote.html
delete-quote.js

update-quote.html
update-quote.js

For inspiration on the content in these files check out Atanas’s solution:
https://discuss.codecademy.com/t/quote-api-challenge-project-javascript/462379/41?u=text9062315027

I added some extra input validation in my solution so if a user enter an id that is not in the array in data.js then an error message is returned to the user.

In the following code (in update-quote.js) you can see that if response.ok is not true then a message is returned to the user: “The quote Id was not found! Try again.”

submitButton.addEventListener('click', () => {
    quoteContainer.innerHTML = '';
    const id = document.getElementById('id').value;
    fetch(`/api/quotes/${id}`, {
            method: 'DELETE',
        })
        .then(response => {
            if (response.ok) {
                const quote = document.createElement('div');
                quote.innerHTML =
                    `<h3>The quote was deleted!</h3>
                    <p>Go to the <a href="index.html">home page</a> to request and view all quotes.</p>`
                quoteContainer.appendChild(quote);
            } else {
                const quote = document.createElement('div');
                quote.innerHTML =
                    `<h3>The quote Id was not found! Try again.</h3>`
                quoteContainer.appendChild(quote);
            }
        });
});

Add links to the new HTML pages in the index.html file so the user can access the new HTML files:

 <nav>
  <a href="index.html">Home</a>
  <a href="add-quote.html">Add a New Quote</a>
  <a href="update-quote.html">Update a Quote</a>
  <a href="delete-quote.html">Delete a Quote</a>
 </nav>

Step 7. Download the project solution code

I choose to download the solution from the beginning.
Project solution:
https://codecademy-content.s3.amazonaws.com/PRO/independent-practice-projects/quote-api/quote-api-solution.zip

Cheers !