Fetch() confusion

I’m doing the requests 2 exercise and i’m not sure what this code actually means.

Fetch() itself confuses me a lot. So I had a few questions, I would be grateful for any help.

  1. I was wondering, if you use another API, how do you know which object to pass into const data. In this instance, I’m assuming this API is looking for {destination: URL}, so it makes sense to pass this.

  2. What does Fetch() actually return?

I’m assuming the part above makes the POST request and then sends the data the API needs. But I don’t get where ‘headers’ comes from and why an API key is needed. Secondly, after this all happens, am I correct in thinking, the API then returns a promise to fetch(), which gives of course a status about the asynchronous operation (e.g. fulfilled) but also resolves to provide an object with basic data.


This is what I got when I console.log() the first Promise returned by fetch(). Does this mean that response.json() returns a promise, which resolves to the object above? I have provided the boilerplate code.

After this, when we write another .then, is the jsonResponse then another promise, which actually returns the shortened URL that we want?

I’m just so confused as to what fetch() initially does when we provide an endpoint and the object. Then I am confused as to what is being returned when the first and second .then are made. Am I correct in thinking all 3 are promises?

Sorry if the question doesn’t even make sense.

1 Like

What data you provide to the API will be determined by the API endpoint that you are attempting to use. Some of them will require you to use POST, like you are doing here, and to provide a JSON object with the parameters of your interaction - in this case, you’re simply giving Rebrandly a URI to shorten.

For the most part, APIs come with documentation explaining what the endpoints are, what data you can retrieve from them and how it expects you to “talk to it” as it were. As you progress, it will be down to you to read the API documentation and work out how to interact with these interfaces. :slight_smile:

fetch() itself returns what’s called a “Promise”, which will eventually resolve to an HTTP response. The reason that you get a Promise before the response is to facilitate asynchronous processing - you don’t necessarily want your web page or app to sit and wait for however long it takes the fetch() to complete. Instead, JavaScript promises it will fetch what you asked and will deliver it to you later.

To give you a real-world analogy, think of it like waiting for a table in a restaurant.

You arrive at your fave restaurant, but there’s a waiting list and the host tells you it’ll be 30-40 minutes for a table. You say “Sure, that’s fine!” and they give you a buzzer. You then go to the bar, and have a drink while you wait for your table to be ready. When it is, the buzzer goes wild and you return to the desk to be seated at your table.

The buzzer is essentially the Promise; you know that you’ll get your table when it’s ready, freeing you up to go enjoy a drink at the bar.

Headers are the metadata that provide additional information about the request that you’re making, or the response that you are receiving, and are a pretty common feature of many protocols.

You can read quite a lot about HTTP headers on the Mozilla Dev Network if you want, and I would encourage you to do so, but for now I think I can simplify them a bit with another analogy.

Think of the HTTP request like a package that you need to ship. The data you’re providing to the API is the contents of your package. The headers could be thought of as analogous to the extra details you need to provide to the courier before they’ll take your package - like what’s in it, what the weight is, the dimensions of the box, where it’s going…

It’s not exactly the same, but it’s a close enough analogy. :slight_smile:

The API key is a security mechanism. In much the same way as you might need a swipe card or access code to get into the lobby of an apartment block, APIs frequently like to know who is asking for their data - and, more importantly, that they’re allowed to have it. The API key authenticates you to the endpoint, much like how your username and password do to Codecademy when you log in. :slight_smile:

Close. The API doesn’t return the Promise, the fetch() method does. When the API returns its response to your request, or if the request fails for some reason (like there’s no connectivity), that is the point at which the Promise resolves.

A successfully resolved Promise returns a Response object, which conveniently has a .json() method to read the response and parse it to JSON. At that point, you can work with it like any other regular JS object.

response.json() returns JSON, not a Promise object. You already have the Response object at this point, so you’re not waiting for anything to resolve but are instead parsing data you already have. :slight_smile:

Yes, response.json() returns a Promise which resolves to the JSON-formatted body of the response.

The JSON response isn’t a Promise, but you do get a new promise by using .then().

There is a good explanation of “chaining” here, but for the benefit of the thread:

Your initial call to fetch() returns a Promise, which will either succeed and provide you with a Response object or it will error and you’ll get an exception. Please keep in mind that an HTTP error, like 404 Not Found, is still a successful request even though you didn’t get the response you were looking for.

As soon as that first Promise resolves, if we want to we can chain the outcome of that Promise (the Response) into a new Promise and wait for that to resolve.

In your code screenshot, you’re getting three Promises:

  1. fetch() is the first Promise,
  2. .then(response => ...) is the second Promise,
  3. and .then(jsonResponse => ...) is the third Promise.

Each of these Promises completes before the next one is given the chance to run.

I should also add that your callback functions need to use return in order for the result of one Promise to be used in the next one along the chain.

In the example below, as per your screenshot, you have two callback functions written using arrow notation.

The first, taking the response parameter, is the “success” callback function which will run if the Promise resolves successfully to a Response. The second, taking the networkError parameter, is the “failure” callback function which will run if the Promise doesn’t return a Response.

Whatever these callback functions return, is what gets passed along the chain to the next Promise.

