fetch asks another server for data and gives you back a promise. It is built into every modern browser — no library needed.
const response = await fetch("https://api.example.com/users");
const users = await response.json();
console.log(users);Two awaits, because two things take time: getting the response, and reading its body.
A complete example#
async function loadUsers() {
const list = document.querySelector("#users");
list.textContent = "Loading…";
try {
const response = await fetch("https://api.example.com/users");
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`);
}
const users = await response.json();
list.innerHTML = "";
for (const user of users) {
const li = document.createElement("li");
li.textContent = user.name;
list.appendChild(li);
}
} catch (error) {
list.textContent = "Could not load users. Please try again.";
console.error(error);
}
}Notice there are three states the user can see: loading, loaded, and failed. Skipping the last two is what makes an app feel broken.
Sending data with POST#
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Ada", email: "ada@example.com" }),
});
const created = await response.json();Three things are easy to forget: the method, the Content-Type header, and JSON.stringify around the body. Missing any of them usually produces a 400 from the server.
Reading other response types#
await response.json(); // parsed JSON
await response.text(); // raw text — useful when JSON parsing fails
await response.blob(); // images and filesIf response.json() throws “Unexpected token”, read it with .text() instead and look at what actually came back. It is often an HTML error page.
The CORS error#
Sooner or later you will see: “has been blocked by CORS policy”. This is a browser security rule: a site can only read responses from another domain if that domain explicitly allows it.
It is not something you can fix in your JavaScript. Your options are to use an API that permits browser requests, or to make the request from your own server instead.
Cancelling a request#
const controller = new AbortController();
fetch("/api/search?q=abc", { signal: controller.signal });
controller.abort(); // e.g. the user typed againUseful for search-as-you-type, where an old slow response could otherwise overwrite a newer one.
Questions people ask#
What is the difference between fetch and axios?
Axios is a library that adds conveniences — automatic JSON parsing, throwing on error statuses, request cancellation. fetch is built in and enough for most work.
Where do I put an API key?
Not in browser JavaScript. Anything in your front-end code is visible to anyone who opens DevTools. Keys belong on a server that makes the request for you.
Why does my request work in the browser address bar but not in fetch?
Almost always CORS. Typing a URL is a top-level navigation, which is not subject to the same restriction as a script reading a cross-origin response.