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:

MethodPurpose
GETRetrieve data from the server
POSTSend new data to the server
PUTUpdate existing data completely
PATCHUpdate part of existing data
DELETERemove 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 / MethodDescription
response.oktrue if status is 200–299
response.statusHTTP status code (200, 404, 500, etc.)
response.statusTextMessage 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: 404

Using 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

OptionDescriptionExample Value
methodHTTP method"GET", "POST", "DELETE"
headersRequest headers object{ "Content-Type": "application/json" }
bodyRequest body dataJSON.stringify({ name: "Raj" })
modeCORS mode"cors", "no-cors", "same-origin"
cacheCache 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() or response.text() to extract the body content
  • fetch() only rejects on network errors — always check response.ok for HTTP errors
  • Use method, headers, and body options 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() with fetch() to load multiple resources in parallel
  • Always wrap fetch calls in try/catch when using async/await

Leave a Comment

Your email address will not be published. Required fields are marked *