Most JSON in the real world sits on a server, or in a file on one, and fetch is how a page asks for it. This lesson puts the two together: request some JSON, check that the request worked, read it with response.json(), and use the data. It builds on the promises, async and await from Debugging Async.
About the examples. In the Try It editor, a name like "students.json" points to a small sample file kept beside the editor. On your own site you would use the path to your own file or the address of a web API.
fetch(url) does not hand you the data. It gives you a Response as soon as the server answers. The body of the response is a separate step, because it can be large and arrives in pieces:
| Piece | What it gives you |
|---|---|
fetch(url) | a promise of a Response object |
response.json() | a promise of the body, parsed as JSON |
response.text() | a promise of the body as a plain string |
response.ok | true for a successful status (200 to 299) |
response.status | the status number, like 200 or 404 |
fetch("students.json")
.then(response => response.json())
.then(students => {
console.log(students.length);
console.log(students[0].name);
});
Each .then waits for the step before it. The first one returns response.json(), which is itself a promise, and the next .then receives its result: the parsed array.
await inside an async function reads top to bottom, which many people find easier to follow:
async function averageScore(name) {
const response = await fetch("students.json");
const students = await response.json();
const student = students.find(s => s.name === name);
const total = student.scores.reduce((sum, n) => sum + n, 0);
return total / student.scores.length;
}
averageScore("Ava").then(average => console.log(average));
An async function always returns a promise, so the caller uses .then (or its own await) to get the result.
You never have to call JSON.parse yourself. response.json() is the same as reading the text and parsing it, in one step:
fetch("products.json")
.then(response => response.text())
.then(text => {
console.log(typeof text);
const data = JSON.parse(text);
console.log(data.items.length);
});
A missing file is not an error to fetch: the server still answers, with a status of 404 and an error page as the body. If you call response.json() on that page, you get a confusing SyntaxError, because the error page is not JSON. Check response.ok before you read the body, and throw your own clear error:
async function load(url) {
const response = await fetch(url);
console.log(response.status, response.ok);
if (!response.ok) {
throw new Error("Could not load " + url);
}
return response.json();
}
async function main() {
try {
const shop = await load("products.json");
console.log(shop.store);
await load("missing.json");
} catch (error) {
console.log(error.message);
}
}
main();
fetch rejects only when the request itself fails (no network, a blocked address). A 404 or 500 from a working server still resolves normally, so catch will not run unless you throw yourself.
A server tells the browser what kind of data it is sending in the Content-Type header. JSON should arrive as application/json:
fetch("students.json").then(response => {
console.log(response.headers.get("content-type"));
});
Once response.json() finishes, you have ordinary objects and arrays, so everything from the Arrays section works: filter, map, reduce, find, loops.
async function inStockValue() {
const response = await fetch("products.json");
const shop = await response.json();
const inStock = shop.items.filter(item => item.inStock);
const total = inStock.reduce((sum, item) => sum + item.price, 0);
console.log(inStock.map(item => item.name).join(", "));
console.log(total.toFixed(2));
}
inStockValue();
Fetch can also send data. Set the method to POST, tell the server you are sending JSON in the header, and put the stringified data in the body:
fetch("/api/scores", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Ava", score: 95 })
});
There is no Try It button for this one because it needs a server that accepts the data. The JS Fetch API lesson looks at requests in more detail.
A page can read JSON from its own site freely. Reading from a different site works only if that site's server says it allows it (this is called CORS). If it does not, the browser blocks the response and fetch fails with TypeError: Failed to fetch, and the console explains that the request was blocked by CORS policy. It is a browser safety rule, not a bug in your code.
fetch() directly instead of reading response.json()await in front of response.json(), which leaves you holding a promiseresponse.ok, then getting a SyntaxError from an HTML error pageresponse.json() twice: the body can be read only oncecatch to run on a 404What does response.json() return?
await or .then to get the parsed data.A fetch for a file that does not exist gives a 404. Does the catch of a try...catch run on its own? Why?
fetch rejects only when it cannot make the request at all. A 404 is still a response, so you must check response.ok and throw an error yourself.What is missing?
async function load() {
const response = await fetch("students.json");
const students = response.json();
console.log(students.length);
}
await before response.json(). Without it, students is a promise, and students.length is undefined.products.json and log the name of every item that costs more than 3students.json and log the names of students whose grade is 10