Using the Fetch API with Promises in JavaScript
Modern JavaScript applications constantly talk to servers—fetching data, sending form submissions, or updating resources. The Fetch API is the standard way to perform HTTP requests in the browser, and it is promise-based by design.
This article explains:
What
fetch()isHow promises work with
fetchHandling JSON responses
Error handling (the right way)
Common real-world patterns
Mistakes to avoid
1. What is the Fetch API?
fetch() is a browser API used to make HTTP requests such as:
GET (read data)
POST (send data)
PUT / PATCH (update data)
DELETE (remove data)
It returns a Promise that resolves to a Response object.
fetch(url)
Important:
fetch()does not return the data directlyIt returns a Promise
That Promise resolves when the HTTP response arrives
2. Basic Fetch Example (GET request)
fetch("https://api.example.com/users")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
});
What happens step by step?
fetch()sends an HTTP requestIt returns a Promise
The first
.then()receives a Response objectresponse.json()reads the body and returns another PromiseThe second
.then()receives the parsed JSON data
3. Why Does response.json() Return a Promise?
Because reading the response body is asynchronous.
The response arrives as a stream, and JavaScript needs time to:
Read it
Decode it
Parse JSON
That’s why this is wrong:
const data = response.json(); // ❌ data is a Promise
And this is correct:
response.json().then((data) => {
console.log(data);
});
4. Handling HTTP Errors Properly
⚠️ Important rulefetch() only rejects on network errors, not HTTP errors like 404 or 500.
❌ Wrong assumption
fetch(url)
.catch(() => {
// This will NOT run for 404 or 500
});
✅ Correct error handling pattern
fetch("https://api.example.com/users")
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("Fetch failed:", error.message);
});
Why this works
response.okistruefor status codes200–299You manually throw an error
Thrown errors are caught by
.catch()
5. Making a POST Request with Fetch
Sending data to a server requires:
HTTP method
Headers
Request body
fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Pushpesh",
role: "Frontend Developer",
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("Failed to create user");
}
return response.json();
})
.then((data) => {
console.log("User created:", data);
})
.catch((error) => {
console.error(error.message);
});
6. Chaining Multiple .then() Calls
Promises allow step-by-step transformations.
fetch("/api/profile")
.then((res) => res.json())
.then((user) => user.name)
.then((name) => name.toUpperCase())
.then((finalName) => {
console.log(finalName);
});
Each .then():
Receives the result of the previous one
Can transform and return a new value
7. Common Fetch Mistakes
❌ Forgetting to return response.json()
fetch(url)
.then((response) => {
response.json(); // ❌ missing return
})
.then((data) => {
console.log(data); // undefined
});
✅ Correct
.then((response) => {
return response.json();
});
❌ Assuming fetch throws on 404
fetch("/wrong-url")
.then((res) => res.json())
.catch(() => console.log("Error")); // ❌
✅ Correct
.then((res) => {
if (!res.ok) throw new Error("Not found");
return res.json();
});
8. Fetch vs XMLHttpRequest (Old Way)
| Feature | fetch | XMLHttpRequest |
| Promise-based | ✅ | ❌ |
| Cleaner syntax | ✅ | ❌ |
| Stream support | ✅ | ❌ |
| Modern standard | ✅ | ❌ |
9. When to Use Promises vs async/await?
fetch() always uses Promises internally.
This article focused on Promises directly, but most production code today uses async/await, which is just syntax sugar on top of Promises.
Understanding promise-based fetch:
Makes debugging easier
Helps you read older code
Strengthens your JavaScript fundamentals
10. Mental Model to Remember
Think of fetch() like this:
“I am asking the browser to make a request.
It promises to tell me later when the response arrives.”
And each .then() is:
“When you’re done, do this next.”
Final Takeaways
fetch()returns a Promiseresponse.json()returns another PromiseHTTP errors must be handled manually
Always return promises in
.then()Promise chaining allows clean, readable async flows