// apis · Web Platform Advent #1
The Fetch API in JavaScript: a practical guide
How to make HTTP requests in the browser with the Fetch API - GET, JSON, POST, error handling, and aborting requests - with runnable examples.
The Fetch API is the modern, promise-based way to make HTTP requests from the browser. It replaces the older XMLHttpRequest with a cleaner interface that works naturally with async/await and streams. Every current browser supports it.
A first GET request
fetch() returns a promise that resolves to a Response object. To read JSON from it, call response.json(), which itself returns a promise:
const res = await fetch('https://api.example.com/articles');
const data = await res.json();
console.log(data); The same thing without async/await, using .then() chains:
fetch('https://api.example.com/articles')
.then((res) => res.json())
.then((data) => console.log(data)); The gotcha: fetch does not reject on HTTP errors
This trips up almost everyone. A 404 or 500 response is still a successful fetch - the promise only rejects on a network failure (offline, DNS, CORS). You must check response.ok yourself:
const res = await fetch(url);
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
const data = await res.json();
fetch() call sends an HTTP request across the network to a server and waits for the response to travel back.Sending data with POST
Pass a second options argument to set the method, headers and body. For JSON, stringify the payload and set the Content-Type:
const res = await fetch('/api/articles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Hello', body: 'World' }),
});
const created = await res.json(); Aborting a request
Use an AbortController to cancel a request - useful for timeouts or when a component unmounts:
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
try {
const res = await fetch(url, { signal: controller.signal });
const data = await res.json();
} catch (err) {
if (err.name === 'AbortError') console.log('Request timed out');
} finally {
clearTimeout(timeout);
} Quick reference
| Task | How |
|---|---|
| Read JSON | await res.json() |
| Read text | await res.text() |
| Check success | res.ok / res.status |
| Send JSON | body: JSON.stringify(...) + Content-Type |
| Cancel | AbortController + signal |
That covers the everyday cases. The Fetch API also supports streaming response bodies, FormData uploads, and request/response cloning - but the patterns above handle the vast majority of real-world calls.
Frequently asked questions
- How do you make a request with the Fetch API?
- Call fetch with a URL; it returns a promise that resolves to a Response object. Read the body with a method like response.json() or response.text(), which also return promises, so you typically await fetch and then await the parsing.
- Why doesn't fetch reject on a 404 or 500?
- fetch only rejects on network failures, not on HTTP error statuses. A 404 or 500 still resolves successfully, so you must check response.ok (or response.status) yourself and throw or handle the error when it is false.
- How do you send JSON with a POST request?
- Pass a second options object with method set to POST, a Content-Type: application/json header, and body set to JSON.stringify(yourData). The server then receives a JSON request body it can parse.
- How do you cancel a fetch request?
- Create an AbortController, pass its signal to fetch in the options, and call controller.abort() to cancel; the fetch promise then rejects with an AbortError you can catch. It's useful for timeouts or superseding an in-flight request.