JavaScript Fetch API
The Fetch API is a modern, built-in browser interface for making HTTP requests — fetching data from a server, submitting form data, or calling third-party APIs. It uses Promises, which makes it clean and easy to use alongside async/await.
Real-life analogy: Think of the Fetch API as a delivery service. A request is placed (the fetch call), and a delivery arrives some time later (the response). While waiting, the rest of the code continues running.
What is an HTTP Request?
When a browser needs data from a server — such as a list of products, user details, or weather information — it sends an HTTP request. The server processes it and sends back a response. This all happens in the background without reloading the page.
The most common HTTP methods are:
| Method | Purpose |
|---|---|
| GET | Retrieve data from the server |
| POST | Send new data to the server |
| PUT | Update existing data completely |
| PATCH | Update part of existing data |
| DELETE | Remove data from the server |
Basic Fetch Syntax
fetch(url)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.log("Error:", error));GET Request — Fetching Data
The default behavior of fetch() is a GET request. Below is an example using a free public API.
fetch("https://jsonplaceholder.typicode.com/users/1")
.then(response => {
console.log("Status:", response.status); // 200
return response.json(); // Parse JSON body
})
.then(user => {
console.log("Name:", user.name);
console.log("Email:", user.email);
})
.catch(error => {
console.log("Request failed:", error.message);
});Understanding the Response Object
The fetch() function resolves with a Response object — not the actual data. This object describes the response and provides methods to extract the body.
| Property / Method | Description |
|---|---|
| response.ok | true if status is 200–299 |
| response.status | HTTP status code (200, 404, 500, etc.) |
| response.statusText | Message like "OK" or "Not Found" |
| response.json() | Parse body as JSON (returns a Promise) |
| response.text() | Parse body as plain text |
| response.blob() | Parse body as binary (images, files) |
Checking for HTTP Errors
An important detail: fetch() only rejects on network failures (no internet, DNS error). A 404 or 500 response is NOT thrown as an error — response.ok must be checked manually.
fetch("https://jsonplaceholder.typicode.com/posts/9999")
.then(response => {
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.log("Caught:", error.message));
// Caught: HTTP Error: 404Using async/await with Fetch
The async/await style is cleaner and easier to read for most fetch operations.
async function getPost(id) {
try {
let response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
if (!response.ok) {
throw new Error(`Failed with status ${response.status}`);
}
let post = await response.json();
console.log("Title:", post.title);
console.log("Body:", post.body);
} catch (error) {
console.log("Error:", error.message);
}
}
getPost(1);Fetching a List of Items
async function loadUsers() {
try {
let response = await fetch("https://jsonplaceholder.typicode.com/users");
let users = await response.json();
users.forEach(user => {
console.log(`${user.id}. ${user.name} — ${user.email}`);
});
} catch (error) {
console.log("Could not load users:", error.message);
}
}
loadUsers();POST Request — Sending Data
To send data to a server, the method must be set to POST and the body must be included. The body is usually serialized as a JSON string.
async function createPost() {
let newPost = {
title: "JavaScript Fetch API",
body: "Learning how to send data using fetch.",
userId: 1
};
try {
let response = await fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newPost)
});
let result = await response.json();
console.log("Created post ID:", result.id);
console.log("Title:", result.title);
} catch (error) {
console.log("Failed to create post:", error.message);
}
}
createPost();PUT Request — Updating Data
async function updatePost(id) {
let updatedData = {
title: "Updated Title",
body: "Updated content here.",
userId: 1
};
let response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(updatedData)
});
let result = await response.json();
console.log("Updated:", result.title);
}
updatePost(1);DELETE Request — Removing Data
async function deletePost(id) {
let response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: "DELETE"
});
if (response.ok) {
console.log(`Post ${id} deleted successfully.`);
} else {
console.log("Delete failed:", response.status);
}
}
deletePost(5);Fetch with Request Headers
Headers provide additional information about the request — such as the content type, authorization tokens, or accepted response formats.
async function getSecureData() {
let response = await fetch("https://api.example.com/secure-data", {
method: "GET",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
});
let data = await response.json();
console.log(data);
}Fetching Multiple APIs in Parallel
async function loadDashboard() {
try {
let [usersRes, postsRes] = await Promise.all([
fetch("https://jsonplaceholder.typicode.com/users"),
fetch("https://jsonplaceholder.typicode.com/posts?_limit=5")
]);
let users = await usersRes.json();
let posts = await postsRes.json();
console.log("Users count:", users.length);
console.log("Recent posts:", posts.length);
} catch (error) {
console.log("Dashboard load error:", error.message);
}
}
loadDashboard();Showing Loading State During Fetch
<p id="status">Ready</p>
<ul id="list"></ul>
<script>
async function loadPosts() {
let status = document.getElementById("status");
let list = document.getElementById("list");
status.textContent = "Loading...";
try {
let response = await fetch("https://jsonplaceholder.typicode.com/posts?_limit=5");
let posts = await response.json();
list.innerHTML = "";
posts.forEach(post => {
let li = document.createElement("li");
li.textContent = post.title;
list.appendChild(li);
});
status.textContent = "Loaded successfully!";
} catch (error) {
status.textContent = "Failed to load posts.";
}
}
loadPosts();
</script>Common Fetch Request Options
| Option | Description | Example Value |
|---|---|---|
| method | HTTP method | "GET", "POST", "DELETE" |
| headers | Request headers object | { "Content-Type": "application/json" } |
| body | Request body data | JSON.stringify({ name: "Raj" }) |
| mode | CORS mode | "cors", "no-cors", "same-origin" |
| cache | Cache behavior | "no-cache", "reload", "default" |
Practical Example — Search and Display API Data
<input type="text" id="searchInput" placeholder="Enter user ID (1-10)" />
<button onclick="searchUser()">Search</button>
<p id="result"></p>
<script>
async function searchUser() {
let id = document.getElementById("searchInput").value.trim();
let result = document.getElementById("result");
if (!id || isNaN(id)) {
result.textContent = "Please enter a valid number.";
return;
}
result.textContent = "Searching...";
try {
let response = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
if (!response.ok) throw new Error("User not found.");
let user = await response.json();
result.textContent = `Name: ${user.name} | Email: ${user.email} | City: ${user.address.city}`;
} catch (error) {
result.textContent = "Error: " + error.message;
}
}
</script>Key Points to Remember
- The Fetch API is the modern way to make HTTP requests from the browser
fetch()returns a Promise that resolves to a Response object — not the data directly- Always call
response.json()orresponse.text()to extract the body content fetch()only rejects on network errors — always checkresponse.okfor HTTP errors- Use
method,headers, andbodyoptions in the second argument for POST/PUT/DELETE requests - Always send
"Content-Type": "application/json"in the header when posting JSON data - Use
Promise.all()withfetch()to load multiple resources in parallel - Always wrap fetch calls in
try/catchwhen using async/await