.then(response => {
    if (response.ok) {
        return response.json();
    };
    throw new Error('Request failed!');
}, networkError => {
    console.log(networkError.message);
})

Apologies for the lengthy response, but you did ask a fairly lengthy question. :slight_smile: Hopefully I’ve not confused you further, but if anything remains unclear please do let us know. :smiley:

4 Likes

Thanks soo much for this response. You may well be one of the best teachers I’ve come across in my years of education. The analogies and explanations were amazing; you’ve got an insane talent for it.

  1. So do we get told which HTTP headers to provide? In the MDN documentation there seems to be so many to choose from.

  2. So is JSON similar to XML? Never quite understood this bit because I thought the reason we used .json() was to convert the illegible response object from the fetch() promise into something more readable? Or in reality, am I wrong? Instead I’m thinking that perhaps the response object from fetch() is actually in XML (hence the AJAX name) and that .json() converts it into something legible in javascript point?

I just read that nowadays XML isn’t used so perhaps I got confused.

  1. So I think I have a slightly better understanding now. So fetch() returns a Promisee which resolves to a response object. This cannot be understood in plain english so we use .then to create a new promise, which allows us to resolve using .json() and convert it into something legible. My question is, at this point, why do we realistically need the third “.then(jsonResponse => …)”.
    Since .json() doesn’t generate a promise, there is no need to have a .then and so you could simply render .json directly without a promise right?

In the MDN documentation, it says .json returns a promise so I had assumed this is why the third promise was required?

Thanks so much again, appreciate it so much!

Thanks so much for your patience btw, much appreciated!

1 Like

That’s quite the compliment, though I wouldn’t go that far. I’ve doled out plenty of dud explanations on topics here, I’m sure.

The documentation for an API ought to point out any which are absolutely required, like the Authorization header for example if they’re expecting that to be in the request to authenticate you.

Others are more standard. For example, if you’re making a POST or PUT request to send data to an API endpoint you would typically include the Content-Type header to let the endpoint know what format you’re sending the data in, e.g. application/json if you’re sending JSON.

JSON is an acronym: JavaScript Object Notation.

It’s similar to XML in the sense that it’s a common format for data exchange, but it’s different in that XML relies on tags to demarcate blocks of data. JSON simply expresses the data like any other JavaScript object, with key-value pairs:

{
    key: value,
    key2: {
        subkey: another_value,
        subkey2: something_else,
    }
}

The reason that we use response.json() to get a workable object is not because the response is in XML. The response is itself an object, that being a Response.

The data that you get back from the API endpoint arrives at your browser as a ReadableStream object. We use response.json() to parse that stream of bytes into something more usable in JS: a JSON object.

I think I might’ve muddled a few things together in my previous reply; I’ll go back and fix it shortly.

The Response object is just another JavaScript object, it’s not illegible but it’s not the object that you want to work with directly if what you’re looking for is the data that came back.

You are correct, though - response.json() does return a Promise. That’s my mistake (which I’ll go back and correct).

The call to response.json() is itself wrapped in .then(), so even though we get a Promise back from response.json() we wait for it to resolve before we move to the next .then(). The final result of that call to .then() is to hand off the JSON object to the next item in the chain.

The reason for the final .then(jsonResponse => ... block is not immediately clear in your example code, because all you’re doing is returning the JSON object. In reality, what you’d likely be doing at that point is manipulating the JSON object for use elsewhere in your program. :slight_smile:

Hope I’m not confusing you further! Let us know if there’s anything else you’re stuck with, and we’ll try and help. (I’ll also re-work my previous post so that it’s not giving the wrong info re: response.json() any more… I only learned this stuff myself recently!)

1 Like

Thanks that makes so much sense!

So essentially:

  1. fetch() sends an object with information the API needs to the API endpoint. HttpRequest headers are specific to the API documentation. This returns a promise, which resolves to the status of the request and info from the API in Readable stream object.
  2. We convert this using .then() and .json() to convert it into a javascript object.
  3. We then use another .then after this resolves for manipulation - i.e. placing it on the DOM.

So could you manipulate the JSON object in step 2 within the first .then()? Or if i understood correctly, do you need the second .then() to cause the promise to resolve? Just don’t get why step 3 is needed really, when it could be done in step 2?

Also, to clarify XML isn’t used at all then right?

Thanks so much once again.

Bump? Anyone got any ideas?

The reason that you don’t do this:

.then(response => {
    if (response.ok) {
        // do some manipulation here
    };
...

is because you have to wait for the Promise from response.json() to resolve. Until that happens, you can’t do anything - but JS won’t wait for it in this instance, it will move immediately to the next bit of code and you’ll find that the JSON isn’t accessible.

That’s why it’s a separate step. :slight_smile:

Not in these lessons, no. :slight_smile:

1 Like

Got it! Thank you so much for answering all my questions and follow-ups haha :slight_smile:
Just for curiosity, You could wrap if (response.ok) in an async…await function and skip the last step then right?

The lesson does eventually get to asynchronous requests, which changes the programming pattern from using chaining and then to a different one. At that point, when the function becomes an async then yes you can use await with the response.json() and do the manipulation immediately after.

1 Like

Thank you so much for all your help!

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.