fetch and the Response
A page often needs something it does not have yet: a list of projects, today's weather, the latest posts. fetch is the browser's built-in way for a script to ask a server for it. A server that answers programs with data, rather than people with pages, offers an API, short for application programming interface: a fixed set of addresses and the answers each one gives. fetch sends the same kind of request that APIs for Beginners sent by hand from DevTools and curl, only now your page's own code sends it. It returns a promise, as the previous topic described, and it has one behaviour that surprises almost everyone: an answer saying "not found" is not treated as a failure.
Think of posting a letter and waiting for a reply. A reply that says "no one by that name lives here" is disappointing, but it is still a reply: the post did its job. Only a letter that never arrives, or never gets an answer at all, is a failure of the post itself. fetch draws exactly that line, and your code has to read the reply to know which kind of news it holds.
A Request in One Line
fetch takes an address and sends a GET request to it. What it gives back is a promise of a response: an object describing the server's answer. A response has three parts, each covered in depth in APIs for Beginners. The status code is a three-digit number: 200 means OK, 404 means not found, 500 means the server itself went wrong. The headers are labelled extra facts, such as what type of content is coming. The body is the content itself.
To try it, make a small file called hello.json in your practice folder, next to a page, and open that page through the preview server. A request needs a real address, just like the modules of Chapter 7, and a double-clicked file does not have one.
{
"message": "Hello from a file",
"count": 3
}
async function show() { const response = await fetch("hello.json"); console.log(response.status, response.ok); const data = await response.json(); console.log(data.message); } show();
200 true Hello from a file
The function asks for hello.json, waits for the response, and logs its status code and a true-or-false value called ok. Then it waits again for the body, read as JSON, and logs one property of the object that comes out. The Console shows 200 and true, then Hello from a file. It may also show a line about a promise first: that is the value show() itself returned, because an async function always returns a promise, and the Console prints whatever the last line gives back.
Reading the Body: Why a Second await?
The promise from fetch is fulfilled as soon as the status and headers arrive. The body may still be on its way, in pieces, especially if it is large. So reading the body is a second wait. response.json() returns another promise: it waits for the whole body, parses it as JSON from Chapter 7, and gives you the value. For plain text, response.text() does the same without parsing. A body can be read only once, so pick one.
The classic slip is forgetting that second await. Write const data = response.json(); without it, and data is a promise, not your object. Reading data.message then gives undefined, with no error to explain why. When a value from fetch is mysteriously undefined, look for a missing await first.
response.ok: Why a 404 Is Not an Error
Now ask for a file that does not exist.
async function tryMissing() { const response = await fetch("missing.json"); console.log(response.status, response.ok); } tryMissing();
404 false
No error was thrown. The promise was fulfilled, because a server did answer: its answer was "not found", status 404. The Console may also show a red line naming the address and the 404. That line is the browser's own note about a failed request, not an error in your script, which carried on and logged 404 and false.
That is what response.ok is for. It is true when the status code is between 200 and 299, the range that means success, and false for anything else. A 404, a 500 or any other failure status still gives a normal response, so the check is yours to write. This book writes it the same way every time.
async function loadJson(url) { const response = await fetch(url); if (!response.ok) { throw new Error(`Request failed: ${response.status}`); } return await response.json(); } async function showSafely() { try { const data = await loadJson("missing.json"); console.log(data); } catch (error) { console.log("Could not load:", error.message); } } showSafely();
Could not load: Request failed: 404
loadJson fetches an address, and if the response is not OK it throws a new error: it creates an error with a message and hands it up, exactly like a rejected promise. showSafely catches it and logs Could not load, followed by Request failed and the status. Every kind of failure now arrives in the same catch, so there is one place to tell the visitor.
When Does fetch Really Fail?
The promise from fetch is rejected only when no usable answer arrived at all: the device is offline, the server cannot be reached, the address is malformed, or the browser itself blocked the request, which is what the topic on cross-origin requests is about. The error is a TypeError, and its message differs from browser to browser. The catch above handles it with no extra code.
You can cause one on purpose. In the Network panel, open the throttling menu, which normally says No throttling, and choose Offline. Run showSafely() again: this time the fetch itself is rejected, and the catch logs the browser's message about a failed fetch. Set the menu back afterwards, or every page in that tab stays offline.
Watching Requests in the Network Panel
The Network panel lists every request the page makes. Its filter bar has a Fetch/XHR button that shows only the requests made by scripts. Each row gives the name, the status code, the type and the time it took, and a failed status is shown in red. Click a row to see its Headers, a Preview of the body with JSON laid out as a tree, and the raw Response. When a page's data does not appear, this is where to look before touching the code.
- "A 404 makes fetch throw an error." The promise is fulfilled with a response whose status is 404. Check
response.okyourself. - "response.json() is the data." It is another promise.
awaitit to get the data. - "fetch needs a library." It is built into every modern browser, with nothing to install.
- The
okcheck is the difference between a page that shows errors honestly and one that breaks mysteriously. - The same few lines work for every API you will ever call from a page.
Knowledge Check
A script fetches a file that does not exist on the server. What does the promise from fetch do?
- It is rejected with an error saying not found
- It stays pending until the file is created
- It is fulfilled with a response whose status is 404
- It is fulfilled with status 200 and an empty body
Why does reading a JSON body need a second await?
- The body may still be arriving after the headers
- The first await only checks that the network is up
- The JSON has to be downloaded twice to be checked
- The second await turns the status code into data
When is the promise returned by fetch rejected?
- When the server answers with a 404 or a 500
- When the response body is not valid JSON
- When the answer takes longer than a second
- When no usable answer arrives, such as offline
What does response.ok tell you?
- Whether the body has been read and parsed
- Whether the status code is from 200 to 299
- Whether the network connection is still up
- Whether the server sent any headers at all
You got correct